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 public: 271 // TraceValue::Helper is used to provide information about initialization 272 // value types and an initialization function. It is a struct that should 273 // provide the following for supported initialization value types: 274 // 275 // - kType: is a static TRACE_VALUE_TYPE_XXX constant. 276 // 277 // - SetValue(TraceValue*, T): is a static inline method that sets 278 // TraceValue value from a given T value. Second parameter type 279 // can also be const T& or T&& to restrict uses. 280 // 281 // IMPORTANT: The type T must be std::decay_t<Q>, where Q is the real C++ 282 // argument type. I.e. you should not have to deal with reference types 283 // in your specialization. 284 // 285 // Specializations are defined for integers, enums, floating point, pointers, 286 // constant C string literals and pointers, std::string, time values below. 287 // 288 // Specializations for custom types are possible provided that there exists 289 // a corresponding Helper specialization, for example: 290 // 291 // template <> 292 // struct base::trace_event::TraceValue::Helper<Foo> { 293 // static constexpr unsigned char kTypes = TRACE_VALUE_TYPE_COPY_STRING; 294 // static inline void SetValue(TraceValue* v, const Foo& value) { 295 // v->as_string = value.c_str(); 296 // } 297 // }; 298 // 299 // Will allow code like: 300 // 301 // Foo foo = ...; 302 // auto v = TraceValue::Make(foo); 303 // 304 // Or even: 305 // Foo foo = ...; 306 // TraceArguments args("foo_arg1", foo); 307 // 308 template <typename T, class = void> 309 struct Helper {}; 310 311 template <typename T> 312 static constexpr bool HasHelperSupport = 313 requires { TraceValue::Helper<std::decay_t<T>>::kType; }; 314 315 // TraceValue::TypeFor<T>::value returns the TRACE_VALUE_TYPE_XXX 316 // corresponding to initialization values of type T. 317 template <typename T, class = void> 318 struct TypeFor; 319 320 template <typename T> 321 struct TypeFor<T, std::enable_if_t<HasHelperSupport<T>>> { 322 using ValueType = std::decay_t<T>; 323 static const unsigned char value = Helper<ValueType>::kType; 324 }; 325 template <typename T> 326 struct TypeFor<T, 327 std::enable_if_t<!HasHelperSupport<T> && 328 perfetto::internal::has_traced_value_support< 329 std::decay_t<T>>::value>> { 330 static const unsigned char value = TRACE_VALUE_TYPE_PROTO; 331 }; 332 333 // TraceValue::TypeCheck<T>::value is only defined iff T can be used to 334 // initialize a TraceValue instance. This is useful to restrict template 335 // instantiation to only the appropriate type (see TraceArguments 336 // constructors below). 337 template < 338 typename T, 339 class = std::enable_if_t< 340 HasHelperSupport<T> || 341 perfetto::internal::has_traced_value_support<std::decay_t<T>>::value>> 342 struct TypeCheck { 343 static const bool value = true; 344 }; 345 346 // There is no constructor to keep this structure POD intentionally. 347 // This avoids un-needed initialization when only 0 or 1 arguments are 348 // used to construct a TraceArguments instance. Use Init() instead to 349 // perform explicit initialization from a given C++ value. 350 351 // Initialize TraceValue instance from a C++ trace value. 352 // This relies on the proper specialization of TraceValue::Helper<> 353 // described below. Usage is simply: 354 // 355 // TraceValue v; 356 // v.Init(<value>); 357 // 358 // NOTE: For ConvertableToTraceFormat values, see the notes above. 359 template <class T> 360 std::enable_if_t<HasHelperSupport<T>> Init(T&& value) { 361 using ValueType = std::decay_t<T>; 362 Helper<ValueType>::SetValue(this, std::forward<T>(value)); 363 } 364 365 template <class T> 366 std::enable_if_t< 367 !HasHelperSupport<T> && 368 perfetto::internal::has_traced_value_support<std::decay_t<T>>::value> 369 Init(T&& value) { 370 as_proto = new protozero::HeapBuffered< 371 perfetto::protos::pbzero::DebugAnnotation>(); 372 perfetto::WriteIntoTracedValue( 373 perfetto::internal::CreateTracedValueFromProto(as_proto->get()), 374 std::forward<T>(value)); 375 } 376 }; 377 378 // TraceValue::Helper for integers and enums. 379 template <typename T> 380 struct TraceValue:: 381 Helper<T, std::enable_if_t<std::is_integral_v<T> || std::is_enum_v<T>>> { 382 static constexpr unsigned char kType = 383 std::is_signed_v<T> ? TRACE_VALUE_TYPE_INT : TRACE_VALUE_TYPE_UINT; 384 static inline void SetValue(TraceValue* v, T value) { 385 v->as_uint = static_cast<unsigned long long>(value); 386 } 387 }; 388 389 // TraceValue::Helper for floating-point types 390 template <typename T> 391 struct TraceValue::Helper<T, std::enable_if_t<std::is_floating_point_v<T>>> { 392 static constexpr unsigned char kType = TRACE_VALUE_TYPE_DOUBLE; 393 static inline void SetValue(TraceValue* v, T value) { v->as_double = value; } 394 }; 395 396 // TraceValue::Helper for bool. 397 template <> 398 struct TraceValue::Helper<bool> { 399 static constexpr unsigned char kType = TRACE_VALUE_TYPE_BOOL; 400 static inline void SetValue(TraceValue* v, bool value) { v->as_bool = value; } 401 }; 402 403 // TraceValue::Helper for generic pointer types. 404 template <> 405 struct TraceValue::Helper<const void*> { 406 static constexpr unsigned char kType = TRACE_VALUE_TYPE_POINTER; 407 static inline void SetValue(TraceValue* v, const void* value) { 408 v->as_pointer = value; 409 } 410 }; 411 412 template <> 413 struct TraceValue::Helper<void*> { 414 static constexpr unsigned char kType = TRACE_VALUE_TYPE_POINTER; 415 static inline void SetValue(TraceValue* v, void* value) { 416 v->as_pointer = value; 417 } 418 }; 419 420 // TraceValue::Helper for raw persistent C strings. 421 template <> 422 struct TraceValue::Helper<const char*> { 423 static constexpr unsigned char kType = TRACE_VALUE_TYPE_STRING; 424 static inline void SetValue(TraceValue* v, const char* value) { 425 v->as_string = value; 426 } 427 }; 428 429 // TraceValue::Helper for std::string values. 430 template <> 431 struct TraceValue::Helper<std::string> { 432 static constexpr unsigned char kType = TRACE_VALUE_TYPE_COPY_STRING; 433 static inline void SetValue(TraceValue* v, const std::string& value) { 434 v->as_string = value.c_str(); 435 } 436 }; 437 438 // Special case for scoped pointers to convertables to trace format. 439 // |CONVERTABLE_TYPE| must be a type whose pointers can be converted to a 440 // ConvertableToTraceFormat* pointer as well (e.g. a derived class). 441 // IMPORTANT: This takes an std::unique_ptr<CONVERTABLE_TYPE> value, and takes 442 // ownership of the pointed object! 443 template <typename CONVERTABLE_TYPE> 444 struct TraceValue::Helper< 445 std::unique_ptr<CONVERTABLE_TYPE>, 446 std::enable_if_t< 447 std::is_convertible_v<CONVERTABLE_TYPE*, ConvertableToTraceFormat*>>> { 448 static constexpr unsigned char kType = TRACE_VALUE_TYPE_CONVERTABLE; 449 static inline void SetValue(TraceValue* v, 450 std::unique_ptr<CONVERTABLE_TYPE> value) { 451 v->as_convertable = value.release(); 452 } 453 }; 454 455 // Specialization for time-based values like base::Time, which provide a 456 // a ToInternalValue() method. 457 template <typename T> 458 struct TraceValue::Helper< 459 T, 460 std::enable_if_t<std::is_same_v<T, base::Time> || 461 std::is_same_v<T, base::TimeTicks> || 462 std::is_same_v<T, base::ThreadTicks>>> { 463 static constexpr unsigned char kType = TRACE_VALUE_TYPE_INT; 464 static inline void SetValue(TraceValue* v, const T& value) { 465 v->as_int = value.ToInternalValue(); 466 } 467 }; 468 469 // Simple container for const char* that should be copied instead of retained. 470 // The goal is to indicate that the C string is copyable, unlike the default 471 // Init(const char*) implementation. Usage is: 472 // 473 // const char* str = ...; 474 // v.Init(TraceStringWithCopy(str)); 475 // 476 // Which will mark the string as TRACE_VALUE_TYPE_COPY_STRING, instead of 477 // TRACE_VALUE_TYPE_STRING. 478 // 479 class TraceStringWithCopy { 480 public: 481 explicit TraceStringWithCopy(const char* str) : str_(str) {} 482 const char* str() const { return str_; } 483 484 private: 485 const char* str_; 486 }; 487 488 template <> 489 struct TraceValue::Helper<TraceStringWithCopy> { 490 static constexpr unsigned char kType = TRACE_VALUE_TYPE_COPY_STRING; 491 static inline void SetValue(TraceValue* v, const TraceStringWithCopy& value) { 492 v->as_string = value.str(); 493 } 494 }; 495 496 class TraceArguments; 497 498 // A small class used to store a copy of all strings from a given 499 // TraceArguments instance (see below). When empty, this should only 500 // take the size of a pointer. Otherwise, this will point to a heap 501 // allocated block containing a size_t value followed by all characters 502 // in the storage area. For most cases, this is more efficient 503 // than using a std::unique_ptr<std::string> or an std::vector<char>. 504 class BASE_EXPORT StringStorage { 505 public: 506 constexpr StringStorage() = default; 507 508 explicit StringStorage(size_t alloc_size) { Reset(alloc_size); } 509 510 ~StringStorage() { 511 if (data_) 512 ::free(data_); 513 } 514 515 StringStorage(StringStorage&& other) noexcept : data_(other.data_) { 516 other.data_ = nullptr; 517 } 518 519 StringStorage& operator=(StringStorage&& other) noexcept { 520 if (this != &other) { 521 if (data_) 522 ::free(data_); 523 data_ = other.data_; 524 other.data_ = nullptr; 525 } 526 return *this; 527 } 528 529 // Reset storage area to new allocation size. Existing content might not 530 // be preserved. If |alloc_size| is 0, this will free the storage area 531 // as well. 532 void Reset(size_t alloc_size = 0); 533 534 // Accessors. 535 constexpr size_t size() const { return data_ ? data_->size : 0u; } 536 constexpr const char* data() const { return data_ ? data_->chars : nullptr; } 537 constexpr char* data() { return data_ ? data_->chars : nullptr; } 538 539 constexpr const char* begin() const { return data(); } 540 constexpr const char* end() const { return data() + size(); } 541 inline char* begin() { return data(); } 542 inline char* end() { return data() + size(); } 543 544 // True iff storage is empty. 545 constexpr bool empty() const { return size() == 0; } 546 547 // Returns true if |ptr| is inside the storage area, false otherwise. 548 // Used during unit-testing. 549 constexpr bool Contains(const void* ptr) const { 550 const char* char_ptr = static_cast<const char*>(ptr); 551 return (char_ptr >= begin() && char_ptr < end()); 552 } 553 554 // Returns true if all string pointers in |args| are contained in this 555 // storage area. 556 bool Contains(const TraceArguments& args) const; 557 558 // Return an estimate of the memory overhead of this instance. This doesn't 559 // count the size of |data_| itself. 560 constexpr size_t EstimateTraceMemoryOverhead() const { 561 return data_ ? sizeof(size_t) + data_->size : 0u; 562 } 563 564 private: 565 // Heap allocated data block (variable size), made of: 566 // 567 // - size: a size_t field, giving the size of the following |chars| array. 568 // - chars: an array of |size| characters, holding all zero-terminated 569 // strings referenced from a TraceArguments instance. 570 struct Data { 571 size_t size = 0; 572 char chars[1]; // really |size| character items in storage. 573 }; 574 575 // This is an owning pointer. Normally, using a std::unique_ptr<> would be 576 // enough, but the compiler will then complaing about inlined constructors 577 // and destructors being too complex (!), resulting in larger code for no 578 // good reason. 579 // RAW_PTR_EXCLUSION: As above, inlining bloats code for no good reason. 580 RAW_PTR_EXCLUSION Data* data_ = nullptr; 581 }; 582 583 // TraceArguments models an array of kMaxSize trace-related items, 584 // each one of them having: 585 // - a name, which is a constant char array literal. 586 // - a type, as described by TRACE_VALUE_TYPE_XXX macros. 587 // - a value, stored in a TraceValue union. 588 // 589 // IMPORTANT: For TRACE_VALUE_TYPE_CONVERTABLE, the value holds an owning 590 // pointer to an AsConvertableToTraceFormat instance, which will 591 // be destroyed with the array (or moved out of it when passed 592 // to a TraceEvent instance). 593 // 594 // For TRACE_VALUE_TYPE_COPY_STRING, the value holds a const char* pointer 595 // whose content will be copied when creating a TraceEvent instance. 596 // 597 // IMPORTANT: Most constructors and the destructor are all inlined 598 // intentionally, in order to let the compiler remove un-necessary operations 599 // and reduce machine code. 600 // 601 class BASE_EXPORT TraceArguments { 602 public: 603 // Maximum number of arguments held by this structure. 604 static constexpr size_t kMaxSize = 2; 605 606 // Default constructor, no arguments. 607 TraceArguments() : size_(0) {} 608 609 // Constructor for a single argument. 610 template <typename T, class = decltype(TraceValue::TypeCheck<T>::value)> 611 TraceArguments(const char* arg1_name, T&& arg1_value) : size_(1) { 612 types_[0] = TraceValue::TypeFor<T>::value; 613 names_[0] = arg1_name; 614 values_[0].Init(std::forward<T>(arg1_value)); 615 } 616 617 // Constructor for two arguments. 618 template <typename T1, 619 typename T2, 620 class = decltype(TraceValue::TypeCheck<T1>::value && 621 TraceValue::TypeCheck<T2>::value)> 622 TraceArguments(const char* arg1_name, 623 T1&& arg1_value, 624 const char* arg2_name, 625 T2&& arg2_value) 626 : size_(2) { 627 types_[0] = TraceValue::TypeFor<T1>::value; 628 types_[1] = TraceValue::TypeFor<T2>::value; 629 names_[0] = arg1_name; 630 names_[1] = arg2_name; 631 values_[0].Init(std::forward<T1>(arg1_value)); 632 values_[1].Init(std::forward<T2>(arg2_value)); 633 } 634 635 // Constructor used to convert a legacy set of arguments when there 636 // are no convertable values at all. 637 TraceArguments(int num_args, 638 const char* const* arg_names, 639 const unsigned char* arg_types, 640 const unsigned long long* arg_values); 641 642 // Constructor used to convert legacy set of arguments, where the 643 // convertable values are also provided by an array of CONVERTABLE_TYPE. 644 template <typename CONVERTABLE_TYPE> 645 TraceArguments(int num_args, 646 const char* const* arg_names, 647 const unsigned char* arg_types, 648 const unsigned long long* arg_values, 649 CONVERTABLE_TYPE* arg_convertables) { 650 static int max_args = static_cast<int>(kMaxSize); 651 if (num_args > max_args) 652 num_args = max_args; 653 size_ = static_cast<unsigned char>(num_args); 654 for (size_t n = 0; n < size_; ++n) { 655 types_[n] = arg_types[n]; 656 names_[n] = arg_names[n]; 657 if (arg_types[n] == TRACE_VALUE_TYPE_CONVERTABLE) { 658 values_[n].Init( 659 std::forward<CONVERTABLE_TYPE>(std::move(arg_convertables[n]))); 660 } else { 661 values_[n].as_uint = arg_values[n]; 662 } 663 } 664 } 665 666 // Destructor. NOTE: Intentionally inlined (see note above). 667 ~TraceArguments() { 668 for (size_t n = 0; n < size_; ++n) { 669 if (types_[n] == TRACE_VALUE_TYPE_CONVERTABLE) 670 delete values_[n].as_convertable; 671 if (types_[n] == TRACE_VALUE_TYPE_PROTO) 672 delete values_[n].as_proto; 673 } 674 } 675 676 // Disallow copy operations. 677 TraceArguments(const TraceArguments&) = delete; 678 TraceArguments& operator=(const TraceArguments&) = delete; 679 680 // Allow move operations. 681 TraceArguments(TraceArguments&& other) noexcept { 682 ::memcpy(this, &other, sizeof(*this)); 683 // All owning pointers were copied to |this|. Setting |other.size_| will 684 // mask the pointer values still in |other|. 685 other.size_ = 0; 686 } 687 688 TraceArguments& operator=(TraceArguments&&) noexcept; 689 690 // Accessors 691 size_t size() const { return size_; } 692 const unsigned char* types() const { return types_; } 693 const char* const* names() const { return names_; } 694 const TraceValue* values() const { return values_; } 695 696 // Reset to empty arguments list. 697 void Reset(); 698 699 // Use |storage| to copy all copyable strings. 700 // If |copy_all_strings| is false, then only the TRACE_VALUE_TYPE_COPY_STRING 701 // values will be copied into storage. If it is true, then argument names are 702 // also copied to storage, as well as the strings pointed to by 703 // |*extra_string1| and |*extra_string2|. 704 // NOTE: If there are no strings to copy, |*storage| is left untouched. 705 void CopyStringsTo(StringStorage* storage, 706 bool copy_all_strings, 707 const char** extra_string1, 708 const char** extra_string2); 709 710 // Append debug string representation to |*out|. 711 void AppendDebugString(std::string* out); 712 713 private: 714 unsigned char size_; 715 unsigned char types_[kMaxSize]; 716 const char* names_[kMaxSize]; 717 TraceValue values_[kMaxSize]; 718 }; 719 720 } // namespace trace_event 721 } // namespace base 722 723 #endif // BASE_TRACE_EVENT_TRACE_ARGUMENTS_H_ 724