• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2018 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef BASE_TRACE_EVENT_TRACE_ARGUMENTS_H_
6 #define BASE_TRACE_EVENT_TRACE_ARGUMENTS_H_
7 
8 #include <stdlib.h>
9 #include <string.h>
10 
11 #include <algorithm>
12 #include <memory>
13 #include <string>
14 #include <utility>
15 
16 #include "base/base_export.h"
17 #include "base/memory/raw_ptr_exclusion.h"
18 #include "base/trace_event/common/trace_event_common.h"
19 #include "base/tracing_buildflags.h"
20 #include "third_party/perfetto/include/perfetto/protozero/scattered_heap_buffer.h"
21 #include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
22 #include "third_party/perfetto/protos/perfetto/trace/track_event/debug_annotation.pbzero.h"
23 
24 // Trace macro can have one or two optional arguments, each one of them
25 // identified by a name (a C string literal) and a value, which can be an
26 // integer, enum, floating point, boolean, string pointer or reference, or
27 // std::unique_ptr<ConvertableToTraceFormat> compatible values. Additionally,
28 // custom data types need to be supported, like time values or WTF::CString.
29 //
30 // TraceArguments is a helper class used to store 0 to 2 named arguments
31 // corresponding to an individual trace macro call. As efficiently as possible,
32 // and with the minimal amount of generated machine code (since this affects
33 // any TRACE macro call). Each argument has:
34 //
35 //  - A name (C string literal, e.g "dumps")
36 //  - An 8-bit type value, corresponding to the TRACE_VALUE_TYPE_XXX macros.
37 //  - A value, stored in a TraceValue union
38 //
39 // IMPORTANT: For a TRACE_VALUE_TYPE_CONVERTABLE types, the TraceArguments
40 // instance owns the pointed ConvertableToTraceFormat object, i.e. it will
41 // delete it automatically on destruction.
42 //
43 // TraceArguments instances should be built using one of specialized
44 // constructors declared below. One cannot modify an instance once it has
45 // been built, except for move operations, Reset() and destruction. Examples:
46 //
47 //    TraceArguments args;    // No arguments.
48 //    // args.size() == 0
49 //
50 //    TraceArguments("foo", 100);
51 //    // args.size() == 1
52 //    // args.types()[0] == TRACE_VALUE_TYPE_INT
53 //    // args.names()[0] == "foo"
54 //    // args.values()[0].as_int == 100
55 //
56 //    TraceArguments("bar", 1ULL);
57 //    // args.size() == 1
58 //    // args.types()[0] == TRACE_VALUE_TYPE_UINT
59 //    // args.names()[0] == "bar"
60 //    // args.values()[0].as_uint == 100
61 //
62 //    TraceArguments("foo", "Hello", "bar", "World");
63 //    // args.size() == 2
64 //    // args.types()[0] == TRACE_VALUE_TYPE_STRING
65 //    // args.types()[1] == TRACE_VALUE_TYPE_STRING
66 //    // args.names()[0] == "foo"
67 //    // args.names()[1] == "bar"
68 //    // args.values()[0].as_string == "Hello"
69 //    // args.values()[1].as_string == "World"
70 //
71 //    std::string some_string = ...;
72 //    TraceArguments("str1", some_string);
73 //    // args.size() == 1
74 //    // args.types()[0] == TRACE_VALUE_TYPE_COPY_STRING
75 //    // args.names()[0] == "str1"
76 //    // args.values()[0].as_string == some_string.c_str()
77 //
78 // Note that TRACE_VALUE_TYPE_COPY_STRING corresponds to string pointers
79 // that point to temporary values that may disappear soon. The
80 // TraceArguments::CopyStringTo() method can be used to copy their content
81 // into a StringStorage memory block, and update the |as_string| value pointers
82 // to it to avoid keeping any dangling pointers. This is used by TraceEvent
83 // to keep copies of such strings in the log after their initialization values
84 // have disappeared.
85 //
86 // The TraceStringWithCopy helper class can be used to initialize a value
87 // from a regular string pointer with TRACE_VALUE_TYPE_COPY_STRING too, as in:
88 //
89 //     const char str[] = "....";
90 //     TraceArguments("foo", str, "bar", TraceStringWithCopy(str));
91 //     // args.size() == 2
92 //     // args.types()[0] == TRACE_VALUE_TYPE_STRING
93 //     // args.types()[1] == TRACE_VALUE_TYPE_COPY_STRING
94 //     // args.names()[0] == "foo"
95 //     // args.names()[1] == "bar"
96 //     // args.values()[0].as_string == str
97 //     // args.values()[1].as_string == str
98 //
99 //     StringStorage storage;
100 //     args.CopyStringTo(&storage, false, nullptr, nullptr);
101 //     // args.size() == 2
102 //     // args.types()[0] == TRACE_VALUE_TYPE_STRING
103 //     // args.types()[1] == TRACE_VALUE_TYPE_COPY_STRING
104 //     // args.names()[0] == "foo"
105 //     // args.names()[1] == "bar"
106 //     // args.values()[0].as_string == str
107 //     // args.values()[1].as_string == Address inside |storage|.
108 //
109 // Initialization from a std::unique_ptr<ConvertableToTraceFormat>
110 // is supported but will move ownership of the pointer objects to the
111 // TraceArguments instance:
112 //
113 //     class MyConvertableType :
114 //         public base::trace_event::AsConvertableToTraceFormat {
115 //        ...
116 //     };
117 //
118 //     {
119 //       TraceArguments args("foo" , std::make_unique<MyConvertableType>(...));
120 //       // args.size() == 1
121 //       // args.values()[0].as_convertable == address of MyConvertable object.
122 //     } // Calls |args| destructor, which will delete the object too.
123 //
124 // Finally, it is possible to support initialization from custom values by
125 // specializing the TraceValue::Helper<> template struct as described below.
126 //
127 // This is how values of custom types like WTF::CString can be passed directly
128 // to trace macros.
129 
130 namespace base {
131 
132 class Time;
133 class TimeTicks;
134 class ThreadTicks;
135 
136 namespace trace_event {
137 
138 class TraceEventMemoryOverhead;
139 
140 // For any argument of type TRACE_VALUE_TYPE_CONVERTABLE the provided
141 // class must implement this interface. Note that unlike other values,
142 // these objects will be owned by the TraceArguments instance that points
143 // to them.
144 class BASE_EXPORT ConvertableToTraceFormat
145 #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
146     : public perfetto::DebugAnnotation
147 #endif
148 {
149  public:
150   ConvertableToTraceFormat() = default;
151   ConvertableToTraceFormat(const ConvertableToTraceFormat&) = delete;
152   ConvertableToTraceFormat& operator=(const ConvertableToTraceFormat&) = delete;
153 #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
154   ~ConvertableToTraceFormat() override = default;
155 #else
156   virtual ~ConvertableToTraceFormat() = default;
157 #endif
158 
159   // Append the class info to the provided |out| string. The appended
160   // data must be a valid JSON object. Strings must be properly quoted, and
161   // escaped. There is no processing applied to the content after it is
162   // appended.
163   virtual void AppendAsTraceFormat(std::string* out) const = 0;
164 
165   // Append the class info directly into the Perfetto-defined proto
166   // format; this is attempted first and if this returns true,
167   // AppendAsTraceFormat is not called. The ProtoAppender interface
168   // acts as a bridge to avoid proto/Perfetto dependencies in base.
169   class BASE_EXPORT ProtoAppender {
170    public:
171     virtual ~ProtoAppender() = default;
172 
173     virtual void AddBuffer(uint8_t* begin, uint8_t* end) = 0;
174     // Copy all of the previous buffers registered with AddBuffer
175     // into the proto, with the given |field_id|.
176     virtual size_t Finalize(uint32_t field_id) = 0;
177   };
178   virtual bool AppendToProto(ProtoAppender* appender) const;
179 
180   virtual void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);
181 
182 #if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
183   // DebugAnnotation implementation.
184   void Add(perfetto::protos::pbzero::DebugAnnotation*) const override;
185 #endif
186 };
187 
188 const int kTraceMaxNumArgs = 2;
189 
190 // A union used to hold the values of individual trace arguments.
191 //
192 // This is a POD union for performance reason. Initialization from an
193 // explicit C++ trace argument should be performed with the Init()
194 // templated method described below.
195 //
196 // Initialization from custom types is possible by implementing a custom
197 // TraceValue::Helper<> instantiation as described below.
198 //
199 // IMPORTANT: Pointer storage inside a TraceUnion follows specific rules:
200 //
201 //   - |as_pointer| is for raw pointers that should be treated as a simple
202 //     address and will never be dereferenced. Associated with the
203 //     TRACE_VALUE_TYPE_POINTER type.
204 //
205 //   - |as_string| is for C-string pointers, associated with both
206 //     TRACE_VALUE_TYPE_STRING and TRACE_VALUE_TYPE_COPY_STRING. The former
207 //     indicates that the string pointer is persistent (e.g. a C string
208 //     literal), while the second indicates that the pointer belongs to a
209 //     temporary variable that may disappear soon. The TraceArguments class
210 //     provides a CopyStringTo() method to copy these strings into a
211 //     StringStorage instance, which is useful if the instance needs to
212 //     survive longer than the temporaries.
213 //
214 //   - |as_convertable| is equivalent to
215 //     std::unique_ptr<ConvertableToTraceFormat>, except that it is a pointer
216 //     to keep this union POD and avoid un-necessary declarations and potential
217 //     code generation. This means that its ownership is passed to the
218 //     TraceValue instance when Init(std::unique_ptr<ConvertableToTraceFormat>)
219 //     is called, and that it will be deleted by the containing TraceArguments
220 //     destructor, or Reset() method.
221 //
222 union BASE_EXPORT TraceValue {
223   bool as_bool;
224   unsigned long long as_uint;
225   long long as_int;
226   double as_double;
227   // This field is not a raw_ptr<> because it was filtered by the rewriter for:
228   // #union
229   RAW_PTR_EXCLUSION const void* as_pointer;
230   const char* as_string;
231   // This field is not a raw_ptr<> because it was filtered by the rewriter for:
232   // #union
233   RAW_PTR_EXCLUSION ConvertableToTraceFormat* as_convertable;
234   // This field is not a raw_ptr<> because it was filtered by the rewriter for:
235   // #union
236   RAW_PTR_EXCLUSION protozero::HeapBuffered<
237       perfetto::protos::pbzero::DebugAnnotation>* as_proto;
238 
239   // Static method to create a new TraceValue instance from a given
240   // initialization value. Note that this deduces the TRACE_VALUE_TYPE_XXX
241   // type but doesn't return it, use ForType<T>::value for this.
242   //
243   // Usage example:
244   //     auto v = TraceValue::Make(100);
245   //     auto v2 = TraceValue::Make("Some text string");
246   //
247   // IMPORTANT: Experience shows that the compiler generates worse code when
248   // using this method rather than calling Init() directly on an existing
249   // TraceValue union :-(
250   //
251   template <typename T>
Make(T && value)252   static TraceValue Make(T&& value) {
253     TraceValue ret;
254     ret.Init(std::forward<T>(value));
255     return ret;
256   }
257 
258   // Output current value as a JSON string. |type| must be a valid
259   // TRACE_VALUE_TYPE_XXX value.
260   void AppendAsJSON(unsigned char type, std::string* out) const;
261 
262   // Output current value as a string. If the output string is to be used
263   // in a JSON format use AppendAsJSON instead. |type| must be valid
264   // TRACE_VALUE_TYPE_XXX value.
265   void AppendAsString(unsigned char type, std::string* out) const;
266 
267  private:
268   void Append(unsigned char type, bool as_json, std::string* out) const;
269 
270   // InnerType<T>::type removes reference, cv-qualifications and decays
271   // function and arrays into pointers. Only used internally.
272   template <typename T>
273   struct InnerType {
274     using type = std::decay_t<T>;
275   };
276 
277  public:
278   // TraceValue::Helper is used to provide information about initialization
279   // value types and an initialization function. It is a struct that should
280   // provide the following for supported initialization value types:
281   //
282   //    - kType: is a static TRACE_VALUE_TYPE_XXX constant.
283   //
284   //    - SetValue(TraceValue*, T): is a static inline method that sets
285   //        TraceValue value from a given T value. Second parameter type
286   //        can also be const T& or T&& to restrict uses.
287   //
288   // IMPORTANT: The type T must be InnerType<Q>, where Q is the real C++
289   // argument type. I.e. you should not have to deal with reference types
290   // in your specialization.
291   //
292   // Specializations are defined for integers, enums, floating point, pointers,
293   // constant C string literals and pointers, std::string, time values below.
294   //
295   // Specializations for custom types are possible provided that there exists
296   // a corresponding Helper specialization, for example:
297   //
298   //    template <>
299   //    struct base::trace_event::TraceValue::Helper<Foo> {
300   //      static constexpr unsigned char kTypes = TRACE_VALUE_TYPE_COPY_STRING;
301   //      static inline void SetValue(TraceValue* v, const Foo& value) {
302   //        v->as_string = value.c_str();
303   //      }
304   //    };
305   //
306   // Will allow code like:
307   //
308   //    Foo foo = ...;
309   //    auto v = TraceValue::Make(foo);
310   //
311   // Or even:
312   //    Foo foo = ...;
313   //    TraceArguments args("foo_arg1", foo);
314   //
315   template <typename T, class = void>
316   struct Helper {};
317 
318   template <typename T>
319   struct HasHelperSupport {
320    private:
321     using Yes = char[1];
322     using No = char[2];
323 
324     template <typename V>
325     static Yes& check_support(
326         decltype(TraceValue::Helper<typename InnerType<V>::type>::kType,
327                  int()));
328     template <typename V>
329     static No& check_support(...);
330 
331    public:
332     static constexpr bool value = sizeof(Yes) == sizeof(check_support<T>(0));
333   };
334 
335   // TraceValue::TypeFor<T>::value returns the TRACE_VALUE_TYPE_XXX
336   // corresponding to initialization values of type T.
337   template <typename T, class = void>
338   struct TypeFor;
339 
340   template <typename T>
341   struct TypeFor<T,
342                  typename std::enable_if<HasHelperSupport<
343                      typename InnerType<T>::type>::value>::type> {
344     using ValueType = typename InnerType<T>::type;
345     static const unsigned char value = Helper<ValueType>::kType;
346   };
347   template <typename T>
348   struct TypeFor<T,
349                  typename std::enable_if<
350                      !HasHelperSupport<typename InnerType<T>::type>::value &&
351                      perfetto::internal::has_traced_value_support<
352                          typename InnerType<T>::type>::value>::type> {
353     static const unsigned char value = TRACE_VALUE_TYPE_PROTO;
354   };
355 
356   // TraceValue::TypeCheck<T>::value is only defined iff T can be used to
357   // initialize a TraceValue instance. This is useful to restrict template
358   // instantiation to only the appropriate type (see TraceArguments
359   // constructors below).
360   template <typename T,
361             class = std::enable_if_t<
362                 HasHelperSupport<typename InnerType<T>::type>::value ||
363                 perfetto::internal::has_traced_value_support<
364                     typename InnerType<T>::type>::value>>
365   struct TypeCheck {
366     static const bool value = true;
367   };
368 
369   // There is no constructor to keep this structure POD intentionally.
370   // This avoids un-needed initialization when only 0 or 1 arguments are
371   // used to construct a TraceArguments instance. Use Init() instead to
372   // perform explicit initialization from a given C++ value.
373 
374   // Initialize TraceValue instance from a C++ trace value.
375   // This relies on the proper specialization of TraceValue::Helper<>
376   // described below. Usage is simply:
377   //
378   //  TraceValue v;
379   //  v.Init(<value>);
380   //
381   // NOTE: For ConvertableToTraceFormat values, see the notes above.
382   template <class T>
383   typename std::enable_if<
384       HasHelperSupport<typename InnerType<T>::type>::value>::type
385   Init(T&& value) {
386     using ValueType = typename InnerType<T>::type;
387     Helper<ValueType>::SetValue(this, std::forward<T>(value));
388   }
389 
390   template <class T>
391   typename std::enable_if<
392       !HasHelperSupport<typename InnerType<T>::type>::value &&
393       perfetto::internal::has_traced_value_support<
394           typename InnerType<T>::type>::value>::type
395   Init(T&& value) {
396     as_proto = new protozero::HeapBuffered<
397         perfetto::protos::pbzero::DebugAnnotation>();
398     perfetto::WriteIntoTracedValue(
399         perfetto::internal::CreateTracedValueFromProto(as_proto->get()),
400         std::forward<T>(value));
401   }
402 };
403 
404 // TraceValue::Helper for integers and enums.
405 template <typename T>
406 struct TraceValue::Helper<
407     T,
408     typename std::enable_if<std::is_integral<T>::value ||
409                             std::is_enum<T>::value>::type> {
410   static constexpr unsigned char kType =
411       std::is_signed<T>::value ? TRACE_VALUE_TYPE_INT : TRACE_VALUE_TYPE_UINT;
412   static inline void SetValue(TraceValue* v, T value) {
413     v->as_uint = static_cast<unsigned long long>(value);
414   }
415 };
416 
417 // TraceValue::Helper for floating-point types
418 template <typename T>
419 struct TraceValue::
420     Helper<T, typename std::enable_if<std::is_floating_point<T>::value>::type> {
421   static constexpr unsigned char kType = TRACE_VALUE_TYPE_DOUBLE;
422   static inline void SetValue(TraceValue* v, T value) { v->as_double = value; }
423 };
424 
425 // TraceValue::Helper for bool.
426 template <>
427 struct TraceValue::Helper<bool> {
428   static constexpr unsigned char kType = TRACE_VALUE_TYPE_BOOL;
429   static inline void SetValue(TraceValue* v, bool value) { v->as_bool = value; }
430 };
431 
432 //  TraceValue::Helper for generic pointer types.
433 template <>
434 struct TraceValue::Helper<const void*> {
435   static constexpr unsigned char kType = TRACE_VALUE_TYPE_POINTER;
436   static inline void SetValue(TraceValue* v, const void* value) {
437     v->as_pointer = value;
438   }
439 };
440 
441 template <>
442 struct TraceValue::Helper<void*> {
443   static constexpr unsigned char kType = TRACE_VALUE_TYPE_POINTER;
444   static inline void SetValue(TraceValue* v, void* value) {
445     v->as_pointer = value;
446   }
447 };
448 
449 // TraceValue::Helper for raw persistent C strings.
450 template <>
451 struct TraceValue::Helper<const char*> {
452   static constexpr unsigned char kType = TRACE_VALUE_TYPE_STRING;
453   static inline void SetValue(TraceValue* v, const char* value) {
454     v->as_string = value;
455   }
456 };
457 
458 // TraceValue::Helper for std::string values.
459 template <>
460 struct TraceValue::Helper<std::string> {
461   static constexpr unsigned char kType = TRACE_VALUE_TYPE_COPY_STRING;
462   static inline void SetValue(TraceValue* v, const std::string& value) {
463     v->as_string = value.c_str();
464   }
465 };
466 
467 // Special case for scoped pointers to convertables to trace format.
468 // |CONVERTABLE_TYPE| must be a type whose pointers can be converted to a
469 // ConvertableToTraceFormat* pointer as well (e.g. a derived class).
470 // IMPORTANT: This takes an std::unique_ptr<CONVERTABLE_TYPE> value, and takes
471 // ownership of the pointed object!
472 template <typename CONVERTABLE_TYPE>
473 struct TraceValue::Helper<std::unique_ptr<CONVERTABLE_TYPE>,
474                           typename std::enable_if<std::is_convertible<
475                               CONVERTABLE_TYPE*,
476                               ConvertableToTraceFormat*>::value>::type> {
477   static constexpr unsigned char kType = TRACE_VALUE_TYPE_CONVERTABLE;
478   static inline void SetValue(TraceValue* v,
479                               std::unique_ptr<CONVERTABLE_TYPE> value) {
480     v->as_convertable = value.release();
481   }
482 };
483 
484 // Specialization for time-based values like base::Time, which provide a
485 // a ToInternalValue() method.
486 template <typename T>
487 struct TraceValue::Helper<
488     T,
489     typename std::enable_if<std::is_same<T, base::Time>::value ||
490                             std::is_same<T, base::TimeTicks>::value ||
491                             std::is_same<T, base::ThreadTicks>::value>::type> {
492   static constexpr unsigned char kType = TRACE_VALUE_TYPE_INT;
493   static inline void SetValue(TraceValue* v, const T& value) {
494     v->as_int = value.ToInternalValue();
495   }
496 };
497 
498 // Simple container for const char* that should be copied instead of retained.
499 // The goal is to indicate that the C string is copyable, unlike the default
500 // Init(const char*) implementation. Usage is:
501 //
502 //    const char* str = ...;
503 //    v.Init(TraceStringWithCopy(str));
504 //
505 // Which will mark the string as TRACE_VALUE_TYPE_COPY_STRING, instead of
506 // TRACE_VALUE_TYPE_STRING.
507 //
508 class TraceStringWithCopy {
509  public:
510   explicit TraceStringWithCopy(const char* str) : str_(str) {}
511   const char* str() const { return str_; }
512 
513  private:
514   const char* str_;
515 };
516 
517 template <>
518 struct TraceValue::Helper<TraceStringWithCopy> {
519   static constexpr unsigned char kType = TRACE_VALUE_TYPE_COPY_STRING;
520   static inline void SetValue(TraceValue* v, const TraceStringWithCopy& value) {
521     v->as_string = value.str();
522   }
523 };
524 
525 class TraceArguments;
526 
527 // A small class used to store a copy of all strings from a given
528 // TraceArguments instance (see below). When empty, this should only
529 // take the size of a pointer. Otherwise, this will point to a heap
530 // allocated block containing a size_t value followed by all characters
531 // in the storage area. For most cases, this is more efficient
532 // than using a std::unique_ptr<std::string> or an std::vector<char>.
533 class BASE_EXPORT StringStorage {
534  public:
535   constexpr StringStorage() = default;
536 
537   explicit StringStorage(size_t alloc_size) { Reset(alloc_size); }
538 
539   ~StringStorage() {
540     if (data_)
541       ::free(data_);
542   }
543 
544   StringStorage(StringStorage&& other) noexcept : data_(other.data_) {
545     other.data_ = nullptr;
546   }
547 
548   StringStorage& operator=(StringStorage&& other) noexcept {
549     if (this != &other) {
550       if (data_)
551         ::free(data_);
552       data_ = other.data_;
553       other.data_ = nullptr;
554     }
555     return *this;
556   }
557 
558   // Reset storage area to new allocation size. Existing content might not
559   // be preserved. If |alloc_size| is 0, this will free the storage area
560   // as well.
561   void Reset(size_t alloc_size = 0);
562 
563   // Accessors.
564   constexpr size_t size() const { return data_ ? data_->size : 0u; }
565   constexpr const char* data() const { return data_ ? data_->chars : nullptr; }
566   constexpr char* data() { return data_ ? data_->chars : nullptr; }
567 
568   constexpr const char* begin() const { return data(); }
569   constexpr const char* end() const { return data() + size(); }
570   inline char* begin() { return data(); }
571   inline char* end() { return data() + size(); }
572 
573   // True iff storage is empty.
574   constexpr bool empty() const { return size() == 0; }
575 
576   // Returns true if |ptr| is inside the storage area, false otherwise.
577   // Used during unit-testing.
578   constexpr bool Contains(const void* ptr) const {
579     const char* char_ptr = static_cast<const char*>(ptr);
580     return (char_ptr >= begin() && char_ptr < end());
581   }
582 
583   // Returns true if all string pointers in |args| are contained in this
584   // storage area.
585   bool Contains(const TraceArguments& args) const;
586 
587   // Return an estimate of the memory overhead of this instance. This doesn't
588   // count the size of |data_| itself.
589   constexpr size_t EstimateTraceMemoryOverhead() const {
590     return data_ ? sizeof(size_t) + data_->size : 0u;
591   }
592 
593  private:
594   // Heap allocated data block (variable size), made of:
595   //
596   //   - size: a size_t field, giving the size of the following |chars| array.
597   //   - chars: an array of |size| characters, holding all zero-terminated
598   //     strings referenced from a TraceArguments instance.
599   struct Data {
600     size_t size = 0;
601     char chars[1];  // really |size| character items in storage.
602   };
603 
604   // This is an owning pointer. Normally, using a std::unique_ptr<> would be
605   // enough, but the compiler will then complaing about inlined constructors
606   // and destructors being too complex (!), resulting in larger code for no
607   // good reason.
608   // This field is not a raw_ptr<> because it was filtered by the rewriter for:
609   // #constexpr-ctor-field-initializer
610   RAW_PTR_EXCLUSION Data* data_ = nullptr;
611 };
612 
613 // TraceArguments models an array of kMaxSize trace-related items,
614 // each one of them having:
615 //   - a name, which is a constant char array literal.
616 //   - a type, as described by TRACE_VALUE_TYPE_XXX macros.
617 //   - a value, stored in a TraceValue union.
618 //
619 // IMPORTANT: For TRACE_VALUE_TYPE_CONVERTABLE, the value holds an owning
620 //            pointer to an AsConvertableToTraceFormat instance, which will
621 //            be destroyed with the array (or moved out of it when passed
622 //            to a TraceEvent instance).
623 //
624 // For TRACE_VALUE_TYPE_COPY_STRING, the value holds a const char* pointer
625 // whose content will be copied when creating a TraceEvent instance.
626 //
627 // IMPORTANT: Most constructors and the destructor are all inlined
628 // intentionally, in order to let the compiler remove un-necessary operations
629 // and reduce machine code.
630 //
631 class BASE_EXPORT TraceArguments {
632  public:
633   // Maximum number of arguments held by this structure.
634   static constexpr size_t kMaxSize = 2;
635 
636   // Default constructor, no arguments.
637   TraceArguments() : size_(0) {}
638 
639   // Constructor for a single argument.
640   template <typename T, class = decltype(TraceValue::TypeCheck<T>::value)>
641   TraceArguments(const char* arg1_name, T&& arg1_value) : size_(1) {
642     types_[0] = TraceValue::TypeFor<T>::value;
643     names_[0] = arg1_name;
644     values_[0].Init(std::forward<T>(arg1_value));
645   }
646 
647   // Constructor for two arguments.
648   template <typename T1,
649             typename T2,
650             class = decltype(TraceValue::TypeCheck<T1>::value &&
651                              TraceValue::TypeCheck<T2>::value)>
652   TraceArguments(const char* arg1_name,
653                  T1&& arg1_value,
654                  const char* arg2_name,
655                  T2&& arg2_value)
656       : size_(2) {
657     types_[0] = TraceValue::TypeFor<T1>::value;
658     types_[1] = TraceValue::TypeFor<T2>::value;
659     names_[0] = arg1_name;
660     names_[1] = arg2_name;
661     values_[0].Init(std::forward<T1>(arg1_value));
662     values_[1].Init(std::forward<T2>(arg2_value));
663   }
664 
665   // Constructor used to convert a legacy set of arguments when there
666   // are no convertable values at all.
667   TraceArguments(int num_args,
668                  const char* const* arg_names,
669                  const unsigned char* arg_types,
670                  const unsigned long long* arg_values);
671 
672   // Constructor used to convert legacy set of arguments, where the
673   // convertable values are also provided by an array of CONVERTABLE_TYPE.
674   template <typename CONVERTABLE_TYPE>
675   TraceArguments(int num_args,
676                  const char* const* arg_names,
677                  const unsigned char* arg_types,
678                  const unsigned long long* arg_values,
679                  CONVERTABLE_TYPE* arg_convertables) {
680     static int max_args = static_cast<int>(kMaxSize);
681     if (num_args > max_args)
682       num_args = max_args;
683     size_ = static_cast<unsigned char>(num_args);
684     for (size_t n = 0; n < size_; ++n) {
685       types_[n] = arg_types[n];
686       names_[n] = arg_names[n];
687       if (arg_types[n] == TRACE_VALUE_TYPE_CONVERTABLE) {
688         values_[n].Init(
689             std::forward<CONVERTABLE_TYPE>(std::move(arg_convertables[n])));
690       } else {
691         values_[n].as_uint = arg_values[n];
692       }
693     }
694   }
695 
696   // Destructor. NOTE: Intentionally inlined (see note above).
697   ~TraceArguments() {
698     for (size_t n = 0; n < size_; ++n) {
699       if (types_[n] == TRACE_VALUE_TYPE_CONVERTABLE)
700         delete values_[n].as_convertable;
701       if (types_[n] == TRACE_VALUE_TYPE_PROTO)
702         delete values_[n].as_proto;
703     }
704   }
705 
706   // Disallow copy operations.
707   TraceArguments(const TraceArguments&) = delete;
708   TraceArguments& operator=(const TraceArguments&) = delete;
709 
710   // Allow move operations.
711   TraceArguments(TraceArguments&& other) noexcept {
712     ::memcpy(this, &other, sizeof(*this));
713     // All owning pointers were copied to |this|. Setting |other.size_| will
714     // mask the pointer values still in |other|.
715     other.size_ = 0;
716   }
717 
718   TraceArguments& operator=(TraceArguments&&) noexcept;
719 
720   // Accessors
721   size_t size() const { return size_; }
722   const unsigned char* types() const { return types_; }
723   const char* const* names() const { return names_; }
724   const TraceValue* values() const { return values_; }
725 
726   // Reset to empty arguments list.
727   void Reset();
728 
729   // Use |storage| to copy all copyable strings.
730   // If |copy_all_strings| is false, then only the TRACE_VALUE_TYPE_COPY_STRING
731   // values will be copied into storage. If it is true, then argument names are
732   // also copied to storage, as well as the strings pointed to by
733   // |*extra_string1| and |*extra_string2|.
734   // NOTE: If there are no strings to copy, |*storage| is left untouched.
735   void CopyStringsTo(StringStorage* storage,
736                      bool copy_all_strings,
737                      const char** extra_string1,
738                      const char** extra_string2);
739 
740   // Append debug string representation to |*out|.
741   void AppendDebugString(std::string* out);
742 
743  private:
744   unsigned char size_;
745   unsigned char types_[kMaxSize];
746   const char* names_[kMaxSize];
747   TraceValue values_[kMaxSize];
748 };
749 
750 }  // namespace trace_event
751 }  // namespace base
752 
753 #endif  // BASE_TRACE_EVENT_TRACE_ARGUMENTS_H_
754