File: trace_arguments.h

package info (click to toggle)
chromium 120.0.6099.224-1~deb11u1
  • links: PTS, VCS
  • area: main
  • in suites: bullseye
  • size: 6,112,112 kB
  • sloc: cpp: 32,907,025; ansic: 8,148,123; javascript: 3,679,536; python: 2,031,248; asm: 959,718; java: 804,675; xml: 617,256; sh: 111,417; objc: 100,835; perl: 88,443; cs: 53,032; makefile: 29,579; fortran: 24,137; php: 21,162; tcl: 21,147; sql: 20,809; ruby: 17,735; pascal: 12,864; yacc: 8,045; lisp: 3,388; lex: 1,323; ada: 727; awk: 329; jsp: 267; csh: 117; exp: 43; sed: 37
file content (748 lines) | stat: -rw-r--r-- 28,070 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
// Copyright 2018 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef BASE_TRACE_EVENT_TRACE_ARGUMENTS_H_
#define BASE_TRACE_EVENT_TRACE_ARGUMENTS_H_

#include <stdlib.h>
#include <string.h>

#include <algorithm>
#include <memory>
#include <string>
#include <utility>

#include "base/base_export.h"
#include "base/memory/raw_ptr_exclusion.h"
#include "base/trace_event/common/trace_event_common.h"
#include "base/tracing_buildflags.h"
#include "third_party/perfetto/include/perfetto/protozero/scattered_heap_buffer.h"
#include "third_party/perfetto/include/perfetto/tracing/traced_value.h"
#include "third_party/perfetto/protos/perfetto/trace/track_event/debug_annotation.pbzero.h"

// Trace macro can have one or two optional arguments, each one of them
// identified by a name (a C string literal) and a value, which can be an
// integer, enum, floating point, boolean, string pointer or reference, or
// std::unique_ptr<ConvertableToTraceFormat> compatible values. Additionally,
// custom data types need to be supported, like time values or WTF::CString.
//
// TraceArguments is a helper class used to store 0 to 2 named arguments
// corresponding to an individual trace macro call. As efficiently as possible,
// and with the minimal amount of generated machine code (since this affects
// any TRACE macro call). Each argument has:
//
//  - A name (C string literal, e.g "dumps")
//  - An 8-bit type value, corresponding to the TRACE_VALUE_TYPE_XXX macros.
//  - A value, stored in a TraceValue union
//
// IMPORTANT: For a TRACE_VALUE_TYPE_CONVERTABLE types, the TraceArguments
// instance owns the pointed ConvertableToTraceFormat object, i.e. it will
// delete it automatically on destruction.
//
// TraceArguments instances should be built using one of specialized
// constructors declared below. One cannot modify an instance once it has
// been built, except for move operations, Reset() and destruction. Examples:
//
//    TraceArguments args;    // No arguments.
//    // args.size() == 0
//
//    TraceArguments("foo", 100);
//    // args.size() == 1
//    // args.types()[0] == TRACE_VALUE_TYPE_INT
//    // args.names()[0] == "foo"
//    // args.values()[0].as_int == 100
//
//    TraceArguments("bar", 1ULL);
//    // args.size() == 1
//    // args.types()[0] == TRACE_VALUE_TYPE_UINT
//    // args.names()[0] == "bar"
//    // args.values()[0].as_uint == 100
//
//    TraceArguments("foo", "Hello", "bar", "World");
//    // args.size() == 2
//    // args.types()[0] == TRACE_VALUE_TYPE_STRING
//    // args.types()[1] == TRACE_VALUE_TYPE_STRING
//    // args.names()[0] == "foo"
//    // args.names()[1] == "bar"
//    // args.values()[0].as_string == "Hello"
//    // args.values()[1].as_string == "World"
//
//    std::string some_string = ...;
//    TraceArguments("str1", some_string);
//    // args.size() == 1
//    // args.types()[0] == TRACE_VALUE_TYPE_COPY_STRING
//    // args.names()[0] == "str1"
//    // args.values()[0].as_string == some_string.c_str()
//
// Note that TRACE_VALUE_TYPE_COPY_STRING corresponds to string pointers
// that point to temporary values that may disappear soon. The
// TraceArguments::CopyStringTo() method can be used to copy their content
// into a StringStorage memory block, and update the |as_string| value pointers
// to it to avoid keeping any dangling pointers. This is used by TraceEvent
// to keep copies of such strings in the log after their initialization values
// have disappeared.
//
// The TraceStringWithCopy helper class can be used to initialize a value
// from a regular string pointer with TRACE_VALUE_TYPE_COPY_STRING too, as in:
//
//     const char str[] = "....";
//     TraceArguments("foo", str, "bar", TraceStringWithCopy(str));
//     // args.size() == 2
//     // args.types()[0] == TRACE_VALUE_TYPE_STRING
//     // args.types()[1] == TRACE_VALUE_TYPE_COPY_STRING
//     // args.names()[0] == "foo"
//     // args.names()[1] == "bar"
//     // args.values()[0].as_string == str
//     // args.values()[1].as_string == str
//
//     StringStorage storage;
//     args.CopyStringTo(&storage, false, nullptr, nullptr);
//     // args.size() == 2
//     // args.types()[0] == TRACE_VALUE_TYPE_STRING
//     // args.types()[1] == TRACE_VALUE_TYPE_COPY_STRING
//     // args.names()[0] == "foo"
//     // args.names()[1] == "bar"
//     // args.values()[0].as_string == str
//     // args.values()[1].as_string == Address inside |storage|.
//
// Initialization from a std::unique_ptr<ConvertableToTraceFormat>
// is supported but will move ownership of the pointer objects to the
// TraceArguments instance:
//
//     class MyConvertableType :
//         public base::trace_event::AsConvertableToTraceFormat {
//        ...
//     };
//
//     {
//       TraceArguments args("foo" , std::make_unique<MyConvertableType>(...));
//       // args.size() == 1
//       // args.values()[0].as_convertable == address of MyConvertable object.
//     } // Calls |args| destructor, which will delete the object too.
//
// Finally, it is possible to support initialization from custom values by
// specializing the TraceValue::Helper<> template struct as described below.
//
// This is how values of custom types like WTF::CString can be passed directly
// to trace macros.

