tracing-controller.cc 12.9 KB
Newer Older
1 2 3 4 5
// Copyright 2016 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include <stdio.h>
6
#include <stdlib.h>
7 8 9 10
#include <string.h>

#include "include/libplatform/v8-tracing.h"

11
#include "src/base/atomicops.h"
12
#include "src/base/platform/mutex.h"
13
#include "src/base/platform/time.h"
14

15
#ifdef V8_USE_PERFETTO
16 17 18 19 20 21
#include "perfetto/ext/trace_processor/export_json.h"
#include "perfetto/trace_processor/trace_processor.h"
#include "perfetto/tracing/tracing.h"
#include "protos/perfetto/config/data_source_config.gen.h"
#include "protos/perfetto/config/trace_config.gen.h"
#include "protos/perfetto/config/track_event/track_event_config.gen.h"
22 23
#include "src/base/platform/platform.h"
#include "src/base/platform/semaphore.h"
24
#include "src/libplatform/tracing/trace-event-listener.h"
25 26 27
#endif  // V8_USE_PERFETTO

#ifdef V8_USE_PERFETTO
28
class JsonOutputWriter : public perfetto::trace_processor::json::OutputWriter {
29
 public:
30 31 32 33 34 35 36
  explicit JsonOutputWriter(std::ostream* stream) : stream_(stream) {}

  perfetto::trace_processor::util::Status AppendString(
      const std::string& string) override {
    *stream_ << string;
    return perfetto::trace_processor::util::OkStatus();
  }
37

38 39 40
 private:
  std::ostream* stream_;
};
41 42
#endif  // V8_USE_PERFETTO

