1 // Copyright 2020 the V8 project authors. All rights reserved. 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 V8_HEAP_CPPGC_TRACE_EVENT_H_ 6 #define V8_HEAP_CPPGC_TRACE_EVENT_H_ 7 8 #if !CPPGC_IS_STANDALONE 9 #include "src/tracing/trace-event.h" 10 using ConvertableToTraceFormat = v8::ConvertableToTraceFormat; 11 #else 12 // This is a subset of stc/tracing/trace-event.h required to support 13 // tracing in the cppgc standalone library using TracingController. 14 15 #include "base/trace_event/common/trace_event_common.h" 16 #include "include/cppgc/platform.h" 17 #include "src/base/atomicops.h" 18 #include "src/base/macros.h" 19 20 // This header file defines implementation details of how the trace macros in 21 // trace_event_common.h collect and store trace events. Anything not 22 // implementation-specific should go in trace_macros_common.h instead of here. 23 24 // The pointer returned from GetCategoryGroupEnabled() points to a 25 // value with zero or more of the following bits. Used in this class only. 26 // The TRACE_EVENT macros should only use the value as a bool. 27 // These values must be in sync with macro values in trace_log.h in 28 // chromium. 29 enum CategoryGroupEnabledFlags { 30 // Category group enabled for the recording mode. 31 kEnabledForRecording_CategoryGroupEnabledFlags = 1 << 0, 32 // Category group enabled by SetEventCallbackEnabled(). 33 kEnabledForEventCallback_CategoryGroupEnabledFlags = 1 << 2, 34 }; 35 36 #define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE() \ 37 TRACE_EVENT_API_LOAD_CATEGORY_GROUP_ENABLED() & \ 38 (kEnabledForRecording_CategoryGroupEnabledFlags | \ 39 kEnabledForEventCallback_CategoryGroupEnabledFlags) 40 41 //////////////////////////////////////////////////////////////////////////////// 42 // Implementation specific tracing API definitions. 43 44 // Get a pointer to the enabled state of the given trace category. Only 45 // long-lived literal strings should be given as the category group. The 46 // returned pointer can be held permanently in a local static for example. If 47 // the unsigned char is non-zero, tracing is enabled. If tracing is enabled, 48 // TRACE_EVENT_API_ADD_TRACE_EVENT can be called. It's OK if tracing is disabled 49 // between the load of the tracing state and the call to 50 // TRACE_EVENT_API_ADD_TRACE_EVENT, because this flag only provides an early out 51 // for best performance when tracing is disabled. 52 // const uint8_t* 53 // TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(const char* category_group) 54 #define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED \ 55 platform->GetTracingController()->GetCategoryGroupEnabled 56 57 // Add a trace event to the platform tracing system. 58 // uint64_t TRACE_EVENT_API_ADD_TRACE_EVENT( 59 // char phase, 60 // const uint8_t* category_group_enabled, 61 // const char* name, 62 // const char* scope, 63 // uint64_t id, 64 // uint64_t bind_id, 65 // int num_args, 66 // const char** arg_names, 67 // const uint8_t* arg_types, 68 // const uint64_t* arg_values, 69 // unsigned int flags) 70 #define TRACE_EVENT_API_ADD_TRACE_EVENT cppgc::internal::AddTraceEventImpl 71 72 // Defines atomic operations used internally by the tracing system. 73 #define TRACE_EVENT_API_ATOMIC_WORD v8::base::AtomicWord 74 #define TRACE_EVENT_API_ATOMIC_LOAD(var) v8::base::Relaxed_Load(&(var)) 75 #define TRACE_EVENT_API_ATOMIC_STORE(var, value) \ 76 v8::base::Relaxed_Store(&(var), (value)) 77 #define TRACE_EVENT_API_LOAD_CATEGORY_GROUP_ENABLED() \ 78 v8::base::Relaxed_Load(reinterpret_cast<const v8::base::Atomic8*>( \ 79 INTERNAL_TRACE_EVENT_UID(category_group_enabled))) 80 81 //////////////////////////////////////////////////////////////////////////////// 82 83 // Implementation detail: trace event macros create temporary variables 84 // to keep instrumentation overhead low. These macros give each temporary 85 // variable a unique name based on the line number to prevent name collisions. 86 #define INTERNAL_TRACE_EVENT_UID3(a, b) cppgc_trace_event_unique_##a##b 87 #define INTERNAL_TRACE_EVENT_UID2(a, b) INTERNAL_TRACE_EVENT_UID3(a, b) 88 #define INTERNAL_TRACE_EVENT_UID(name_prefix) \ 89 INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__) 90 91 // Implementation detail: internal macro to create static category. 92 // No barriers are needed, because this code is designed to operate safely 93 // even when the unsigned char* points to garbage data (which may be the case 94 // on processors without cache coherency). 95 #define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( \ 96 category_group, atomic, category_group_enabled) \ 97 category_group_enabled = \ 98 reinterpret_cast<const uint8_t*>(TRACE_EVENT_API_ATOMIC_LOAD(atomic)); \ 99 if (!category_group_enabled) { \ 100 category_group_enabled = \ 101 TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); \ 102 TRACE_EVENT_API_ATOMIC_STORE( \ 103 atomic, reinterpret_cast<TRACE_EVENT_API_ATOMIC_WORD>( \ 104 category_group_enabled)); \ 105 } 106 107 #define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group) \ 108 static TRACE_EVENT_API_ATOMIC_WORD INTERNAL_TRACE_EVENT_UID(atomic) = 0; \ 109 const uint8_t* INTERNAL_TRACE_EVENT_UID(category_group_enabled); \ 110 INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( \ 111 category_group, INTERNAL_TRACE_EVENT_UID(atomic), \ 112 INTERNAL_TRACE_EVENT_UID(category_group_enabled)); 113 114 // Implementation detail: internal macro to create static category and add 115 // event if the category is enabled. 116 #define INTERNAL_TRACE_EVENT_ADD(phase, category_group, name, flags, ...) \ 117 DCHECK_NOT_NULL(name); \ 118 do { \ 119 cppgc::Platform* platform = stats_collector_->platform_; \ 120 INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \ 121 if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \ 122 cppgc::internal::AddTraceEvent( \ 123 phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \ 124 nullptr /* scope */, 0 /* id */, 0 /* bind_id */, flags, platform, \ 125 ##__VA_ARGS__); \ 126 } \ 127 } while (false) 128 129 namespace cppgc { 130 namespace internal { 131 132 using ConvertableToTraceFormat = v8::ConvertableToTraceFormat; 133 134 class TraceEventHelper { 135 public: 136 V8_EXPORT_PRIVATE static TracingController* GetTracingController(); 137 }; 138 139 static V8_INLINE uint64_t AddTraceEventImpl( 140 char phase, const uint8_t* category_group_enabled, const char* name, 141 const char* scope, uint64_t id, uint64_t bind_id, int32_t num_args, 142 const char** arg_names, const uint8_t* arg_types, 143 const uint64_t* arg_values, unsigned int flags, Platform* platform) { 144 std::unique_ptr<ConvertableToTraceFormat> arg_convertables[2]; 145 if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) { 146 arg_convertables[0].reset(reinterpret_cast<ConvertableToTraceFormat*>( 147 static_cast<intptr_t>(arg_values[0]))); 148 } 149 if (num_args > 1 && arg_types[1] == TRACE_VALUE_TYPE_CONVERTABLE) { 150 arg_convertables[1].reset(reinterpret_cast<ConvertableToTraceFormat*>( 151 static_cast<intptr_t>(arg_values[1]))); 152 } 153 DCHECK_LE(num_args, 2); 154 TracingController* controller = platform->GetTracingController(); 155 return controller->AddTraceEvent(phase, category_group_enabled, name, scope, 156 id, bind_id, num_args, arg_names, arg_types, 157 arg_values, arg_convertables, flags); 158 } 159 160 // Define SetTraceValue for each allowed type. It stores the type and value 161 // in the return arguments. This allows this API to avoid declaring any 162 // structures so that it is portable to third_party libraries. 163 // This is the base implementation for integer types (including bool) and enums. 164 template <typename T> 165 static V8_INLINE typename std::enable_if< 166 std::is_integral<T>::value || std::is_enum<T>::value, void>::type 167 SetTraceValue(T arg, unsigned char* type, uint64_t* value) { 168 *type = std::is_same<T, bool>::value 169 ? TRACE_VALUE_TYPE_BOOL 170 : std::is_signed<T>::value ? TRACE_VALUE_TYPE_INT 171 : TRACE_VALUE_TYPE_UINT; 172 *value = static_cast<uint64_t>(arg); 173 } 174 175 #define INTERNAL_DECLARE_SET_TRACE_VALUE(actual_type, value_type_id) \ 176 static V8_INLINE void SetTraceValue(actual_type arg, unsigned char* type, \ 177 uint64_t* value) { \ 178 *type = value_type_id; \ 179 *value = 0; \ 180 STATIC_ASSERT(sizeof(arg) <= sizeof(*value)); \ 181 memcpy(value, &arg, sizeof(arg)); \ 182 } 183 INTERNAL_DECLARE_SET_TRACE_VALUE(double, TRACE_VALUE_TYPE_DOUBLE) 184 INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, TRACE_VALUE_TYPE_STRING) 185 #undef INTERNAL_DECLARE_SET_TRACE_VALUE 186 187 // These AddTraceEvent template functions are defined here instead of in 188 // the macro, because the arg_values could be temporary objects, such as 189 // std::string. In order to store pointers to the internal c_str and pass 190 // through to the tracing API, the arg_values must live throughout these 191 // procedures. 192 193 static V8_INLINE uint64_t AddTraceEvent(char phase, 194 const uint8_t* category_group_enabled, 195 const char* name, const char* scope, 196 uint64_t id, uint64_t bind_id, 197 unsigned int flags, 198 Platform* platform) { 199 return TRACE_EVENT_API_ADD_TRACE_EVENT( 200 phase, category_group_enabled, name, scope, id, bind_id, 0 /* num_args */, 201 nullptr, nullptr, nullptr, flags, platform); 202 } 203 204 template <class ARG1_TYPE> 205 static V8_INLINE uint64_t AddTraceEvent( 206 char phase, const uint8_t* category_group_enabled, const char* name, 207 const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, 208 Platform* platform, const char* arg1_name, ARG1_TYPE&& arg1_val) { 209 const int num_args = 1; 210 uint8_t arg_type; 211 uint64_t arg_value; 212 SetTraceValue(std::forward<ARG1_TYPE>(arg1_val), &arg_type, &arg_value); 213 return TRACE_EVENT_API_ADD_TRACE_EVENT( 214 phase, category_group_enabled, name, scope, id, bind_id, num_args, 215 &arg1_name, &arg_type, &arg_value, flags, platform); 216 } 217 218 template <class ARG1_TYPE, class ARG2_TYPE> 219 static V8_INLINE uint64_t AddTraceEvent( 220 char phase, const uint8_t* category_group_enabled, const char* name, 221 const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags, 222 Platform* platform, const char* arg1_name, ARG1_TYPE&& arg1_val, 223 const char* arg2_name, ARG2_TYPE&& arg2_val) { 224 const int num_args = 2; 225 const char* arg_names[2] = {arg1_name, arg2_name}; 226 unsigned char arg_types[2]; 227 uint64_t arg_values[2]; 228 SetTraceValue(std::forward<ARG1_TYPE>(arg1_val), &arg_types[0], 229 &arg_values[0]); 230 SetTraceValue(std::forward<ARG2_TYPE>(arg2_val), &arg_types[1], 231 &arg_values[1]); 232 return TRACE_EVENT_API_ADD_TRACE_EVENT( 233 phase, category_group_enabled, name, scope, id, bind_id, num_args, 234 arg_names, arg_types, arg_values, flags, platform); 235 } 236 237 } // namespace internal 238 } // namespace cppgc 239 240 #endif // !CPPGC_IS_STANDALONE 241 242 #endif // V8_HEAP_CPPGC_TRACE_EVENT_H_ 243