namespace base {

class Time;
class TimeTicks;
class ThreadTicks;

namespace trace_event {

class TraceEventMemoryOverhead;

// For any argument of type TRACE_VALUE_TYPE_CONVERTABLE the provided
// class must implement this interface. Note that unlike other values,
// these objects will be owned by the TraceArguments instance that points
// to them.
class BASE_EXPORT ConvertableToTraceFormat
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
    : public perfetto::DebugAnnotation
#endif
{
 public:
  ConvertableToTraceFormat() = default;
  ConvertableToTraceFormat(const ConvertableToTraceFormat&) = delete;
  ConvertableToTraceFormat& operator=(const ConvertableToTraceFormat&) = delete;
#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
  ~ConvertableToTraceFormat() override = default;
#else
  virtual ~ConvertableToTraceFormat() = default;
#endif

  // Append the class info to the provided |out| string. The appended
  // data must be a valid JSON object. Strings must be properly quoted, and
  // escaped. There is no processing applied to the content after it is
  // appended.
  virtual void AppendAsTraceFormat(std::string* out) const = 0;

  // Append the class info directly into the Perfetto-defined proto
  // format; this is attempted first and if this returns true,
  // AppendAsTraceFormat is not called. The ProtoAppender interface
  // acts as a bridge to avoid proto/Perfetto dependencies in base.
  class BASE_EXPORT ProtoAppender {
   public:
    virtual ~ProtoAppender() = default;

    virtual void AddBuffer(uint8_t* begin, uint8_t* end) = 0;
    // Copy all of the previous buffers registered with AddBuffer
    // into the proto, with the given |field_id|.
    virtual size_t Finalize(uint32_t field_id) = 0;
  };
  virtual bool AppendToProto(ProtoAppender* appender) const;

  virtual void EstimateTraceMemoryOverhead(TraceEventMemoryOverhead* overhead);

#if BUILDFLAG(USE_PERFETTO_CLIENT_LIBRARY)
  // DebugAnnotation implementation.
  void Add(perfetto::protos::pbzero::DebugAnnotation*) const override;
#endif
};

const int kTraceMaxNumArgs = 2;

// A union used to hold the values of individual trace arguments.
//
// This is a POD union for performance reason. Initialization from an
// explicit C++ trace argument should be performed with the Init()
// templated method described below.
//
// Initialization from custom types is possible by implementing a custom
// TraceValue::Helper<> instantiation as described below.
//
// IMPORTANT: Pointer storage inside a TraceUnion follows specific rules:
//
//   - |as_pointer| is for raw pointers that should be treated as a simple
//     address and will never be dereferenced. Associated with the
//     TRACE_VALUE_TYPE_POINTER type.
//
//   - |as_string| is for C-string pointers, associated with both
//     TRACE_VALUE_TYPE_STRING and TRACE_VALUE_TYPE_COPY_STRING. The former
//     indicates that the string pointer is persistent (e.g. a C string
//     literal), while the second indicates that the pointer belongs to a
//     temporary variable that may disappear soon. The TraceArguments class
//     provides a CopyStringTo() method to copy these strings into a
//     StringStorage instance, which is useful if the instance needs to
//     survive longer than the temporaries.
//
//   - |as_convertable| is equivalent to
//     std::unique_ptr<ConvertableToTraceFormat>, except that it is a pointer
//     to keep this union POD and avoid un-necessary declarations and potential
//     code generation. This means that its ownership is passed to the
//     TraceValue instance when Init(std::unique_ptr<ConvertableToTraceFormat>)
//     is called, and that it will be deleted by the containing TraceArguments
//     destructor, or Reset() method.
//
union BASE_EXPORT TraceValue {
  bool as_bool;
  unsigned long long as_uint;
  long long as_int;
  double as_double;
  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
  // #union
  RAW_PTR_EXCLUSION const void* as_pointer;
  const char* as_string;
  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
  // #union
  RAW_PTR_EXCLUSION ConvertableToTraceFormat* as_convertable;
  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
  // #union
  RAW_PTR_EXCLUSION protozero::HeapBuffered<
      perfetto::protos::pbzero::DebugAnnotation>* as_proto;

