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