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 #include "perfetto/tracing/traced_value.h" 24 25 namespace perfetto { 26 class EventContext; 27 namespace internal { 28 template <typename FieldMetadata, 29 bool is_message, 30 protozero::proto_utils::RepetitionType repetition_type> 31 struct TypedProtoWriterImpl; 32 } 33 34 // A Wrapper around a protozero message to allow C++ classes to specify how it 35 // should be serialised into the trace: 36 // 37 // class Foo { 38 // public: 39 // void WriteIntoTrace(perfetto::TracedProto<pbzero::Foo> message) { 40 // message->set_int_field(int_field_); 41 // } 42 // }; 43 // 44 // This class also exposes EventContext, e.g. to enable data interning. 45 // 46 // NOTE: the functionality below is not ready yet. 47 // TODO(altimin): Make the interop below possible. 48 // TracedProto also provides a seamless integration with writing untyped 49 // values via TracedValue / TracedDictionary / TracedArray: 50 // 51 // - TracedValue can be converted to a TracedProto, either by calling 52 // TracedValue::WriteProto<T>() or implicitly. 53 // - If a proto message has a repeating DebugAnnotation debug_annotations 54 // field, it can be filled using the TracedDictionary obtained from 55 // TracedProto::AddDebugAnnotations. 56 template <typename MessageType> 57 class TracedProto { 58 public: 59 // implicit TracedProto(TracedValue && value)60 TracedProto(TracedValue&& value) 61 : TracedProto(std::move(value).WriteProto<MessageType>()) {} 62 ~TracedProto() = default; 63 64 TracedProto(const TracedProto&) = delete; 65 TracedProto& operator=(const TracedProto&) = delete; 66 TracedProto& operator=(TracedProto&&) = delete; 67 TracedProto(TracedProto&&) = default; 68 69 MessageType* operator->() const { return message_; } 70 message()71 MessageType* message() { return message_; } 72 73 // Write additional untyped values into the same context, which is useful 74 // when a given C++ class has a typed representation, but also either has 75 // members which can only be written into an untyped context (e.g. they are 76 // autogenerated) or it's desirable to have a way to quickly extend the 77 // trace representation of this class (e.g. for debugging). 78 // 79 // The usage of the returned TracedDictionary should not be interleaved with 80 // writing into |message| as this results in an inefficient proto layout. To 81 // enforce this, AddDebugAnnotations should be called on TracedProto&&, i.e. 82 // std::move(message).AddDebugAnnotations(). 83 // 84 // This requires a 'repeated DebugAnnotations debug_annotations' field in 85 // MessageType. 86 template <typename Check = void> AddDebugAnnotations()87 TracedDictionary AddDebugAnnotations() && { 88 static_assert( 89 std::is_base_of< 90 protozero::proto_utils::FieldMetadataBase, 91 typename MessageType::FieldMetadata_DebugAnnotations>::value, 92 "This message does not have a |debug_annotations| field. Please add a" 93 "'repeated perfetto.protos.DebugAnnotation debug_annnotations = N;' " 94 "field to your message."); 95 return TracedDictionary(message_, MessageType::kDebugAnnotations, context_, 96 nullptr); 97 } 98 99 // Start writing a single entry corresponding to the given |field| and return 100 // TracedProto should be used to populate this further. 101 // This method requires |field|'s type to be a nested message, but both 102 // repeated and non-repeated complex fields are supported. 103 template <typename FieldMetadata> WriteNestedMessage(FieldMetadata)104 TracedProto<typename FieldMetadata::cpp_field_type> WriteNestedMessage( 105 FieldMetadata) { 106 static_assert(std::is_base_of<MessageType, 107 typename FieldMetadata::message_type>::value, 108 "Field should belong to the current message"); 109 static_assert( 110 FieldMetadata::kProtoFieldType == 111 protozero::proto_utils::ProtoSchemaType::kMessage, 112 "AddItem() can be used only for nested message fields. To write a " 113 "primitive field, use traced_proto->set_field() or traced_proto.Set()"); 114 return Wrap( 115 message_->template BeginNestedMessage< 116 typename FieldMetadata::cpp_field_type>(FieldMetadata::kFieldId)); 117 } 118 119 // Write a given |value| into proto as a new |field| of the current message. 120 // This method supports both nested messages and primitive types (i.e. int or 121 // string), but requires the |field| to be non-repeateable (i.e. optional). 122 // For repeatable fields, AppendValue or AppendFrom should be used. 123 template <typename FieldMetadata, typename ValueType> Set(FieldMetadata,ValueType && value)124 void Set(FieldMetadata, ValueType&& value) { 125 static_assert(std::is_base_of<MessageType, 126 typename FieldMetadata::message_type>::value, 127 "Field should belong to the current message"); 128 static_assert( 129 FieldMetadata::kRepetitionType == 130 protozero::proto_utils::RepetitionType::kNotRepeated, 131 "Set() can't be used with repeated fields due to ambiguity between " 132 "writing |value| as a single entry or treating |value| as a container " 133 "and writing all contained items as multiple entries. Please use " 134 "dedicated AppendValue() or AppendFrom() methods to differentiate " 135 "between " 136 "these two situations"); 137 138 internal::TypedProtoWriterImpl< 139 FieldMetadata, 140 FieldMetadata::kProtoFieldType == 141 protozero::proto_utils::ProtoSchemaType::kMessage, 142 protozero::proto_utils::RepetitionType::kNotRepeated>:: 143 Write(*this, std::forward<ValueType>(value)); 144 } 145 146 // Write a given |value| a single entry into the repeated |field| of the 147 // current message. If the field is not repeated, Set() should be used 148 // instead. 149 template <typename FieldMetadata, typename ValueType> AppendValue(FieldMetadata,ValueType && value)150 void AppendValue(FieldMetadata, ValueType&& value) { 151 static_assert(std::is_base_of<MessageType, 152 typename FieldMetadata::message_type>::value, 153 "Field should belong to the current message"); 154 static_assert( 155 FieldMetadata::kRepetitionType == 156 protozero::proto_utils::RepetitionType::kRepeatedNotPacked, 157 "Append*() methods can be used only with repeated fields. " 158 "Please use Set() for non-repeated"); 159 160 // Write a single value into a given repeated field by explicitly passing 161 // "kNotRepeated" to the TypedProtoWriterImpl. 162 internal::TypedProtoWriterImpl< 163 FieldMetadata, 164 FieldMetadata::kProtoFieldType == 165 protozero::proto_utils::ProtoSchemaType::kMessage, 166 protozero::proto_utils::RepetitionType::kNotRepeated>:: 167 Write(*this, std::forward<ValueType>(value)); 168 } 169 170 // Write a given |value| as a set of entries into the repeated |field| of the 171 // current message. If the field is not repeated, Set() should be used 172 // instead. 173 template <typename FieldMetadata, typename ValueType> AppendFrom(FieldMetadata,ValueType && value)174 void AppendFrom(FieldMetadata, ValueType&& value) { 175 static_assert(std::is_base_of<MessageType, 176 typename FieldMetadata::message_type>::value, 177 "Field should belong to the current message"); 178 static_assert( 179 FieldMetadata::kRepetitionType == 180 protozero::proto_utils::RepetitionType::kRepeatedNotPacked, 181 "Append*() methods can be used only with repeated fields. " 182 "Please use Set() for non-repeated"); 183 184 internal::TypedProtoWriterImpl< 185 FieldMetadata, 186 FieldMetadata::kProtoFieldType == 187 protozero::proto_utils::ProtoSchemaType::kMessage, 188 protozero::proto_utils::RepetitionType::kRepeatedNotPacked>:: 189 Write(*this, std::forward<ValueType>(value)); 190 } 191 192 // Write a nested message into a field according to the provided metadata. 193 // TODO(altimin): Replace the current usages in Chrome with the functions 194 // above and make these methods private. 195 template <typename FieldMetadata> WriteNestedMessage()196 TracedProto<typename FieldMetadata::cpp_field_type> WriteNestedMessage() { 197 return WriteNestedMessage(FieldMetadata()); 198 } 199 200 private: 201 friend class EventContext; 202 friend class TracedValue; 203 // Allow TracedProto<Foo> to create TracedProto<Bar>. 204 template <typename T> 205 friend class TracedProto; 206 207 // Wraps a raw protozero message using the same context as the current object. 208 template <typename ChildMessageType> Wrap(ChildMessageType * message)209 TracedProto<ChildMessageType> Wrap(ChildMessageType* message) { 210 return TracedProto<ChildMessageType>(message, context_); 211 } 212 213 // Context might be null here when writing typed message which is 214 // nested into untyped legacy trace event macro argument. 215 // TODO(altimin): Turn this into EventContext& when this case is eliminated 216 // and expose it in public API. context()217 EventContext* context() const { return context_; } 218 TracedProto(MessageType * message,EventContext * context)219 TracedProto(MessageType* message, EventContext* context) 220 : message_(message), context_(context) {} 221 222 MessageType* const message_; 223 EventContext* context_; 224 }; 225 226 template <typename MessageType, typename ValueType> 227 void WriteIntoTracedProto(TracedProto<MessageType> message, ValueType&& value); 228 229 namespace internal { 230 231 template <typename FieldMetadata, 232 bool is_message, 233 protozero::proto_utils::RepetitionType repetition_type> 234 struct TypedProtoWriterImpl; 235 236 // Simple non-repeated field. 237 template <typename FieldMetadata> 238 struct TypedProtoWriterImpl< 239 FieldMetadata, 240 /*is_message=*/false, 241 protozero::proto_utils::RepetitionType::kNotRepeated> { 242 template <typename Proto, typename ValueType> 243 static void Write(TracedProto<Proto>& context, ValueType&& value) { 244 protozero::internal::FieldWriter<FieldMetadata::kProtoFieldType>::Append( 245 *context.message(), FieldMetadata::kFieldId, value); 246 } 247 }; 248 249 // Simple repeated non-packed field. 250 template <typename FieldMetadata> 251 struct TypedProtoWriterImpl< 252 FieldMetadata, 253 /*is_message=*/false, 254 protozero::proto_utils::RepetitionType::kRepeatedNotPacked> { 255 template <typename Proto, typename ValueType> 256 static void Write(TracedProto<Proto>& context, ValueType&& value) { 257 for (auto&& item : value) { 258 protozero::internal::FieldWriter<FieldMetadata::kProtoFieldType>::Append( 259 *context.message(), FieldMetadata::kFieldId, item); 260 } 261 } 262 }; 263 264 // Nested repeated non-packed field. 265 template <typename FieldMetadata> 266 struct TypedProtoWriterImpl< 267 FieldMetadata, 268 /*is_message=*/true, 269 protozero::proto_utils::RepetitionType::kNotRepeated> { 270 template <typename Proto, typename ValueType> 271 static void Write(TracedProto<Proto>& context, ValueType&& value) { 272 WriteIntoTracedProto(context.template WriteNestedMessage<FieldMetadata>(), 273 std::forward<ValueType>(value)); 274 } 275 }; 276 277 // Nested repeated non-packed field. 278 template <typename FieldMetadata> 279 struct TypedProtoWriterImpl< 280 FieldMetadata, 281 /*is_message=*/true, 282 protozero::proto_utils::RepetitionType::kRepeatedNotPacked> { 283 template <typename Proto, typename ValueType> 284 static void Write(TracedProto<Proto>& context, ValueType&& value) { 285 for (auto&& item : value) { 286 WriteIntoTracedProto(context.template WriteNestedMessage<FieldMetadata>(), 287 item); 288 } 289 } 290 }; 291 292 constexpr int kMaxWriteTracedProtoImplPriority = 1; 293 294 // If perfetto::TraceFormatTraits<T>::WriteIntoTrace(TracedProto<MessageType>, 295 // T) is available, use it. 296 template <typename MessageType, typename T> 297 decltype(TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTrace( 298 std::declval<TracedProto<MessageType>>(), 299 std::declval<T>()), 300 void()) 301 WriteIntoTracedProtoImpl(base::priority_tag<1>, 302 TracedProto<MessageType> message, 303 T&& value) { 304 TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTrace( 305 std::move(message), std::forward<T>(value)); 306 } 307 308 // If T has WriteIntoTrace(TracedProto<MessageType>) method, use it. 309 template <typename MessageType, typename T> 310 decltype( 311 std::declval<T>().WriteIntoTrace(std::declval<TracedProto<MessageType>>()), 312 void()) 313 WriteIntoTracedProtoImpl(base::priority_tag<0>, 314 TracedProto<MessageType> message, 315 T&& value) { 316 value.WriteIntoTrace(std::move(message)); 317 } 318 319 // TypedProtoWriter takes the protozero message (TracedProto<MessageType>), 320 // field description (FieldMetadata) and value and writes the given value 321 // into the given field of the given protozero message. 322 // 323 // This is primarily used for inline writing of typed messages: 324 // TRACE_EVENT(..., pbzero::Message:kField, value); 325 // 326 // Ideally we would use a function here and not a struct, but passing template 327 // arguments directly to the function (e.g. foo<void>()) isn't supported until 328 // C++20, so we have to use a helper struct here. 329 template <typename FieldMetadata> 330 struct TypedProtoWriter { 331 private: 332 using ProtoSchemaType = protozero::proto_utils::ProtoSchemaType; 333 using RepetitionType = protozero::proto_utils::RepetitionType; 334 335 static_assert(FieldMetadata::kRepetitionType != 336 RepetitionType::kRepeatedPacked, 337 "writing packed fields isn't supported yet"); 338 339 template <bool is_message, RepetitionType repetition_type> 340 struct Writer; 341 342 public: 343 template <typename Proto, typename ValueType> 344 static void Write(TracedProto<Proto>& context, ValueType&& value) { 345 TypedProtoWriterImpl< 346 FieldMetadata, 347 FieldMetadata::kProtoFieldType == ProtoSchemaType::kMessage, 348 FieldMetadata::kRepetitionType>::Write(context, 349 std::forward<ValueType>(value)); 350 } 351 }; 352 353 } // namespace internal 354 355 // Helper template to determine if a given type can be passed to 356 // perfetto::WriteIntoTracedProto. These templates will fail to resolve if the 357 // class does not have necesary support, so they are useful for SFINAE and for 358 // producing helpful compiler error messages. 359 template <typename MessageType, typename ValueType, typename Result = void> 360 using check_traced_proto_support_t = 361 decltype(internal::WriteIntoTracedProtoImpl( 362 std::declval< 363 base::priority_tag<internal::kMaxWriteTracedProtoImplPriority>>(), 364 std::declval<TracedProto<MessageType>>(), 365 std::declval<ValueType>())); 366 367 // check_traced_proto_support<MessageType, T, V>::type is defined (and equal to 368 // V) iff T supports being passed to WriteIntoTracedProto together with 369 // TracedProto<MessageType>. See the comment in traced_value_forward.h for more 370 // details. 371 template <typename MessageType, typename ValueType, class Result> 372 struct check_traced_proto_support< 373 MessageType, 374 ValueType, 375 Result, 376 check_traced_proto_support_t<MessageType, ValueType, Result>> { 377 static constexpr bool value = true; 378 using type = Result; 379 }; 380 381 template <typename MessageType, typename ValueType> 382 void WriteIntoTracedProto(TracedProto<MessageType> message, ValueType&& value) { 383 // TODO(altimin): Add a URL to the documentation and a list of common failure 384 // patterns. 385 static_assert( 386 std::is_same<check_traced_proto_support_t<MessageType, ValueType>, 387 void>::value, 388 "The provided type does not support being serialised into the " 389 "provided protozero message. Please see the comment in traced_proto.h " 390 "for more details."); 391 392 internal::WriteIntoTracedProtoImpl( 393 base::priority_tag<internal::kMaxWriteTracedProtoImplPriority>(), 394 std::move(message), std::forward<ValueType>(value)); 395 } 396 397 template <typename MessageType, typename FieldMetadataType, typename ValueType> 398 void WriteTracedProtoField(TracedProto<MessageType>& message, 399 FieldMetadataType, 400 ValueType&& value) { 401 static_assert( 402 std::is_base_of<protozero::proto_utils::FieldMetadataBase, 403 FieldMetadataType>::value, 404 "Field name should be a protozero::internal::FieldMetadata<...>"); 405 static_assert( 406 std::is_base_of<MessageType, 407 typename FieldMetadataType::message_type>::value, 408 "Field's parent type should match the context."); 409 410 internal::TypedProtoWriter<FieldMetadataType>::Write( 411 message, std::forward<ValueType>(value)); 412 } 413 414 } // namespace perfetto 415 416 #endif // INCLUDE_PERFETTO_TRACING_TRACED_PROTO_H_ 417