  // Static method to create a new TraceValue instance from a given
  // initialization value. Note that this deduces the TRACE_VALUE_TYPE_XXX
  // type but doesn't return it, use ForType<T>::value for this.
  //
  // Usage example:
  //     auto v = TraceValue::Make(100);
  //     auto v2 = TraceValue::Make("Some text string");
  //
  // IMPORTANT: Experience shows that the compiler generates worse code when
  // using this method rather than calling Init() directly on an existing
  // TraceValue union :-(
  //
  template <typename T>
  static TraceValue Make(T&& value) {
    TraceValue ret;
    ret.Init(std::forward<T>(value));
    return ret;
  }

  // Output current value as a JSON string. |type| must be a valid
  // TRACE_VALUE_TYPE_XXX value.
  void AppendAsJSON(unsigned char type, std::string* out) const;

  // Output current value as a string. If the output string is to be used
  // in a JSON format use AppendAsJSON instead. |type| must be valid
  // TRACE_VALUE_TYPE_XXX value.
  void AppendAsString(unsigned char type, std::string* out) const;

 private:
  void Append(unsigned char type, bool as_json, std::string* out) const;

  // InnerType<T>::type removes reference, cv-qualifications and decays
  // function and arrays into pointers. Only used internally.
  template <typename T>
  struct InnerType {
    using type = std::decay_t<T>;
  };

 public:
  // TraceValue::Helper is used to provide information about initialization
  // value types and an initialization function. It is a struct that should
  // provide the following for supported initialization value types:
  //
  //    - kType: is a static TRACE_VALUE_TYPE_XXX constant.
  //
  //    - SetValue(TraceValue*, T): is a static inline method that sets
  //        TraceValue value from a given T value. Second parameter type
  //        can also be const T& or T&& to restrict uses.
  //
  // IMPORTANT: The type T must be InnerType<Q>, where Q is the real C++
  // argument type. I.e. you should not have to deal with reference types
  // in your specialization.
  //
  // Specializations are defined for integers, enums, floating point, pointers,
  // constant C string literals and pointers, std::string, time values below.
  //
  // Specializations for custom types are possible provided that there exists
  // a corresponding Helper specialization, for example:
  //
  //    template <>
  //    struct base::trace_event::TraceValue::Helper<Foo> {
  //      static constexpr unsigned char kTypes = TRACE_VALUE_TYPE_COPY_STRING;
  //      static inline void SetValue(TraceValue* v, const Foo& value) {
  //        v->as_string = value.c_str();
  //      }
  //    };
  //
  // Will allow code like:
  //
  //    Foo foo = ...;
  //    auto v = TraceValue::Make(foo);
  //
  // Or even:
  //    Foo foo = ...;
  //    TraceArguments args("foo_arg1", foo);
  //
  template <typename T, class = void>
  struct Helper {};

