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