1 // Copyright 2016 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 #include <stdio.h> 6 #include <string.h> 7 8 #include "include/libplatform/v8-tracing.h" 9 10 #include "src/base/platform/mutex.h" 11 12 namespace v8 { 13 namespace platform { 14 namespace tracing { 15 16 #define MAX_CATEGORY_GROUPS 200 17 18 // Parallel arrays g_category_groups and g_category_group_enabled are separate 19 // so that a pointer to a member of g_category_group_enabled can be easily 20 // converted to an index into g_category_groups. This allows macros to deal 21 // only with char enabled pointers from g_category_group_enabled, and we can 22 // convert internally to determine the category name from the char enabled 23 // pointer. 24 const char* g_category_groups[MAX_CATEGORY_GROUPS] = { 25 "toplevel", "tracing already shutdown", 26 "tracing categories exhausted; must increase MAX_CATEGORY_GROUPS", 27 "__metadata"}; 28 29 // The enabled flag is char instead of bool so that the API can be used from C. 30 unsigned char g_category_group_enabled[MAX_CATEGORY_GROUPS] = {0}; 31 // Indexes here have to match the g_category_groups array indexes above. 32 const int g_category_already_shutdown = 1; 33 const int g_category_categories_exhausted = 2; 34 // Metadata category not used in V8. 35 // const int g_category_metadata = 3; 36 const int g_num_builtin_categories = 4; 37 38 // Skip default categories. 39 v8::base::AtomicWord g_category_index = g_num_builtin_categories; 40 41 TracingController::TracingController() {} 42 43 TracingController::~TracingController() {} 44 45 void TracingController::Initialize(TraceBuffer* trace_buffer) { 46 trace_buffer_.reset(trace_buffer); 47 mutex_.reset(new base::Mutex()); 48 } 49 50 uint64_t TracingController::AddTraceEvent( 51 char phase, const uint8_t* category_enabled_flag, const char* name, 52 const char* scope, uint64_t id, uint64_t bind_id, int num_args, 53 const char** arg_names, const uint8_t* arg_types, 54 const uint64_t* arg_values, 55 std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables, 56 unsigned int flags) { 57 uint64_t handle; 58 TraceObject* trace_object = trace_buffer_->AddTraceEvent(&handle); 59 if (trace_object) { 60 trace_object->Initialize(phase, category_enabled_flag, name, scope, id, 61 bind_id, num_args, arg_names, arg_types, 62 arg_values, arg_convertables, flags); 63 } 64 return handle; 65 } 66 67 void TracingController::UpdateTraceEventDuration( 68 const uint8_t* category_enabled_flag, const char* name, uint64_t handle) { 69 TraceObject* trace_object = trace_buffer_->GetEventByHandle(handle); 70 if (!trace_object) return; 71 trace_object->UpdateDuration(); 72 } 73 74 const uint8_t* TracingController::GetCategoryGroupEnabled( 75 const char* category_group) { 76 if (!trace_buffer_) { 77 DCHECK(!g_category_group_enabled[g_category_already_shutdown]); 78 return &g_category_group_enabled[g_category_already_shutdown]; 79 } 80 return GetCategoryGroupEnabledInternal(category_group); 81 } 82 83 const char* TracingController::GetCategoryGroupName( 84 const uint8_t* category_group_enabled) { 85 // Calculate the index of the category group by finding 86 // category_group_enabled in g_category_group_enabled array. 87 uintptr_t category_begin = 88 reinterpret_cast<uintptr_t>(g_category_group_enabled); 89 uintptr_t category_ptr = reinterpret_cast<uintptr_t>(category_group_enabled); 90 // Check for out of bounds category pointers. 91 DCHECK(category_ptr >= category_begin && 92 category_ptr < reinterpret_cast<uintptr_t>(g_category_group_enabled + 93 MAX_CATEGORY_GROUPS)); 94 uintptr_t category_index = 95 (category_ptr - category_begin) / sizeof(g_category_group_enabled[0]); 96 return g_category_groups[category_index]; 97 } 98 99 void TracingController::StartTracing(TraceConfig* trace_config) { 100 trace_config_.reset(trace_config); 101 std::unordered_set<Platform::TraceStateObserver*> observers_copy; 102 { 103 base::LockGuard<base::Mutex> lock(mutex_.get()); 104 mode_ = RECORDING_MODE; 105 UpdateCategoryGroupEnabledFlags(); 106 observers_copy = observers_; 107 } 108 for (auto o : observers_copy) { 109 o->OnTraceEnabled(); 110 } 111 } 112 113 void TracingController::StopTracing() { 114 mode_ = DISABLED; 115 UpdateCategoryGroupEnabledFlags(); 116 std::unordered_set<Platform::TraceStateObserver*> observers_copy; 117 { 118 base::LockGuard<base::Mutex> lock(mutex_.get()); 119 observers_copy = observers_; 120 } 121 for (auto o : observers_copy) { 122 o->OnTraceDisabled(); 123 } 124 trace_buffer_->Flush(); 125 } 126 127 void TracingController::UpdateCategoryGroupEnabledFlag(size_t category_index) { 128 unsigned char enabled_flag = 0; 129 const char* category_group = g_category_groups[category_index]; 130 if (mode_ == RECORDING_MODE && 131 trace_config_->IsCategoryGroupEnabled(category_group)) { 132 enabled_flag |= ENABLED_FOR_RECORDING; 133 } 134 135 // TODO(fmeawad): EventCallback and ETW modes are not yet supported in V8. 136 // TODO(primiano): this is a temporary workaround for catapult:#2341, 137 // to guarantee that metadata events are always added even if the category 138 // filter is "-*". See crbug.com/618054 for more details and long-term fix. 139 if (mode_ == RECORDING_MODE && !strcmp(category_group, "__metadata")) { 140 enabled_flag |= ENABLED_FOR_RECORDING; 141 } 142 143 g_category_group_enabled[category_index] = enabled_flag; 144 } 145 146 void TracingController::UpdateCategoryGroupEnabledFlags() { 147 size_t category_index = base::NoBarrier_Load(&g_category_index); 148 for (size_t i = 0; i < category_index; i++) UpdateCategoryGroupEnabledFlag(i); 149 } 150 151 const uint8_t* TracingController::GetCategoryGroupEnabledInternal( 152 const char* category_group) { 153 // Check that category groups does not contain double quote 154 DCHECK(!strchr(category_group, '"')); 155 156 // The g_category_groups is append only, avoid using a lock for the fast path. 157 size_t current_category_index = v8::base::Acquire_Load(&g_category_index); 158 159 // Search for pre-existing category group. 160 for (size_t i = 0; i < current_category_index; ++i) { 161 if (strcmp(g_category_groups[i], category_group) == 0) { 162 return &g_category_group_enabled[i]; 163 } 164 } 165 166 unsigned char* category_group_enabled = NULL; 167 size_t category_index = base::Acquire_Load(&g_category_index); 168 for (size_t i = 0; i < category_index; ++i) { 169 if (strcmp(g_category_groups[i], category_group) == 0) { 170 return &g_category_group_enabled[i]; 171 } 172 } 173 174 // Create a new category group. 175 // Check that there is a slot for the new category_group. 176 DCHECK(category_index < MAX_CATEGORY_GROUPS); 177 if (category_index < MAX_CATEGORY_GROUPS) { 178 // Don't hold on to the category_group pointer, so that we can create 179 // category groups with strings not known at compile time (this is 180 // required by SetWatchEvent). 181 const char* new_group = strdup(category_group); 182 g_category_groups[category_index] = new_group; 183 DCHECK(!g_category_group_enabled[category_index]); 184 // Note that if both included and excluded patterns in the 185 // TraceConfig are empty, we exclude nothing, 186 // thereby enabling this category group. 187 UpdateCategoryGroupEnabledFlag(category_index); 188 category_group_enabled = &g_category_group_enabled[category_index]; 189 // Update the max index now. 190 base::Release_Store(&g_category_index, category_index + 1); 191 } else { 192 category_group_enabled = 193 &g_category_group_enabled[g_category_categories_exhausted]; 194 } 195 return category_group_enabled; 196 } 197 198 void TracingController::AddTraceStateObserver( 199 Platform::TraceStateObserver* observer) { 200 { 201 base::LockGuard<base::Mutex> lock(mutex_.get()); 202 observers_.insert(observer); 203 if (mode_ != RECORDING_MODE) return; 204 } 205 // Fire the observer if recording is already in progress. 206 observer->OnTraceEnabled(); 207 } 208 209 void TracingController::RemoveTraceStateObserver( 210 Platform::TraceStateObserver* observer) { 211 base::LockGuard<base::Mutex> lock(mutex_.get()); 212 DCHECK(observers_.find(observer) != observers_.end()); 213 observers_.erase(observer); 214 } 215 216 } // namespace tracing 217 } // namespace platform 218 } // namespace v8 219