  template <typename T>
  struct HasHelperSupport {
   private:
    using Yes = char[1];
    using No = char[2];

    template <typename V>
    static Yes& check_support(
        decltype(TraceValue::Helper<typename InnerType<V>::type>::kType,
                 int()));
    template <typename V>
    static No& check_support(...);

   public:
    static constexpr bool value = sizeof(Yes) == sizeof(check_support<T>(0));
  };

  // TraceValue::TypeFor<T>::value returns the TRACE_VALUE_TYPE_XXX
  // corresponding to initialization values of type T.
  template <typename T, class = void>
  struct TypeFor;

  template <typename T>
  struct TypeFor<
      T,
      std::enable_if_t<HasHelperSupport<typename InnerType<T>::type>::value>> {
    using ValueType = typename InnerType<T>::type;
    static const unsigned char value = Helper<ValueType>::kType;
  };
  template <typename T>
  struct TypeFor<
      T,
      std::enable_if_t<!HasHelperSupport<typename InnerType<T>::type>::value &&
                       perfetto::internal::has_traced_value_support<
                           typename InnerType<T>::type>::value>> {
    static const unsigned char value = TRACE_VALUE_TYPE_PROTO;
  };

  // TraceValue::TypeCheck<T>::value is only defined iff T can be used to
  // initialize a TraceValue instance. This is useful to restrict template
  // instantiation to only the appropriate type (see TraceArguments
  // constructors below).
  template <typename T,
            class = std::enable_if_t<
                HasHelperSupport<typename InnerType<T>::type>::value ||
                perfetto::internal::has_traced_value_support<
                    typename InnerType<T>::type>::value>>
  struct TypeCheck {
    static const bool value = true;
  };

  // There is no constructor to keep this structure POD intentionally.
  // This avoids un-needed initialization when only 0 or 1 arguments are
  // used to construct a TraceArguments instance. Use Init() instead to
  // perform explicit initialization from a given C++ value.

  // Initialize TraceValue instance from a C++ trace value.
  // This relies on the proper specialization of TraceValue::Helper<>
  // described below. Usage is simply:
  //
  //  TraceValue v;
  //  v.Init(<value>);
  //
  // NOTE: For ConvertableToTraceFormat values, see the notes above.
  template <class T>
  std::enable_if_t<HasHelperSupport<typename InnerType<T>::type>::value> Init(
      T&& value) {
    using ValueType = typename InnerType<T>::type;
    Helper<ValueType>::SetValue(this, std::forward<T>(value));
  }

