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