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_VALUE_H_
18 #define INCLUDE_PERFETTO_TRACING_TRACED_VALUE_H_
19
20 #include "perfetto/base/compiler.h"
21 #include "perfetto/base/export.h"
22 #include "perfetto/base/template_util.h"
23 #include "perfetto/protozero/message.h"
24 #include "perfetto/protozero/proto_utils.h"
25 #include "perfetto/tracing/internal/checked_scope.h"
26 #include "perfetto/tracing/string_helpers.h"
27 #include "perfetto/tracing/traced_value_forward.h"
28
29 #include <memory>
30 #include <type_traits>
31 #include <utility>
32
33 namespace perfetto {
34
35 namespace protos {
36 namespace pbzero {
37 class DebugAnnotation;
38 }
39 } // namespace protos
40
41 class DebugAnnotation;
42 class EventContext;
43
44 // These classes provide a JSON-inspired way to write structed data into traces.
45 //
46 // Each TracedValue can be consumed exactly once to write a value into a trace
47 // using one of the Write* methods.
48 //
49 // Write* methods fall into two categories:
50 // - Primitive types (int, string, bool, double, etc): they just write the
51 // provided value, consuming the TracedValue in the process.
52 // - Complex types (arrays and dicts): they consume the TracedValue and
53 // return a corresponding scoped object (TracedArray or TracedDictionary).
54 // This scope then can be used to write multiple items into the container:
55 // TracedArray::AppendItem and TracedDictionary::AddItem return a new
56 // TracedValue which then can be used to write an element of the
57 // dictionary or array.
58 //
59 // To define how a custom class should be written into the trace, users should
60 // define one of the two following functions:
61 // - Foo::WriteIntoTrace(TracedValue) const
62 // (preferred for code which depends on perfetto directly)
63 // - perfetto::TraceFormatTraits<T>::WriteIntoTrace(
64 // TracedValue, const T&);
65 // (should be used if T is defined in a library which doesn't know anything
66 // about tracing).
67 //
68 //
69 // After definiting a conversion method, the object can be used directly as a
70 // TRACE_EVENT argument:
71 //
72 // Foo foo;
73 // TRACE_EVENT("cat", "Event", "arg", foo);
74 //
75 // Examples:
76 //
77 // TRACE_EVENT("cat", "event", "params", [&](perfetto::TracedValue context)
78 // {
79 // auto dict = std::move(context).WriteDictionary();
80 // dict->Add("param1", param1);
81 // dict->Add("param2", param2);
82 // ...
83 // dict->Add("paramN", paramN);
84 //
85 // {
86 // auto inner_array = dict->AddArray("inner");
87 // inner_array->Append(value1);
88 // inner_array->Append(value2);
89 // }
90 // });
91 //
92 // template <typename T>
93 // TraceFormatTraits<std::optional<T>>::WriteIntoTrace(
94 // TracedValue context, const std::optional<T>& value) {
95 // if (!value) {
96 // std::move(context).WritePointer(nullptr);
97 // return;
98 // }
99 // perfetto::WriteIntoTrace(std::move(context), *value);
100 // }
101 //
102 // template <typename T>
103 // TraceFormatTraits<std::vector<T>>::WriteIntoTrace(
104 // TracedValue context, const std::array<T>& value) {
105 // auto array = std::move(context).WriteArray();
106 // for (const auto& item: value) {
107 // array_scope.Append(item);
108 // }
109 // }
110 //
111 // class Foo {
112 // void WriteIntoTrace(TracedValue context) const {
113 // auto dict = std::move(context).WriteDictionary();
114 // dict->Set("key", 42);
115 // dict->Set("foo", "bar");
116 // dict->Set("member", member_);
117 // }
118 // }
119 namespace internal {
120 // TODO(altimin): Currently EventContext can be null due the need to support
121 // TracedValue-based serialisation with the Chrome's TraceLog. After this is
122 // gone, the second parameter should be changed to EventContext&.
123 PERFETTO_EXPORT TracedValue
124 CreateTracedValueFromProto(protos::pbzero::DebugAnnotation*,
125 EventContext* = nullptr);
126 }
127
128 class PERFETTO_EXPORT TracedValue {
129 public:
130 TracedValue(const TracedValue&) = delete;
131 TracedValue& operator=(const TracedValue&) = delete;
132 TracedValue& operator=(TracedValue&&) = delete;
133 TracedValue(TracedValue&&);
134 ~TracedValue();
135
136 // TracedValue represents a context into which a single value can be written
137 // (either by writing it directly for primitive types, or by creating a
138 // TracedArray or TracedDictionary for the complex types). This is enforced
139 // by allowing Write* methods to be called only on rvalue references.
140
141 void WriteInt64(int64_t value) &&;
142 void WriteUInt64(uint64_t value) &&;
143 void WriteDouble(double value) &&;
144 void WriteBoolean(bool value) &&;
145 void WriteString(const char*) &&;
146 void WriteString(const char*, size_t len) &&;
147 void WriteString(const std::string&) &&;
148 void WritePointer(const void* value) &&;
149 template <typename MessageType>
150 TracedProto<MessageType> WriteProto() &&;
151
152 // Rules for writing nested dictionaries and arrays:
153 // - Only one scope (TracedArray, TracedDictionary or TracedValue) can be
154 // active at the same time. It's only allowed to call methods on the active
155 // scope.
156 // - When a scope creates a nested scope, the new scope becomes active.
157 // - When a scope is destroyed, it's parent scope becomes active again.
158 //
159 // Typically users will have to create a scope only at the beginning of a
160 // conversion function and this scope should be destroyed at the end of it.
161 // TracedArray::Append and TracedDictionary::Add create, write and complete
162 // inner scopes automatically.
163
164 // Scope which allows multiple values to be appended.
165 TracedArray WriteArray() && PERFETTO_WARN_UNUSED_RESULT;
166
167 // Scope which allows multiple key-value pairs to be added.
168 TracedDictionary WriteDictionary() && PERFETTO_WARN_UNUSED_RESULT;
169
170 private:
171 friend class TracedArray;
172 friend class TracedDictionary;
173 friend TracedValue internal::CreateTracedValueFromProto(
174 protos::pbzero::DebugAnnotation*,
175 EventContext*);
176
177 static TracedValue CreateFromProto(protos::pbzero::DebugAnnotation* proto,
178 EventContext* event_context = nullptr);
179
TracedValue(protos::pbzero::DebugAnnotation * annotation,EventContext * event_context,internal::CheckedScope * parent_scope)180 inline explicit TracedValue(protos::pbzero::DebugAnnotation* annotation,
181 EventContext* event_context,
182 internal::CheckedScope* parent_scope)
183 : annotation_(annotation),
184 event_context_(event_context),
185 checked_scope_(parent_scope) {}
186
187 protozero::Message* WriteProtoInternal(const char* name);
188
189 // Temporary support for perfetto::DebugAnnotation C++ class before it's going
190 // to be replaced by TracedValue.
191 // TODO(altimin): Convert v8 to use TracedValue directly and delete it.
192 friend class DebugAnnotation;
193
194 protos::pbzero::DebugAnnotation* const annotation_ = nullptr;
195 EventContext* const event_context_ = nullptr;
196
197 internal::CheckedScope checked_scope_;
198 };
199
200 template <typename MessageType>
WriteProto()201 TracedProto<MessageType> TracedValue::WriteProto() && {
202 return TracedProto<MessageType>(
203 static_cast<MessageType*>(WriteProtoInternal(MessageType::GetName())),
204 event_context_);
205 }
206
207 class PERFETTO_EXPORT TracedArray {
208 public:
209 // implicit
210 TracedArray(TracedValue);
211
212 TracedArray(const TracedArray&) = delete;
213 TracedArray& operator=(const TracedArray&) = delete;
214 TracedArray& operator=(TracedArray&&) = delete;
215 TracedArray(TracedArray&&) = default;
216 ~TracedArray() = default;
217
218 TracedValue AppendItem();
219
220 template <typename T>
Append(T && value)221 void Append(T&& value) {
222 WriteIntoTracedValue(AppendItem(), std::forward<T>(value));
223 }
224
225 TracedDictionary AppendDictionary() PERFETTO_WARN_UNUSED_RESULT;
226 TracedArray AppendArray();
227
228 private:
229 friend class TracedValue;
230
TracedArray(protos::pbzero::DebugAnnotation * annotation,EventContext * event_context,internal::CheckedScope * parent_scope)231 inline explicit TracedArray(protos::pbzero::DebugAnnotation* annotation,
232 EventContext* event_context,
233 internal::CheckedScope* parent_scope)
234 : annotation_(annotation),
235 event_context_(event_context),
236 checked_scope_(parent_scope) {}
237
238 protos::pbzero::DebugAnnotation* annotation_;
239 EventContext* const event_context_;
240
241 internal::CheckedScope checked_scope_;
242 };
243
244 class PERFETTO_EXPORT TracedDictionary {
245 public:
246 // implicit
247 TracedDictionary(TracedValue);
248
249 TracedDictionary(const TracedDictionary&) = delete;
250 TracedDictionary& operator=(const TracedDictionary&) = delete;
251 TracedDictionary& operator=(TracedDictionary&&) = delete;
252 TracedDictionary(TracedDictionary&&) = default;
253 ~TracedDictionary() = default;
254
255 // There are two paths for writing dictionary keys: fast path for writing
256 // compile-time const, whose pointer is remains valid during the entire
257 // runtime of the program and the slow path for dynamic strings, which need to
258 // be copied.
259 // In the most common case, a string literal can be passed to `Add`/`AddItem`.
260 // In other cases, either StaticString or DynamicString declarations are
261 // needed.
262
263 TracedValue AddItem(StaticString key);
264 TracedValue AddItem(DynamicString key);
265
266 template <typename T>
Add(StaticString key,T && value)267 void Add(StaticString key, T&& value) {
268 WriteIntoTracedValue(AddItem(key), std::forward<T>(value));
269 }
270
271 template <typename T>
Add(DynamicString key,T && value)272 void Add(DynamicString key, T&& value) {
273 WriteIntoTracedValue(AddItem(key), std::forward<T>(value));
274 }
275
276 TracedDictionary AddDictionary(StaticString key);
277 TracedDictionary AddDictionary(DynamicString key);
278 TracedArray AddArray(StaticString key);
279 TracedArray AddArray(DynamicString key);
280
281 private:
282 friend class TracedValue;
283 template <typename T>
284 friend class TracedProto;
285
286 // Create a |TracedDictionary| which will populate the given field of the
287 // given |message|.
288 template <typename MessageType, typename FieldMetadata>
TracedDictionary(MessageType * message,protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,EventContext * event_context,internal::CheckedScope * parent_scope)289 inline explicit TracedDictionary(
290 MessageType* message,
291 protozero::proto_utils::internal::FieldMetadataHelper<FieldMetadata>,
292 EventContext* event_context,
293 internal::CheckedScope* parent_scope)
294 : message_(message),
295 field_id_(FieldMetadata::kFieldId),
296 event_context_(event_context),
297 checked_scope_(parent_scope) {
298 static_assert(std::is_base_of<protozero::Message, MessageType>::value,
299 "Message should be a subclass of protozero::Message");
300 static_assert(std::is_base_of<protozero::proto_utils::FieldMetadataBase,
301 FieldMetadata>::value,
302 "FieldMetadata should be a subclass of FieldMetadataBase");
303 static_assert(
304 std::is_same<typename FieldMetadata::message_type, MessageType>::value,
305 "Field does not belong to this message");
306 static_assert(
307 std::is_same<typename FieldMetadata::cpp_field_type,
308 ::perfetto::protos::pbzero::DebugAnnotation>::value,
309 "Field should be of DebugAnnotation type");
310 static_assert(
311 FieldMetadata::kRepetitionType ==
312 protozero::proto_utils::RepetitionType::kRepeatedNotPacked,
313 "Field should be non-packed repeated");
314 }
315
316 protozero::Message* const message_;
317 const uint32_t field_id_;
318 EventContext* event_context_;
319
320 internal::CheckedScope checked_scope_;
321 };
322
323 namespace internal {
324
325 // SFINAE helpers for finding a right overload to convert a given class to
326 // trace-friendly form, ordered from most to least preferred.
327
328 constexpr int kMaxWriteImplPriority = 4;
329
330 // If T has WriteIntoTracedValue member function, call it.
331 template <typename T>
332 decltype(std::declval<T>().WriteIntoTracedValue(std::declval<TracedValue>()),
333 void())
WriteImpl(base::priority_tag<4>,TracedValue context,T && value)334 WriteImpl(base::priority_tag<4>, TracedValue context, T&& value) {
335 value.WriteIntoTracedValue(std::move(context));
336 }
337
338 // If T has WriteIntoTrace member function, call it.
339 template <typename T>
340 decltype(std::declval<T>().WriteIntoTrace(std::declval<TracedValue>()), void())
WriteImpl(base::priority_tag<4>,TracedValue context,T && value)341 WriteImpl(base::priority_tag<4>, TracedValue context, T&& value) {
342 value.WriteIntoTrace(std::move(context));
343 }
344
345 // If perfetto::TraceFormatTraits<T>::WriteIntoTracedValue(TracedValue, const
346 // T&) is available, use it.
347 template <typename T>
decltype(TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTracedValue (std::declval<TracedValue> (),std::declval<T> ()),void ())348 decltype(TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTracedValue(
349 std::declval<TracedValue>(),
350 std::declval<T>()),
351 void())
352 WriteImpl(base::priority_tag<3>, TracedValue context, T&& value) {
353 TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTracedValue(
354 std::move(context), std::forward<T>(value));
355 }
356
357 // If perfetto::TraceFormatTraits<T>::WriteIntoTrace(TracedValue, const T&)
358 // is available, use it.
359 template <typename T>
decltype(TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTrace (std::declval<TracedValue> (),std::declval<T> ()),void ())360 decltype(TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTrace(
361 std::declval<TracedValue>(),
362 std::declval<T>()),
363 void())
364 WriteImpl(base::priority_tag<3>, TracedValue context, T&& value) {
365 TraceFormatTraits<base::remove_cvref_t<T>>::WriteIntoTrace(
366 std::move(context), std::forward<T>(value));
367 }
368
369 // If T has operator(), which takes TracedValue, use it.
370 // Very useful for lambda resolutions.
371 template <typename T>
decltype(std::declval<T> ()(std::declval<TracedValue> ()),void ())372 decltype(std::declval<T>()(std::declval<TracedValue>()), void())
373 WriteImpl(base::priority_tag<2>, TracedValue context, T&& value) {
374 std::forward<T>(value)(std::move(context));
375 }
376
377 // If T is a container and its elements have tracing support, use it.
378 //
379 // Note: a reference to T should be passed to std::begin, otherwise
380 // for non-reference types const T& will be passed to std::begin, losing
381 // support for non-const WriteIntoTracedValue methods.
382 template <typename T>
383 typename check_traced_value_support<
384 decltype(*std::begin(std::declval<T&>()))>::type
WriteImpl(base::priority_tag<1>,TracedValue context,T && value)385 WriteImpl(base::priority_tag<1>, TracedValue context, T&& value) {
386 auto array = std::move(context).WriteArray();
387 for (auto&& item : value) {
388 array.Append(item);
389 }
390 }
391
392 // std::underlying_type can't be used with non-enum types, so we need this
393 // indirection.
394 template <typename T, bool = std::is_enum<T>::value>
395 struct safe_underlying_type {
396 using type = typename std::underlying_type<T>::type;
397 };
398
399 template <typename T>
400 struct safe_underlying_type<T, false> {
401 using type = T;
402 };
403
404 template <typename T>
405 struct is_incomplete_type {
406 static constexpr bool value = sizeof(T) != 0;
407 };
408
409 // sizeof is not available for const char[], but it's still not considered to be
410 // an incomplete type for our purposes as the size can be determined at runtime
411 // due to strings being null-terminated.
412 template <>
413 struct is_incomplete_type<const char[]> {
414 static constexpr bool value = true;
415 };
416
417 } // namespace internal
418
419 // Helper template to determine if a given type can be passed to
420 // perfetto::WriteIntoTracedValue. These templates will fail to resolve if the
421 // class does not have it support, so they are useful in SFINAE and in producing
422 // helpful compiler results.
423 template <typename T, class Result = void>
424 using check_traced_value_support_t = decltype(
425 internal::WriteImpl(
426 std::declval<base::priority_tag<internal::kMaxWriteImplPriority>>(),
427 std::declval<TracedValue>(),
428 std::declval<T>()),
429 std::declval<Result>());
430
431 // check_traced_value_support<T, V>::type is defined (and equal to V) iff T
432 // supports being passed to WriteIntoTracedValue. See the comment in
433 // traced_value_forward.h for more details.
434 template <typename T, class Result>
435 struct check_traced_value_support<T,
436 Result,
437 check_traced_value_support_t<T, Result>> {
438 static_assert(
439 internal::is_incomplete_type<T>::value,
440 "perfetto::TracedValue should not be used with incomplete types");
441
442 static constexpr bool value = true;
443 using type = Result;
444 };
445
446 namespace internal {
447
448 // Helper class to check if a given type can be passed to
449 // perfetto::WriteIntoTracedValue. This template will always resolve (with
450 // |value| being set to either true or false depending on presence of the
451 // support, so this macro is useful in the situation when you want to e.g. OR
452 // the result with some other conditions.
453 //
454 // In this case, compiler will not give you the full deduction chain, so, for
455 // example, use check_traced_value_support for writing positive static_asserts
456 // and has_traced_value_support for writing negative.
457 template <typename T>
458 class has_traced_value_support {
459 using Yes = char[1];
460 using No = char[2];
461
462 template <typename V>
463 static Yes& check_support(check_traced_value_support_t<V, int>);
464 template <typename V>
465 static No& check_support(...);
466
467 public:
468 static constexpr bool value = sizeof(Yes) == sizeof(check_support<T>(0));
469 };
470
471 } // namespace internal
472
473 template <typename T>
474 void WriteIntoTracedValue(TracedValue context, T&& value) {
475 // TODO(altimin): Add a URL to documentation and a list of common failure
476 // patterns.
477 static_assert(
478 internal::has_traced_value_support<T>::value,
479 "The provided type (passed to TRACE_EVENT argument / TracedArray::Append "
480 "/ TracedDictionary::Add) does not support being written in a trace "
481 "format. Please see the comment in traced_value.h for more details.");
482
483 // Should be kept in sync with check_traced_value_support_t!
484 internal::WriteImpl(base::priority_tag<internal::kMaxWriteImplPriority>(),
485 std::move(context), std::forward<T>(value));
486 }
487
488 // Helpers to write a given value into TracedValue even if the given type
489 // doesn't support conversion (in which case the provided fallback should be
490 // used). Useful for automatically generating conversions for autogenerated
491 // code, but otherwise shouldn't be used as non-autogenerated code is expected
492 // to define WriteIntoTracedValue convertor.
493 // See WriteWithFallback test in traced_value_unittest.cc for a concrete
494 // example.
495 template <typename T>
496 typename std::enable_if<internal::has_traced_value_support<T>::value>::type
497 WriteIntoTracedValueWithFallback(TracedValue context,
498 T&& value,
499 const std::string&) {
500 WriteIntoTracedValue(std::move(context), std::forward<T>(value));
501 }
502
503 template <typename T>
504 typename std::enable_if<!internal::has_traced_value_support<T>::value>::type
505 WriteIntoTracedValueWithFallback(TracedValue context,
506 T&&,
507 const std::string& fallback) {
508 std::move(context).WriteString(fallback);
509 }
510
511 // TraceFormatTraits implementations for primitive types.
512
513 // Specialisation for signed integer types (note: it excludes enums, which have
514 // their own explicit specialisation).
515 template <typename T>
516 struct TraceFormatTraits<
517 T,
518 typename std::enable_if<std::is_integral<T>::value &&
519 !std::is_same<T, bool>::value &&
520 std::is_signed<T>::value>::type> {
521 inline static void WriteIntoTrace(TracedValue context, T value) {
522 std::move(context).WriteInt64(value);
523 }
524 };
525
526 // Specialisation for unsigned integer types (note: it excludes enums, which
527 // have their own explicit specialisation).
528 template <typename T>
529 struct TraceFormatTraits<
530 T,
531 typename std::enable_if<std::is_integral<T>::value &&
532 !std::is_same<T, bool>::value &&
533 std::is_unsigned<T>::value>::type> {
534 inline static void WriteIntoTrace(TracedValue context, T value) {
535 std::move(context).WriteUInt64(value);
536 }
537 };
538
539 // Specialisation for bools.
540 template <>
541 struct TraceFormatTraits<bool> {
542 inline static void WriteIntoTrace(TracedValue context, bool value) {
543 std::move(context).WriteBoolean(value);
544 }
545 };
546
547 // Specialisation for floating point values.
548 template <typename T>
549 struct TraceFormatTraits<
550 T,
551 typename std::enable_if<std::is_floating_point<T>::value>::type> {
552 inline static void WriteIntoTrace(TracedValue context, T value) {
553 std::move(context).WriteDouble(static_cast<double>(value));
554 }
555 };
556
557 // Specialisation for signed enums.
558 template <typename T>
559 struct TraceFormatTraits<
560 T,
561 typename std::enable_if<
562 std::is_enum<T>::value &&
563 std::is_signed<
564 typename internal::safe_underlying_type<T>::type>::value>::type> {
565 inline static void WriteIntoTrace(TracedValue context, T value) {
566 std::move(context).WriteInt64(static_cast<int64_t>(value));
567 }
568 };
569
570 // Specialisation for unsigned enums.
571 template <typename T>
572 struct TraceFormatTraits<
573 T,
574 typename std::enable_if<
575 std::is_enum<T>::value &&
576 std::is_unsigned<
577 typename internal::safe_underlying_type<T>::type>::value>::type> {
578 inline static void WriteIntoTrace(TracedValue context, T value) {
579 std::move(context).WriteUInt64(static_cast<uint64_t>(value));
580 }
581 };
582
583 // Specialisations for C-style strings.
584 template <>
585 struct TraceFormatTraits<const char*> {
586 inline static void WriteIntoTrace(TracedValue context, const char* value) {
587 std::move(context).WriteString(value);
588 }
589 };
590
591 template <>
592 struct TraceFormatTraits<char[]> {
593 inline static void WriteIntoTrace(TracedValue context, const char value[]) {
594 std::move(context).WriteString(value);
595 }
596 };
597
598 template <size_t N>
599 struct TraceFormatTraits<char[N]> {
600 inline static void WriteIntoTrace(TracedValue context, const char value[N]) {
601 std::move(context).WriteString(value);
602 }
603 };
604
605 // Specialisation for C++ strings.
606 template <>
607 struct TraceFormatTraits<std::string> {
608 inline static void WriteIntoTrace(TracedValue context,
609 const std::string& value) {
610 std::move(context).WriteString(value);
611 }
612 };
613
614 // Specialisation for (const) void*, which writes the pointer value.
615 template <>
616 struct TraceFormatTraits<void*> {
617 inline static void WriteIntoTrace(TracedValue context, void* value) {
618 std::move(context).WritePointer(value);
619 }
620 };
621
622 template <>
623 struct TraceFormatTraits<const void*> {
624 inline static void WriteIntoTrace(TracedValue context, const void* value) {
625 std::move(context).WritePointer(value);
626 }
627 };
628
629 // Specialisation for std::unique_ptr<>, which writes either nullptr or the
630 // object it points to.
631 template <typename T>
632 struct TraceFormatTraits<std::unique_ptr<T>, check_traced_value_support_t<T>> {
633 inline static void WriteIntoTrace(TracedValue context,
634 const std::unique_ptr<T>& value) {
635 ::perfetto::WriteIntoTracedValue(std::move(context), value.get());
636 }
637
638 template <typename MessageType>
639 inline static void WriteIntoTrace(TracedProto<MessageType> message,
640 const std::unique_ptr<T>& value) {
641 ::perfetto::WriteIntoTracedProto(std::move(message), value.get());
642 }
643 };
644
645 // Specialisation for raw pointer, which writes either nullptr or the object it
646 // points to.
647 template <typename T>
648 struct TraceFormatTraits<T*, check_traced_value_support_t<T>> {
649 inline static void WriteIntoTrace(TracedValue context, T* value) {
650 if (!value) {
651 std::move(context).WritePointer(nullptr);
652 return;
653 }
654 ::perfetto::WriteIntoTracedValue(std::move(context), *value);
655 }
656
657 template <typename MessageType>
658 inline static void WriteIntoTrace(TracedProto<MessageType> message,
659 T* value) {
660 if (!value) {
661 // Start the message, but do not write anything. TraceProcessor will emit
662 // a NULL value.
663 return;
664 }
665
666 ::perfetto::WriteIntoTracedProto(std::move(message), *value);
667 }
668 };
669
670 // Specialisation for nullptr.
671 template <>
672 struct TraceFormatTraits<std::nullptr_t> {
673 inline static void WriteIntoTrace(TracedValue context, std::nullptr_t) {
674 std::move(context).WritePointer(nullptr);
675 }
676
677 template <typename MessageType>
678 inline static void WriteIntoTrace(TracedProto<MessageType>, std::nullptr_t) {
679 // Start the message, but do not write anything. TraceProcessor will emit a
680 // NULL value.
681 }
682 };
683
684 } // namespace perfetto
685
686 #endif // INCLUDE_PERFETTO_TRACING_TRACED_VALUE_H_
687