  template <class T>
  std::enable_if_t<!HasHelperSupport<typename InnerType<T>::type>::value &&
                   perfetto::internal::has_traced_value_support<
                       typename InnerType<T>::type>::value>
  Init(T&& value) {
    as_proto = new protozero::HeapBuffered<
        perfetto::protos::pbzero::DebugAnnotation>();
    perfetto::WriteIntoTracedValue(
        perfetto::internal::CreateTracedValueFromProto(as_proto->get()),
        std::forward<T>(value));
  }
};

// TraceValue::Helper for integers and enums.
template <typename T>
struct TraceValue::
    Helper<T, std::enable_if_t<std::is_integral_v<T> || std::is_enum_v<T>>> {
  static constexpr unsigned char kType =
      std::is_signed_v<T> ? TRACE_VALUE_TYPE_INT : TRACE_VALUE_TYPE_UINT;
  static inline void SetValue(TraceValue* v, T value) {
    v->as_uint = static_cast<unsigned long long>(value);
  }
};

// TraceValue::Helper for floating-point types
template <typename T>
struct TraceValue::Helper<T, std::enable_if_t<std::is_floating_point_v<T>>> {
  static constexpr unsigned char kType = TRACE_VALUE_TYPE_DOUBLE;
  static inline void SetValue(TraceValue* v, T value) { v->as_double = value; }
};

// TraceValue::Helper for bool.
template <>
struct TraceValue::Helper<bool> {
  static constexpr unsigned char kType = TRACE_VALUE_TYPE_BOOL;
  static inline void SetValue(TraceValue* v, bool value) { v->as_bool = value; }
};

//  TraceValue::Helper for generic pointer types.
template <>
struct TraceValue::Helper<const void*> {
  static constexpr unsigned char kType = TRACE_VALUE_TYPE_POINTER;
  static inline void SetValue(TraceValue* v, const void* value) {
    v->as_pointer = value;
  }
};

template <>
struct TraceValue::Helper<void*> {
  static constexpr unsigned char kType = TRACE_VALUE_TYPE_POINTER;
  static inline void SetValue(TraceValue* v, void* value) {
    v->as_pointer = value;
  }
};

// TraceValue::Helper for raw persistent C strings.
template <>
struct TraceValue::Helper<const char*> {
  static constexpr unsigned char kType = TRACE_VALUE_TYPE_STRING;
  static inline void SetValue(TraceValue* v, const char* value) {
    v->as_string = value;
  }
};

// TraceValue::Helper for std::string values.
template <>
struct TraceValue::Helper<std::string> {
  static constexpr unsigned char kType = TRACE_VALUE_TYPE_COPY_STRING;
  static inline void SetValue(TraceValue* v, const std::string& value) {
    v->as_string = value.c_str();
  }
};

// Special case for scoped pointers to convertables to trace format.
// |CONVERTABLE_TYPE| must be a type whose pointers can be converted to a
// ConvertableToTraceFormat* pointer as well (e.g. a derived class).
// IMPORTANT: This takes an std::unique_ptr<CONVERTABLE_TYPE> value, and takes
// ownership of the pointed object!
template <typename CONVERTABLE_TYPE>
struct TraceValue::Helper<
    std::unique_ptr<CONVERTABLE_TYPE>,
    std::enable_if_t<
        std::is_convertible_v<CONVERTABLE_TYPE*, ConvertableToTraceFormat*>>> {
  static constexpr unsigned char kType = TRACE_VALUE_TYPE_CONVERTABLE;
  static inline void SetValue(TraceValue* v,
                              std::unique_ptr<CONVERTABLE_TYPE> value) {
    v->as_convertable = value.release();
  }
};

// Specialization for time-based values like base::Time, which provide a
// a ToInternalValue() method.
template <typename T>
struct TraceValue::Helper<
    T,
    std::enable_if_t<std::is_same_v<T, base::Time> ||
                     std::is_same_v<T, base::TimeTicks> ||
                     std::is_same_v<T, base::ThreadTicks>>> {
  static constexpr unsigned char kType = TRACE_VALUE_TYPE_INT;
  static inline void SetValue(TraceValue* v, const T& value) {
    v->as_int = value.ToInternalValue();
  }
};

// Simple container for const char* that should be copied instead of retained.
// The goal is to indicate that the C string is copyable, unlike the default
// Init(const char*) implementation. Usage is:
//
//    const char* str = ...;
//    v.Init(TraceStringWithCopy(str));
//
// Which will mark the string as TRACE_VALUE_TYPE_COPY_STRING, instead of
// TRACE_VALUE_TYPE_STRING.
//
class TraceStringWithCopy {
 public:
  explicit TraceStringWithCopy(const char* str) : str_(str) {}
  const char* str() const { return str_; }

 private:
  const char* str_;
};

template <>
struct TraceValue::Helper<TraceStringWithCopy> {
  static constexpr unsigned char kType = TRACE_VALUE_TYPE_COPY_STRING;
  static inline void SetValue(TraceValue* v, const TraceStringWithCopy& value) {
    v->as_string = value.str();
  }
};

class TraceArguments;

// A small class used to store a copy of all strings from a given
// TraceArguments instance (see below). When empty, this should only
// take the size of a pointer. Otherwise, this will point to a heap
// allocated block containing a size_t value followed by all characters
// in the storage area. For most cases, this is more efficient
// than using a std::unique_ptr<std::string> or an std::vector<char>.
class BASE_EXPORT StringStorage {
 public:
  constexpr StringStorage() = default;