43 44 45 46
namespace v8 {
namespace platform {
namespace tracing {

47
#if !defined(V8_USE_PERFETTO)
48
static const size_t kMaxCategoryGroups = 200;
49 50 51 52 53 54 55

// Parallel arrays g_category_groups and g_category_group_enabled are separate
// so that a pointer to a member of g_category_group_enabled can be easily
// converted to an index into g_category_groups. This allows macros to deal
// only with char enabled pointers from g_category_group_enabled, and we can
// convert internally to determine the category name from the char enabled
// pointer.
56
const char* g_category_groups[kMaxCategoryGroups] = {
57
    "toplevel",
58
    "tracing categories exhausted; must increase kMaxCategoryGroups",
59 60 61
    "__metadata"};

// The enabled flag is char instead of bool so that the API can be used from C.
62
unsigned char g_category_group_enabled[kMaxCategoryGroups] = {0};
63
// Indexes here have to match the g_category_groups array indexes above.
64
const int g_category_categories_exhausted = 1;
65
// Metadata category not used in V8.
66 67
// const int g_category_metadata = 2;
const int g_num_builtin_categories = 3;
68 69 70

// Skip default categories.
v8::base::AtomicWord g_category_index = g_num_builtin_categories;
71
#endif  // !defined(V8_USE_PERFETTO)
72

73
TracingController::TracingController() { mutex_.reset(new base::Mutex()); }
74

75 76 77
TracingController::~TracingController() {
  StopTracing();

78
#if !defined(V8_USE_PERFETTO)
79 80
  {
    // Free memory for category group names allocated via strdup.
81
    base::MutexGuard lock(mutex_.get());
82
    for (size_t i = g_category_index - 1; i >= g_num_builtin_categories; --i) {
83 84 85 86 87 88
      const char* group = g_category_groups[i];
      g_category_groups[i] = nullptr;
      free(const_cast<char*>(group));
    }
    g_category_index = g_num_builtin_categories;
  }
89
#endif  // !defined(V8_USE_PERFETTO)
90 91
}

92 93 94 95 96 97
#ifdef V8_USE_PERFETTO
void TracingController::InitializeForPerfetto(std::ostream* output_stream) {
  output_stream_ = output_stream;
  DCHECK_NOT_NULL(output_stream);
  DCHECK(output_stream->good());
}
98 99 100 101 102

void TracingController::SetTraceEventListenerForTesting(
    TraceEventListener* listener) {
  listener_for_testing_ = listener;
}
103 104 105 106
#else   // !V8_USE_PERFETTO
void TracingController::Initialize(TraceBuffer* trace_buffer) {
  trace_buffer_.reset(trace_buffer);
}
107

108 109 110 111 112 113 114 115
int64_t TracingController::CurrentTimestampMicroseconds() {
  return base::TimeTicks::HighResolutionNow().ToInternalValue();
}

int64_t TracingController::CurrentCpuTimestampMicroseconds() {
  return base::ThreadTicks::Now().ToInternalValue();
}

116 117 118 119
uint64_t TracingController::AddTraceEvent(
    char phase, const uint8_t* category_enabled_flag, const char* name,
    const char* scope, uint64_t id, uint64_t bind_id, int num_args,
    const char** arg_names, const uint8_t* arg_types,
120 121 122
    const uint64_t* arg_values,
    std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
    unsigned int flags) {
123 124 125 126 127
  int64_t now_us = CurrentTimestampMicroseconds();

  return AddTraceEventWithTimestamp(
      phase, category_enabled_flag, name, scope, id, bind_id, num_args,
      arg_names, arg_types, arg_values, arg_convertables, flags, now_us);
128 129
}

130 131 132 133 134 135 136
uint64_t TracingController::AddTraceEventWithTimestamp(
    char phase, const uint8_t* category_enabled_flag, const char* name,
    const char* scope, uint64_t id, uint64_t bind_id, int num_args,
    const char** arg_names, const uint8_t* arg_types,
    const uint64_t* arg_values,
    std::unique_ptr<v8::ConvertableToTraceFormat>* arg_convertables,
    unsigned int flags, int64_t timestamp) {
137 138
  int64_t cpu_now_us = CurrentCpuTimestampMicroseconds();

139
  uint64_t handle = 0;
140
  if (recording_.load(std::memory_order_acquire)) {
141 142
    TraceObject* trace_object = trace_buffer_->AddTraceEvent(&handle);
    if (trace_object) {
143 144 145 146 147
      {
        base::MutexGuard lock(mutex_.get());
        trace_object->Initialize(phase, category_enabled_flag, name, scope, id,
                                 bind_id, num_args, arg_names, arg_types,
                                 arg_values, arg_convertables, flags, timestamp,
148
                                 cpu_now_us);
149
      }
150
    }
151 152 153 154
  }
  return handle;
}

155 156
void TracingController::UpdateTraceEventDuration(
    const uint8_t* category_enabled_flag, const char* name, uint64_t handle) {
157 158 159
  int64_t now_us = CurrentTimestampMicroseconds();
  int64_t cpu_now_us = CurrentCpuTimestampMicroseconds();

160 161
  TraceObject* trace_object = trace_buffer_->GetEventByHandle(handle);
  if (!trace_object) return;
162
  trace_object->UpdateDuration(now_us, cpu_now_us);
163 164 165 166 167 168 169 170 171 172 173 174
}

const char* TracingController::GetCategoryGroupName(
    const uint8_t* category_group_enabled) {
  // Calculate the index of the category group by finding
  // category_group_enabled in g_category_group_enabled array.
  uintptr_t category_begin =
      reinterpret_cast<uintptr_t>(g_category_group_enabled);
  uintptr_t category_ptr = reinterpret_cast<uintptr_t>(category_group_enabled);
  // Check for out of bounds category pointers.
  DCHECK(category_ptr >= category_begin &&
         category_ptr < reinterpret_cast<uintptr_t>(g_category_group_enabled +
175
                                                    kMaxCategoryGroups));
176 177 178 179
  uintptr_t category_index =
      (category_ptr - category_begin) / sizeof(g_category_group_enabled[0]);
  return g_category_groups[category_index];
}
180
#endif  // !defined(V8_USE_PERFETTO)
181 182

void TracingController::StartTracing(TraceConfig* trace_config) {
183
#ifdef V8_USE_PERFETTO
184 185
  DCHECK_NOT_NULL(output_stream_);
  DCHECK(output_stream_->good());
186 187 188 189
  perfetto::trace_processor::Config processor_config;
  trace_processor_ =
      perfetto::trace_processor::TraceProcessorStorage::CreateInstance(
          processor_config);
190

191
  ::perfetto::TraceConfig perfetto_trace_config;
192
  perfetto_trace_config.add_buffers()->set_size_kb(4096);
193 194 195 196 197 198 199
  auto ds_config = perfetto_trace_config.add_data_sources()->mutable_config();
  ds_config->set_name("track_event");
  perfetto::protos::gen::TrackEventConfig te_config;
  te_config.add_disabled_categories("*");
  for (const auto& category : trace_config->GetEnabledCategories())
    te_config.add_enabled_categories(category);
  ds_config->set_track_event_config_raw(te_config.SerializeAsString());
200 201 202 203

  tracing_session_ =
      perfetto::Tracing::NewTrace(perfetto::BackendType::kUnspecifiedBackend);
  tracing_session_->Setup(perfetto_trace_config);
204
  tracing_session_->StartBlocking();
205

206 207
#endif  // V8_USE_PERFETTO

208
  trace_config_.reset(trace_config);
209
  std::unordered_set<v8::TracingController::TraceStateObserver*> observers_copy;
210
  {
211
    base::MutexGuard lock(mutex_.get());
212
    recording_.store(true, std::memory_order_release);
213
#ifndef V8_USE_PERFETTO
214
    UpdateCategoryGroupEnabledFlags();
215
#endif
216 217 218 219 220
    observers_copy = observers_;
  }
  for (auto o : observers_copy) {
    o->OnTraceEnabled();
  }
221 222 223
}

void TracingController::StopTracing() {
224 225
  bool expected = true;
  if (!recording_.compare_exchange_strong(expected, false)) {
226 227
    return;
  }
228
#ifndef V8_USE_PERFETTO
229
  UpdateCategoryGroupEnabledFlags();
230
#endif
231
  std::unordered_set<v8::TracingController::TraceStateObserver*> observers_copy;
232
  {
233
    base::MutexGuard lock(mutex_.get());
234 235 236 237 238
    observers_copy = observers_;
  }
  for (auto o : observers_copy) {
    o->OnTraceDisabled();
  }
239 240

#ifdef V8_USE_PERFETTO
241
  tracing_session_->StopBlocking();
242 243

  std::vector<char> trace = tracing_session_->ReadTraceBlocking();
244 245 246 247 248 249 250 251 252
  std::unique_ptr<uint8_t[]> trace_bytes(new uint8_t[trace.size()]);
  std::copy(&trace[0], &trace[0] + trace.size(), &trace_bytes[0]);
  trace_processor_->Parse(std::move(trace_bytes), trace.size());
  trace_processor_->NotifyEndOfFile();
  JsonOutputWriter output_writer(output_stream_);
  auto status = perfetto::trace_processor::json::ExportJson(
      trace_processor_.get(), &output_writer, nullptr, nullptr, nullptr);
  DCHECK(status.ok());

253 254
  if (listener_for_testing_) listener_for_testing_->ParseFromArray(trace);

255
  trace_processor_.reset();
256
#else
257

258 259
  {
    base::MutexGuard lock(mutex_.get());
260
    DCHECK(trace_buffer_);
261 262
    trace_buffer_->Flush();
  }
263
#endif  // V8_USE_PERFETTO
264 265
}

266
#if !defined(V8_USE_PERFETTO)
267 268 269
void TracingController::UpdateCategoryGroupEnabledFlag(size_t category_index) {
  unsigned char enabled_flag = 0;
  const char* category_group = g_category_groups[category_index];
270
  if (recording_.load(std::memory_order_acquire) &&
271 272 273 274 275 276 277 278
      trace_config_->IsCategoryGroupEnabled(category_group)) {
    enabled_flag |= ENABLED_FOR_RECORDING;
  }

  // TODO(fmeawad): EventCallback and ETW modes are not yet supported in V8.
  // TODO(primiano): this is a temporary workaround for catapult:#2341,
  // to guarantee that metadata events are always added even if the category
  // filter is "-*". See crbug.com/618054 for more details and long-term fix.
279 280
  if (recording_.load(std::memory_order_acquire) &&
      !strcmp(category_group, "__metadata")) {
281 282 283
    enabled_flag |= ENABLED_FOR_RECORDING;
  }

284 285 286
  base::Relaxed_Store(reinterpret_cast<base::Atomic8*>(
                          g_category_group_enabled + category_index),
                      enabled_flag);
287 288 289
}

void TracingController::UpdateCategoryGroupEnabledFlags() {
290
  size_t category_index = base::Acquire_Load(&g_category_index);
291 292 293
  for (size_t i = 0; i < category_index; i++) UpdateCategoryGroupEnabledFlag(i);
}

294
const uint8_t* TracingController::GetCategoryGroupEnabled(
295
    const char* category_group) {
296
  // Check that category group does not contain double quote
297 298 299
  DCHECK(!strchr(category_group, '"'));

  // The g_category_groups is append only, avoid using a lock for the fast path.
300
  size_t category_index = base::Acquire_Load(&g_category_index);
301 302

  // Search for pre-existing category group.
303
  for (size_t i = 0; i < category_index; ++i) {
304 305 306 307 308
    if (strcmp(g_category_groups[i], category_group) == 0) {
      return &g_category_group_enabled[i];
    }
  }

309
  // Slow path. Grab the lock.
310
  base::MutexGuard lock(mutex_.get());
311 312

  // Check the list again with lock in hand.
313
  unsigned char* category_group_enabled = nullptr;
314
  category_index = base::Acquire_Load(&g_category_index);
315 316 317 318 319 320 321 322
  for (size_t i = 0; i < category_index; ++i) {
    if (strcmp(g_category_groups[i], category_group) == 0) {
      return &g_category_group_enabled[i];
    }
  }

  // Create a new category group.
  // Check that there is a slot for the new category_group.
323 324
  DCHECK(category_index < kMaxCategoryGroups);
  if (category_index < kMaxCategoryGroups) {
325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343
    // Don't hold on to the category_group pointer, so that we can create
    // category groups with strings not known at compile time (this is
    // required by SetWatchEvent).
    const char* new_group = strdup(category_group);
    g_category_groups[category_index] = new_group;
    DCHECK(!g_category_group_enabled[category_index]);
    // Note that if both included and excluded patterns in the
    // TraceConfig are empty, we exclude nothing,
    // thereby enabling this category group.
    UpdateCategoryGroupEnabledFlag(category_index);
    category_group_enabled = &g_category_group_enabled[category_index];
    // Update the max index now.
    base::Release_Store(&g_category_index, category_index + 1);
  } else {
    category_group_enabled =
        &g_category_group_enabled[g_category_categories_exhausted];
  }
  return category_group_enabled;
}
344
#endif  // !defined(V8_USE_PERFETTO)
345

346
void TracingController::AddTraceStateObserver(
347
    v8::TracingController::TraceStateObserver* observer) {
348
  {
349
    base::MutexGuard lock(mutex_.get());
350
    observers_.insert(observer);
351
    if (!recording_.load(std::memory_order_acquire)) return;
352 353 354 355 356 357
  }
  // Fire the observer if recording is already in progress.
  observer->OnTraceEnabled();
}

void TracingController::RemoveTraceStateObserver(
358
    v8::TracingController::TraceStateObserver* observer) {
359
  base::MutexGuard lock(mutex_.get());
360 361 362 363
  DCHECK(observers_.find(observer) != observers_.end());
  observers_.erase(observer);
}

364 365 366
}  // namespace tracing
}  // namespace platform
}  // namespace v8