Home | History | Annotate | Download | only in tracing
      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