  explicit StringStorage(size_t alloc_size) { Reset(alloc_size); }

  ~StringStorage() {
    if (data_)
      ::free(data_);
  }

  StringStorage(StringStorage&& other) noexcept : data_(other.data_) {
    other.data_ = nullptr;
  }

  StringStorage& operator=(StringStorage&& other) noexcept {
    if (this != &other) {
      if (data_)
        ::free(data_);
      data_ = other.data_;
      other.data_ = nullptr;
    }
    return *this;
  }

  // Reset storage area to new allocation size. Existing content might not
  // be preserved. If |alloc_size| is 0, this will free the storage area
  // as well.
  void Reset(size_t alloc_size = 0);

  // Accessors.
  constexpr size_t size() const { return data_ ? data_->size : 0u; }
  constexpr const char* data() const { return data_ ? data_->chars : nullptr; }
  constexpr char* data() { return data_ ? data_->chars : nullptr; }

  constexpr const char* begin() const { return data(); }
  constexpr const char* end() const { return data() + size(); }
  inline char* begin() { return data(); }
  inline char* end() { return data() + size(); }

  // True iff storage is empty.
  constexpr bool empty() const { return size() == 0; }

  // Returns true if |ptr| is inside the storage area, false otherwise.
  // Used during unit-testing.
  constexpr bool Contains(const void* ptr) const {
    const char* char_ptr = static_cast<const char*>(ptr);
    return (char_ptr >= begin() && char_ptr < end());
  }

  // Returns true if all string pointers in |args| are contained in this
  // storage area.
  bool Contains(const TraceArguments& args) const;

  // Return an estimate of the memory overhead of this instance. This doesn't
  // count the size of |data_| itself.
  constexpr size_t EstimateTraceMemoryOverhead() const {
    return data_ ? sizeof(size_t) + data_->size : 0u;
  }

 private:
  // Heap allocated data block (variable size), made of:
  //
  //   - size: a size_t field, giving the size of the following |chars| array.
  //   - chars: an array of |size| characters, holding all zero-terminated
  //     strings referenced from a TraceArguments instance.
  struct Data {
    size_t size = 0;
    char chars[1];  // really |size| character items in storage.
  };

  // This is an owning pointer. Normally, using a std::unique_ptr<> would be
  // enough, but the compiler will then complaing about inlined constructors
  // and destructors being too complex (!), resulting in larger code for no
  // good reason.
  // This field is not a raw_ptr<> because it was filtered by the rewriter for:
  // #constexpr-ctor-field-initializer
  RAW_PTR_EXCLUSION Data* data_ = nullptr;
};

// TraceArguments models an array of kMaxSize trace-related items,
// each one of them having:
//   - a name, which is a constant char array literal.
//   - a type, as described by TRACE_VALUE_TYPE_XXX macros.
//   - a value, stored in a TraceValue union.
//
// IMPORTANT: For TRACE_VALUE_TYPE_CONVERTABLE, the value holds an owning
//            pointer to an AsConvertableToTraceFormat instance, which will
//            be destroyed with the array (or moved out of it when passed
//            to a TraceEvent instance).
//
// For TRACE_VALUE_TYPE_COPY_STRING, the value holds a const char* pointer
// whose content will be copied when creating a TraceEvent instance.
//
// IMPORTANT: Most constructors and the destructor are all inlined
// intentionally, in order to let the compiler remove un-necessary operations
// and reduce machine code.
//
class BASE_EXPORT TraceArguments {
 public:
  // Maximum number of arguments held by this structure.
  static constexpr size_t kMaxSize = 2;

  // Default constructor, no arguments.
  TraceArguments() : size_(0) {}

  // Constructor for a single argument.
  template <typename T, class = decltype(TraceValue::TypeCheck<T>::value)>
  TraceArguments(const char* arg1_name, T&& arg1_value) : size_(1) {
    types_[0] = TraceValue::TypeFor<T>::value;
    names_[0] = arg1_name;
    values_[0].Init(std::forward<T>(arg1_value));
  }

  // Constructor for two arguments.
  template <typename T1,
            typename T2,
            class = decltype(TraceValue::TypeCheck<T1>::value &&
                             TraceValue::TypeCheck<T2>::value)>
  TraceArguments(const char* arg1_name,
                 T1&& arg1_value,
                 const char* arg2_name,
                 T2&& arg2_value)
      : size_(2) {
    types_[0] = TraceValue::TypeFor<T1>::value;
    types_[1] = TraceValue::TypeFor<T2>::value;
    names_[0] = arg1_name;
    names_[1] = arg2_name;
    values_[0].Init(std::forward<T1>(arg1_value));
    values_[1].Init(std::forward<T2>(arg2_value));
  }

  // Constructor used to convert a legacy set of arguments when there
  // are no convertable values at all.
  TraceArguments(int num_args,
                 const char* const* arg_names,
                 const unsigned char* arg_types,
                 const unsigned long long* arg_values);

  // Constructor used to convert legacy set of arguments, where the
  // convertable values are also provided by an array of CONVERTABLE_TYPE.
  template <typename CONVERTABLE_TYPE>
  TraceArguments(int num_args,
                 const char* const* arg_names,
                 const unsigned char* arg_types,
                 const unsigned long long* arg_values,
                 CONVERTABLE_TYPE* arg_convertables) {
    static int max_args = static_cast<int>(kMaxSize);
    if (num_args > max_args)
      num_args = max_args;
    size_ = static_cast<unsigned char>(num_args);
    for (size_t n = 0; n < size_; ++n) {
      types_[n] = arg_types[n];
      names_[n] = arg_names[n];
      if (arg_types[n] == TRACE_VALUE_TYPE_CONVERTABLE) {
        values_[n].Init(
            std::forward<CONVERTABLE_TYPE>(std::move(arg_convertables[n])));
      } else {
        values_[n].as_uint = arg_values[n];
      }
    }
  }

  // Destructor. NOTE: Intentionally inlined (see note above).
  ~TraceArguments() {
    for (size_t n = 0; n < size_; ++n) {
      if (types_[n] == TRACE_VALUE_TYPE_CONVERTABLE)
        delete values_[n].as_convertable;
      if (types_[n] == TRACE_VALUE_TYPE_PROTO)
        delete values_[n].as_proto;
    }
  }

  // Disallow copy operations.
  TraceArguments(const TraceArguments&) = delete;
  TraceArguments& operator=(const TraceArguments&) = delete;

  // Allow move operations.
  TraceArguments(TraceArguments&& other) noexcept {
    ::memcpy(this, &other, sizeof(*this));
    // All owning pointers were copied to |this|. Setting |other.size_| will
    // mask the pointer values still in |other|.
    other.size_ = 0;
  }

  TraceArguments& operator=(TraceArguments&&) noexcept;

  // Accessors
  size_t size() const { return size_; }
  const unsigned char* types() const { return types_; }
  const char* const* names() const { return names_; }
  const TraceValue* values() const { return values_; }

  // Reset to empty arguments list.
  void Reset();

  // Use |storage| to copy all copyable strings.
  // If |copy_all_strings| is false, then only the TRACE_VALUE_TYPE_COPY_STRING
  // values will be copied into storage. If it is true, then argument names are
  // also copied to storage, as well as the strings pointed to by
  // |*extra_string1| and |*extra_string2|.
  // NOTE: If there are no strings to copy, |*storage| is left untouched.
  void CopyStringsTo(StringStorage* storage,
                     bool copy_all_strings,
                     const char** extra_string1,
                     const char** extra_string2);

  // Append debug string representation to |*out|.
  void AppendDebugString(std::string* out);

 private:
  unsigned char size_;
  unsigned char types_[kMaxSize];
  const char* names_[kMaxSize];
  TraceValue values_[kMaxSize];
};

}  // namespace trace_event
}  // namespace base

#endif  // BASE_TRACE_EVENT_TRACE_ARGUMENTS_H_