[Tracing] V8 Tracing Controller
V8 has had a trace event macro interface for while, but without a tracing controller a standalone V8 would be unable to collect traces. This CL introduces a complete Tracing Controller system for V8. It is fully function except that it does not yet store trace event args. This CL has a few components, The tracing controller itself, contributed by the author of this CL The Trace config (including the parser), contributed by lpy@ The Trace Object, Trace Writer, and Trace Buffer are all contributed by rksang@ BUG=v8:4561 LOG=N Review-Url: https://codereview.chromium.org/2137013006 Cr-Commit-Position: refs/heads/master@{#38073}
This commit is contained in:
parent
038bafcabf
commit
3d59845267
8
BUILD.gn
8
BUILD.gn
@ -2112,10 +2112,18 @@ v8_source_set("v8_libbase") {
|
||||
v8_source_set("v8_libplatform") {
|
||||
sources = [
|
||||
"include/libplatform/libplatform.h",
|
||||
"include/libplatform/v8-tracing.h",
|
||||
"src/libplatform/default-platform.cc",
|
||||
"src/libplatform/default-platform.h",
|
||||
"src/libplatform/task-queue.cc",
|
||||
"src/libplatform/task-queue.h",
|
||||
"src/libplatform/tracing/trace-buffer.cc",
|
||||
"src/libplatform/tracing/trace-buffer.h",
|
||||
"src/libplatform/tracing/trace-config.cc",
|
||||
"src/libplatform/tracing/trace-object.cc",
|
||||
"src/libplatform/tracing/trace-writer.cc",
|
||||
"src/libplatform/tracing/trace-writer.h",
|
||||
"src/libplatform/tracing/tracing-controller.cc",
|
||||
"src/libplatform/worker-thread.cc",
|
||||
"src/libplatform/worker-thread.h",
|
||||
]
|
||||
|
@ -5,6 +5,7 @@
|
||||
#ifndef V8_LIBPLATFORM_LIBPLATFORM_H_
|
||||
#define V8_LIBPLATFORM_LIBPLATFORM_H_
|
||||
|
||||
#include "include/libplatform/v8-tracing.h"
|
||||
#include "v8-platform.h" // NOLINT(build/include)
|
||||
|
||||
namespace v8 {
|
||||
@ -31,6 +32,14 @@ v8::Platform* CreateDefaultPlatform(int thread_pool_size = 0);
|
||||
*/
|
||||
bool PumpMessageLoop(v8::Platform* platform, v8::Isolate* isolate);
|
||||
|
||||
/**
|
||||
* Attempts to set the tracing controller for the given platform.
|
||||
*
|
||||
* The |platform| has to be created using |CreateDefaultPlatform|.
|
||||
*/
|
||||
void SetTracingController(
|
||||
v8::Platform* platform,
|
||||
v8::platform::tracing::TracingController* tracing_controller);
|
||||
|
||||
} // namespace platform
|
||||
} // namespace v8
|
||||
|
232
include/libplatform/v8-tracing.h
Normal file
232
include/libplatform/v8-tracing.h
Normal file
@ -0,0 +1,232 @@
|
||||
// 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.
|
||||
|
||||
#ifndef V8_LIBPLATFORM_V8_TRACING_H_
|
||||
#define V8_LIBPLATFORM_V8_TRACING_H_
|
||||
|
||||
#include <fstream>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
class TraceObject {
|
||||
public:
|
||||
TraceObject() {}
|
||||
void Initialize(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,
|
||||
unsigned int flags);
|
||||
void UpdateDuration();
|
||||
void InitializeForTesting(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, unsigned int flags,
|
||||
int pid, int tid, int64_t ts, int64_t tts,
|
||||
uint64_t duration, uint64_t cpu_duration);
|
||||
|
||||
int pid() const { return pid_; }
|
||||
int tid() const { return tid_; }
|
||||
char phase() const { return phase_; }
|
||||
const uint8_t* category_enabled_flag() const {
|
||||
return category_enabled_flag_;
|
||||
}
|
||||
const char* name() const { return name_; }
|
||||
const char* scope() const { return scope_; }
|
||||
uint64_t id() const { return id_; }
|
||||
uint64_t bind_id() const { return bind_id_; }
|
||||
unsigned int flags() const { return flags_; }
|
||||
int64_t ts() { return ts_; }
|
||||
int64_t tts() { return tts_; }
|
||||
uint64_t duration() { return duration_; }
|
||||
uint64_t cpu_duration() { return cpu_duration_; }
|
||||
|
||||
private:
|
||||
int pid_;
|
||||
int tid_;
|
||||
char phase_;
|
||||
const char* name_;
|
||||
const char* scope_;
|
||||
const uint8_t* category_enabled_flag_;
|
||||
uint64_t id_;
|
||||
uint64_t bind_id_;
|
||||
int num_args_;
|
||||
unsigned int flags_;
|
||||
int64_t ts_;
|
||||
int64_t tts_;
|
||||
uint64_t duration_;
|
||||
uint64_t cpu_duration_;
|
||||
// TODO(fmeawad): Add args support.
|
||||
|
||||
// Disallow copy and assign
|
||||
TraceObject(const TraceObject&) = delete;
|
||||
void operator=(const TraceObject&) = delete;
|
||||
};
|
||||
|
||||
class TraceWriter {
|
||||
public:
|
||||
TraceWriter() {}
|
||||
virtual ~TraceWriter() {}
|
||||
virtual void AppendTraceEvent(TraceObject* trace_event) = 0;
|
||||
virtual void Flush() = 0;
|
||||
|
||||
static TraceWriter* CreateJSONTraceWriter(std::ostream& stream);
|
||||
|
||||
private:
|
||||
// Disallow copy and assign
|
||||
TraceWriter(const TraceWriter&) = delete;
|
||||
void operator=(const TraceWriter&) = delete;
|
||||
};
|
||||
|
||||
class TraceBufferChunk {
|
||||
public:
|
||||
explicit TraceBufferChunk(uint32_t seq);
|
||||
|
||||
void Reset(uint32_t new_seq);
|
||||
bool IsFull() const { return next_free_ == kChunkSize; }
|
||||
TraceObject* AddTraceEvent(size_t* event_index);
|
||||
TraceObject* GetEventAt(size_t index) { return &chunk_[index]; }
|
||||
|
||||
uint32_t seq() const { return seq_; }
|
||||
size_t size() const { return next_free_; }
|
||||
|
||||
static const size_t kChunkSize = 64;
|
||||
|
||||
private:
|
||||
size_t next_free_ = 0;
|
||||
TraceObject chunk_[kChunkSize];
|
||||
uint32_t seq_;
|
||||
|
||||
// Disallow copy and assign
|
||||
TraceBufferChunk(const TraceBufferChunk&) = delete;
|
||||
void operator=(const TraceBufferChunk&) = delete;
|
||||
};
|
||||
|
||||
class TraceBuffer {
|
||||
public:
|
||||
TraceBuffer() {}
|
||||
virtual ~TraceBuffer() {}
|
||||
|
||||
virtual TraceObject* AddTraceEvent(uint64_t* handle) = 0;
|
||||
virtual TraceObject* GetEventByHandle(uint64_t handle) = 0;
|
||||
virtual bool Flush() = 0;
|
||||
|
||||
static const size_t kRingBufferChunks = 1024;
|
||||
|
||||
static TraceBuffer* CreateTraceBufferRingBuffer(size_t max_chunks,
|
||||
TraceWriter* trace_writer);
|
||||
|
||||
private:
|
||||
// Disallow copy and assign
|
||||
TraceBuffer(const TraceBuffer&) = delete;
|
||||
void operator=(const TraceBuffer&) = delete;
|
||||
};
|
||||
|
||||
// Options determines how the trace buffer stores data.
|
||||
enum TraceRecordMode {
|
||||
// Record until the trace buffer is full.
|
||||
RECORD_UNTIL_FULL,
|
||||
|
||||
// Record until the user ends the trace. The trace buffer is a fixed size
|
||||
// and we use it as a ring buffer during recording.
|
||||
RECORD_CONTINUOUSLY,
|
||||
|
||||
// Record until the trace buffer is full, but with a huge buffer size.
|
||||
RECORD_AS_MUCH_AS_POSSIBLE,
|
||||
|
||||
// Echo to console. Events are discarded.
|
||||
ECHO_TO_CONSOLE,
|
||||
};
|
||||
|
||||
class TraceConfig {
|
||||
public:
|
||||
typedef std::vector<std::string> StringList;
|
||||
|
||||
TraceConfig()
|
||||
: enable_sampling_(false),
|
||||
enable_systrace_(false),
|
||||
enable_argument_filter_(false) {}
|
||||
TraceRecordMode GetTraceRecordMode() const { return record_mode_; }
|
||||
bool IsSamplingEnabled() const { return enable_sampling_; }
|
||||
bool IsSystraceEnabled() const { return enable_systrace_; }
|
||||
bool IsArgumentFilterEnabled() const { return enable_argument_filter_; }
|
||||
|
||||
void SetTraceRecordMode(TraceRecordMode mode) { record_mode_ = mode; }
|
||||
void EnableSampling() { enable_sampling_ = true; }
|
||||
void EnableSystrace() { enable_systrace_ = true; }
|
||||
void EnableArgumentFilter() { enable_argument_filter_ = true; }
|
||||
|
||||
void AddIncludedCategory(const char* included_category);
|
||||
void AddExcludedCategory(const char* excluded_category);
|
||||
|
||||
bool IsCategoryGroupEnabled(const char* category_group) const;
|
||||
|
||||
private:
|
||||
TraceRecordMode record_mode_;
|
||||
bool enable_sampling_ : 1;
|
||||
bool enable_systrace_ : 1;
|
||||
bool enable_argument_filter_ : 1;
|
||||
StringList included_categories_;
|
||||
StringList excluded_categories_;
|
||||
|
||||
// Disallow copy and assign
|
||||
TraceConfig(const TraceConfig&) = delete;
|
||||
void operator=(const TraceConfig&) = delete;
|
||||
};
|
||||
|
||||
class TracingController {
|
||||
public:
|
||||
enum Mode { DISABLED = 0, RECORDING_MODE };
|
||||
|
||||
// The pointer returned from GetCategoryGroupEnabledInternal() points to a
|
||||
// value with zero or more of the following bits. Used in this class only.
|
||||
// The TRACE_EVENT macros should only use the value as a bool.
|
||||
// These values must be in sync with macro values in TraceEvent.h in Blink.
|
||||
enum CategoryGroupEnabledFlags {
|
||||
// Category group enabled for the recording mode.
|
||||
ENABLED_FOR_RECORDING = 1 << 0,
|
||||
// Category group enabled by SetEventCallbackEnabled().
|
||||
ENABLED_FOR_EVENT_CALLBACK = 1 << 2,
|
||||
// Category group enabled to export events to ETW.
|
||||
ENABLED_FOR_ETW_EXPORT = 1 << 3
|
||||
};
|
||||
|
||||
TracingController() {}
|
||||
void Initialize(TraceBuffer* trace_buffer);
|
||||
const uint8_t* GetCategoryGroupEnabled(const char* category_group);
|
||||
static const char* GetCategoryGroupName(const uint8_t* category_enabled_flag);
|
||||
uint64_t AddTraceEvent(char phase, const uint8_t* category_enabled_flag,
|
||||
const char* name, const char* scope, uint64_t id,
|
||||
uint64_t bind_id, int32_t num_args,
|
||||
const char** arg_names, const uint8_t* arg_types,
|
||||
const uint64_t* arg_values, unsigned int flags);
|
||||
void UpdateTraceEventDuration(const uint8_t* category_enabled_flag,
|
||||
const char* name, uint64_t handle);
|
||||
|
||||
void StartTracing(TraceConfig* trace_config);
|
||||
void StopTracing();
|
||||
|
||||
private:
|
||||
const uint8_t* GetCategoryGroupEnabledInternal(const char* category_group);
|
||||
void UpdateCategoryGroupEnabledFlag(size_t category_index);
|
||||
void UpdateCategoryGroupEnabledFlags();
|
||||
|
||||
std::unique_ptr<TraceBuffer> trace_buffer_;
|
||||
std::unique_ptr<TraceConfig> trace_config_;
|
||||
Mode mode_ = DISABLED;
|
||||
|
||||
// Disallow copy and assign
|
||||
TracingController(const TracingController&) = delete;
|
||||
void operator=(const TracingController&) = delete;
|
||||
};
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_LIBPLATFORM_V8_TRACING_H_
|
1
src/DEPS
1
src/DEPS
@ -22,5 +22,6 @@ include_rules = [
|
||||
specific_include_rules = {
|
||||
"d8\.cc": [
|
||||
"+include/libplatform/libplatform.h",
|
||||
"+include/libplatform/v8-tracing.h",
|
||||
],
|
||||
}
|
||||
|
28
src/d8.cc
28
src/d8.cc
@ -35,6 +35,7 @@
|
||||
#include "src/ostreams.h"
|
||||
|
||||
#include "include/libplatform/libplatform.h"
|
||||
#include "include/libplatform/v8-tracing.h"
|
||||
#ifndef V8_SHARED
|
||||
#include "src/api.h"
|
||||
#include "src/base/cpu.h"
|
||||
@ -1996,6 +1997,9 @@ bool Shell::SetOptions(int argc, char* argv[]) {
|
||||
return false;
|
||||
}
|
||||
argv[i] = NULL;
|
||||
} else if (strcmp(argv[i], "--enable-tracing") == 0) {
|
||||
options.trace_enabled = true;
|
||||
argv[i] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2412,6 +2416,7 @@ static void DumpHeapConstants(i::Isolate* isolate) {
|
||||
|
||||
|
||||
int Shell::Main(int argc, char* argv[]) {
|
||||
std::ofstream trace_file;
|
||||
#if (defined(_WIN32) || defined(_WIN64))
|
||||
UINT new_flags =
|
||||
SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX;
|
||||
@ -2479,6 +2484,29 @@ int Shell::Main(int argc, char* argv[]) {
|
||||
Initialize(isolate);
|
||||
PerIsolateData data(isolate);
|
||||
|
||||
if (options.trace_enabled) {
|
||||
trace_file.open("v8_trace.json");
|
||||
platform::tracing::TracingController* tracing_controller =
|
||||
new platform::tracing::TracingController();
|
||||
platform::tracing::TraceBuffer* trace_buffer =
|
||||
platform::tracing::TraceBuffer::CreateTraceBufferRingBuffer(
|
||||
platform::tracing::TraceBuffer::kRingBufferChunks,
|
||||
platform::tracing::TraceWriter::CreateJSONTraceWriter(
|
||||
trace_file));
|
||||
platform::tracing::TraceConfig* trace_config;
|
||||
trace_config = new platform::tracing::TraceConfig();
|
||||
trace_config->AddIncludedCategory("v8");
|
||||
tracing_controller->Initialize(trace_buffer);
|
||||
tracing_controller->StartTracing(trace_config);
|
||||
#ifndef V8_SHARED
|
||||
if (!i::FLAG_verify_predictable) {
|
||||
platform::SetTracingController(g_platform, tracing_controller);
|
||||
}
|
||||
#else
|
||||
platform::SetTracingController(g_platform, tracing_controller);
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifndef V8_SHARED
|
||||
if (options.dump_heap_constants) {
|
||||
DumpHeapConstants(reinterpret_cast<i::Isolate*>(isolate));
|
||||
|
5
src/d8.h
5
src/d8.h
@ -290,7 +290,8 @@ class ShellOptions {
|
||||
isolate_sources(NULL),
|
||||
icu_data_file(NULL),
|
||||
natives_blob(NULL),
|
||||
snapshot_blob(NULL) {}
|
||||
snapshot_blob(NULL),
|
||||
trace_enabled(false) {}
|
||||
|
||||
~ShellOptions() {
|
||||
delete[] isolate_sources;
|
||||
@ -318,6 +319,8 @@ class ShellOptions {
|
||||
const char* icu_data_file;
|
||||
const char* natives_blob;
|
||||
const char* snapshot_blob;
|
||||
bool trace_enabled;
|
||||
const char* trace_config;
|
||||
};
|
||||
|
||||
#ifdef V8_SHARED
|
||||
|
@ -29,11 +29,17 @@ bool PumpMessageLoop(v8::Platform* platform, v8::Isolate* isolate) {
|
||||
return reinterpret_cast<DefaultPlatform*>(platform)->PumpMessageLoop(isolate);
|
||||
}
|
||||
|
||||
void SetTracingController(
|
||||
v8::Platform* platform,
|
||||
v8::platform::tracing::TracingController* tracing_controller) {
|
||||
return reinterpret_cast<DefaultPlatform*>(platform)->SetTracingController(
|
||||
tracing_controller);
|
||||
}
|
||||
|
||||
const int DefaultPlatform::kMaxThreadPoolSize = 8;
|
||||
|
||||
DefaultPlatform::DefaultPlatform()
|
||||
: initialized_(false), thread_pool_size_(0) {}
|
||||
|
||||
: initialized_(false), thread_pool_size_(0), tracing_controller_(NULL) {}
|
||||
|
||||
DefaultPlatform::~DefaultPlatform() {
|
||||
base::LockGuard<base::Mutex> guard(&lock_);
|
||||
@ -57,6 +63,11 @@ DefaultPlatform::~DefaultPlatform() {
|
||||
i->second.pop();
|
||||
}
|
||||
}
|
||||
|
||||
if (tracing_controller_) {
|
||||
tracing_controller_->StopTracing();
|
||||
delete tracing_controller_;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -173,15 +184,27 @@ uint64_t DefaultPlatform::AddTraceEvent(
|
||||
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, unsigned int flags) {
|
||||
if (tracing_controller_) {
|
||||
return tracing_controller_->AddTraceEvent(
|
||||
phase, category_enabled_flag, name, scope, id, bind_id, num_args,
|
||||
arg_names, arg_types, arg_values, flags);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
void DefaultPlatform::UpdateTraceEventDuration(
|
||||
const uint8_t* category_enabled_flag, const char* name, uint64_t handle) {}
|
||||
|
||||
const uint8_t* category_enabled_flag, const char* name, uint64_t handle) {
|
||||
if (tracing_controller_) {
|
||||
tracing_controller_->UpdateTraceEventDuration(category_enabled_flag, name,
|
||||
handle);
|
||||
}
|
||||
}
|
||||
|
||||
const uint8_t* DefaultPlatform::GetCategoryGroupEnabled(const char* name) {
|
||||
if (tracing_controller_) {
|
||||
return tracing_controller_->GetCategoryGroupEnabled(name);
|
||||
}
|
||||
static uint8_t no = 0;
|
||||
return &no;
|
||||
}
|
||||
@ -193,6 +216,10 @@ const char* DefaultPlatform::GetCategoryGroupName(
|
||||
return dummy;
|
||||
}
|
||||
|
||||
void DefaultPlatform::SetTracingController(
|
||||
tracing::TracingController* tracing_controller) {
|
||||
tracing_controller_ = tracing_controller;
|
||||
}
|
||||
|
||||
size_t DefaultPlatform::NumberOfAvailableBackgroundThreads() {
|
||||
return static_cast<size_t>(thread_pool_size_);
|
||||
|
@ -10,6 +10,7 @@
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
|
||||
#include "include/libplatform/v8-tracing.h"
|
||||
#include "include/v8-platform.h"
|
||||
#include "src/base/macros.h"
|
||||
#include "src/base/platform/mutex.h"
|
||||
@ -22,6 +23,10 @@ class TaskQueue;
|
||||
class Thread;
|
||||
class WorkerThread;
|
||||
|
||||
namespace tracing {
|
||||
class TracingController;
|
||||
}
|
||||
|
||||
class DefaultPlatform : public Platform {
|
||||
public:
|
||||
DefaultPlatform();
|
||||
@ -54,7 +59,7 @@ class DefaultPlatform : public Platform {
|
||||
unsigned int flags) override;
|
||||
void UpdateTraceEventDuration(const uint8_t* category_enabled_flag,
|
||||
const char* name, uint64_t handle) override;
|
||||
|
||||
void SetTracingController(tracing::TracingController* tracing_controller);
|
||||
|
||||
private:
|
||||
static const int kMaxThreadPoolSize;
|
||||
@ -74,6 +79,7 @@ class DefaultPlatform : public Platform {
|
||||
std::priority_queue<DelayedEntry, std::vector<DelayedEntry>,
|
||||
std::greater<DelayedEntry> > >
|
||||
main_thread_delayed_queue_;
|
||||
tracing::TracingController* tracing_controller_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(DefaultPlatform);
|
||||
};
|
||||
|
109
src/libplatform/tracing/trace-buffer.cc
Normal file
109
src/libplatform/tracing/trace-buffer.cc
Normal file
@ -0,0 +1,109 @@
|
||||
// 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 "src/libplatform/tracing/trace-buffer.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
TraceBufferRingBuffer::TraceBufferRingBuffer(size_t max_chunks,
|
||||
TraceWriter* trace_writer)
|
||||
: max_chunks_(max_chunks) {
|
||||
trace_writer_.reset(trace_writer);
|
||||
chunks_.resize(max_chunks);
|
||||
}
|
||||
|
||||
TraceBufferRingBuffer::~TraceBufferRingBuffer() {}
|
||||
|
||||
TraceObject* TraceBufferRingBuffer::AddTraceEvent(uint64_t* handle) {
|
||||
base::LockGuard<base::Mutex> guard(&mutex_);
|
||||
if (is_empty_ || chunks_[chunk_index_]->IsFull()) {
|
||||
chunk_index_ = is_empty_ ? 0 : NextChunkIndex(chunk_index_);
|
||||
is_empty_ = false;
|
||||
auto& chunk = chunks_[chunk_index_];
|
||||
if (chunk) {
|
||||
chunk->Reset(current_chunk_seq_++);
|
||||
} else {
|
||||
chunk.reset(new TraceBufferChunk(current_chunk_seq_++));
|
||||
}
|
||||
}
|
||||
auto& chunk = chunks_[chunk_index_];
|
||||
size_t event_index;
|
||||
TraceObject* trace_object = chunk->AddTraceEvent(&event_index);
|
||||
*handle = MakeHandle(chunk_index_, chunk->seq(), event_index);
|
||||
return trace_object;
|
||||
}
|
||||
|
||||
TraceObject* TraceBufferRingBuffer::GetEventByHandle(uint64_t handle) {
|
||||
base::LockGuard<base::Mutex> guard(&mutex_);
|
||||
size_t chunk_index, event_index;
|
||||
uint32_t chunk_seq;
|
||||
ExtractHandle(handle, &chunk_index, &chunk_seq, &event_index);
|
||||
if (chunk_index >= chunks_.size()) return NULL;
|
||||
auto& chunk = chunks_[chunk_index];
|
||||
if (!chunk || chunk->seq() != chunk_seq) return NULL;
|
||||
return chunk->GetEventAt(event_index);
|
||||
}
|
||||
|
||||
bool TraceBufferRingBuffer::Flush() {
|
||||
base::LockGuard<base::Mutex> guard(&mutex_);
|
||||
// This flushes all the traces stored in the buffer.
|
||||
if (!is_empty_) {
|
||||
for (size_t i = NextChunkIndex(chunk_index_);; i = NextChunkIndex(i)) {
|
||||
if (auto& chunk = chunks_[i]) {
|
||||
for (size_t j = 0; j < chunk->size(); ++j) {
|
||||
trace_writer_->AppendTraceEvent(chunk->GetEventAt(j));
|
||||
}
|
||||
}
|
||||
if (i == chunk_index_) break;
|
||||
}
|
||||
}
|
||||
trace_writer_->Flush();
|
||||
// This resets the trace buffer.
|
||||
is_empty_ = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
uint64_t TraceBufferRingBuffer::MakeHandle(size_t chunk_index,
|
||||
uint32_t chunk_seq,
|
||||
size_t event_index) const {
|
||||
return static_cast<uint64_t>(chunk_seq) * Capacity() +
|
||||
chunk_index * TraceBufferChunk::kChunkSize + event_index;
|
||||
}
|
||||
|
||||
void TraceBufferRingBuffer::ExtractHandle(uint64_t handle, size_t* chunk_index,
|
||||
uint32_t* chunk_seq,
|
||||
size_t* event_index) const {
|
||||
*chunk_seq = static_cast<uint32_t>(handle / Capacity());
|
||||
size_t indices = handle % Capacity();
|
||||
*chunk_index = indices / TraceBufferChunk::kChunkSize;
|
||||
*event_index = indices % TraceBufferChunk::kChunkSize;
|
||||
}
|
||||
|
||||
size_t TraceBufferRingBuffer::NextChunkIndex(size_t index) const {
|
||||
if (++index >= max_chunks_) index = 0;
|
||||
return index;
|
||||
}
|
||||
|
||||
TraceBufferChunk::TraceBufferChunk(uint32_t seq) : seq_(seq) {}
|
||||
|
||||
void TraceBufferChunk::Reset(uint32_t new_seq) {
|
||||
next_free_ = 0;
|
||||
seq_ = new_seq;
|
||||
}
|
||||
|
||||
TraceObject* TraceBufferChunk::AddTraceEvent(size_t* event_index) {
|
||||
*event_index = next_free_++;
|
||||
return &chunk_[*event_index];
|
||||
}
|
||||
|
||||
TraceBuffer* TraceBuffer::CreateTraceBufferRingBuffer(
|
||||
size_t max_chunks, TraceWriter* trace_writer) {
|
||||
return new TraceBufferRingBuffer(max_chunks, trace_writer);
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
48
src/libplatform/tracing/trace-buffer.h
Normal file
48
src/libplatform/tracing/trace-buffer.h
Normal file
@ -0,0 +1,48 @@
|
||||
// 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.
|
||||
|
||||
#ifndef SRC_LIBPLATFORM_TRACING_TRACE_BUFFER_H_
|
||||
#define SRC_LIBPLATFORM_TRACING_TRACE_BUFFER_H_
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "include/libplatform/v8-tracing.h"
|
||||
#include "src/base/platform/mutex.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
class TraceBufferRingBuffer : public TraceBuffer {
|
||||
public:
|
||||
TraceBufferRingBuffer(size_t max_chunks, TraceWriter* trace_writer);
|
||||
~TraceBufferRingBuffer();
|
||||
|
||||
TraceObject* AddTraceEvent(uint64_t* handle) override;
|
||||
TraceObject* GetEventByHandle(uint64_t handle) override;
|
||||
bool Flush() override;
|
||||
|
||||
private:
|
||||
uint64_t MakeHandle(size_t chunk_index, uint32_t chunk_seq,
|
||||
size_t event_index) const;
|
||||
void ExtractHandle(uint64_t handle, size_t* chunk_index, uint32_t* chunk_seq,
|
||||
size_t* event_index) const;
|
||||
size_t Capacity() const { return max_chunks_ * TraceBufferChunk::kChunkSize; }
|
||||
size_t NextChunkIndex(size_t index) const;
|
||||
|
||||
mutable base::Mutex mutex_;
|
||||
size_t max_chunks_;
|
||||
std::unique_ptr<TraceWriter> trace_writer_;
|
||||
std::vector<std::unique_ptr<TraceBufferChunk>> chunks_;
|
||||
size_t chunk_index_;
|
||||
bool is_empty_ = true;
|
||||
uint32_t current_chunk_seq_ = 1;
|
||||
};
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
||||
|
||||
#endif // SRC_LIBPLATFORM_TRACING_TRACE_BUFFER_H_
|
36
src/libplatform/tracing/trace-config.cc
Normal file
36
src/libplatform/tracing/trace-config.cc
Normal file
@ -0,0 +1,36 @@
|
||||
// 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 <string.h>
|
||||
|
||||
#include "include/libplatform/v8-tracing.h"
|
||||
#include "src/base/logging.h"
|
||||
|
||||
namespace v8 {
|
||||
|
||||
class Isolate;
|
||||
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
bool TraceConfig::IsCategoryGroupEnabled(const char* category_group) const {
|
||||
for (auto included_category : included_categories_) {
|
||||
if (strcmp(included_category.data(), category_group) == 0) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void TraceConfig::AddIncludedCategory(const char* included_category) {
|
||||
DCHECK(included_category != NULL && strlen(included_category) > 0);
|
||||
included_categories_.push_back(included_category);
|
||||
}
|
||||
|
||||
void TraceConfig::AddExcludedCategory(const char* excluded_category) {
|
||||
DCHECK(excluded_category != NULL && strlen(excluded_category) > 0);
|
||||
excluded_categories_.push_back(excluded_category);
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
64
src/libplatform/tracing/trace-object.cc
Normal file
64
src/libplatform/tracing/trace-object.cc
Normal file
@ -0,0 +1,64 @@
|
||||
// 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 "include/libplatform/v8-tracing.h"
|
||||
|
||||
#include "src/base/platform/platform.h"
|
||||
#include "src/base/platform/time.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
void TraceObject::Initialize(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, unsigned int flags) {
|
||||
pid_ = base::OS::GetCurrentProcessId();
|
||||
tid_ = base::OS::GetCurrentThreadId();
|
||||
phase_ = phase;
|
||||
category_enabled_flag_ = category_enabled_flag;
|
||||
name_ = name;
|
||||
scope_ = scope;
|
||||
id_ = id;
|
||||
bind_id_ = bind_id;
|
||||
num_args_ = num_args;
|
||||
flags_ = flags;
|
||||
ts_ = base::TimeTicks::HighResolutionNow().ToInternalValue();
|
||||
tts_ = base::ThreadTicks::Now().ToInternalValue();
|
||||
duration_ = 0;
|
||||
cpu_duration_ = 0;
|
||||
}
|
||||
|
||||
void TraceObject::UpdateDuration() {
|
||||
duration_ = base::TimeTicks::HighResolutionNow().ToInternalValue() - ts_;
|
||||
cpu_duration_ = base::ThreadTicks::Now().ToInternalValue() - tts_;
|
||||
}
|
||||
|
||||
void TraceObject::InitializeForTesting(
|
||||
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, unsigned int flags, int pid, int tid,
|
||||
int64_t ts, int64_t tts, uint64_t duration, uint64_t cpu_duration) {
|
||||
pid_ = pid;
|
||||
tid_ = tid;
|
||||
phase_ = phase;
|
||||
category_enabled_flag_ = category_enabled_flag;
|
||||
name_ = name;
|
||||
scope_ = scope;
|
||||
id_ = id;
|
||||
bind_id_ = bind_id;
|
||||
num_args_ = num_args;
|
||||
flags_ = flags;
|
||||
ts_ = ts;
|
||||
tts_ = tts;
|
||||
duration_ = duration;
|
||||
cpu_duration_ = cpu_duration;
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
57
src/libplatform/tracing/trace-writer.cc
Normal file
57
src/libplatform/tracing/trace-writer.cc
Normal file
@ -0,0 +1,57 @@
|
||||
// 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 "src/libplatform/tracing/trace-writer.h"
|
||||
|
||||
#include "src/base/platform/platform.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
JSONTraceWriter::JSONTraceWriter(std::ostream& stream) : stream_(stream) {
|
||||
stream_ << "{\"traceEvents\":[";
|
||||
}
|
||||
|
||||
JSONTraceWriter::~JSONTraceWriter() { stream_ << "]}"; }
|
||||
|
||||
void JSONTraceWriter::AppendTraceEvent(TraceObject* trace_event) {
|
||||
if (append_comma_) stream_ << ",";
|
||||
append_comma_ = true;
|
||||
if (trace_event->scope() == NULL) {
|
||||
stream_ << "{\"pid\":" << trace_event->pid()
|
||||
<< ",\"tid\":" << trace_event->tid()
|
||||
<< ",\"ts\":" << trace_event->ts()
|
||||
<< ",\"tts\":" << trace_event->tts() << ",\"ph\":\""
|
||||
<< trace_event->phase() << "\",\"cat\":\""
|
||||
<< TracingController::GetCategoryGroupName(
|
||||
trace_event->category_enabled_flag())
|
||||
<< "\",\"name\":\"" << trace_event->name()
|
||||
<< "\",\"args\":{},\"dur\":" << trace_event->duration()
|
||||
<< ",\"tdur\":" << trace_event->cpu_duration() << "}";
|
||||
} else {
|
||||
stream_ << "{\"pid\":" << trace_event->pid()
|
||||
<< ",\"tid\":" << trace_event->tid()
|
||||
<< ",\"ts\":" << trace_event->ts()
|
||||
<< ",\"tts\":" << trace_event->tts() << ",\"ph\":\""
|
||||
<< trace_event->phase() << "\",\"cat\":\""
|
||||
<< TracingController::GetCategoryGroupName(
|
||||
trace_event->category_enabled_flag())
|
||||
<< "\",\"name\":\"" << trace_event->name() << "\",\"scope\":\""
|
||||
<< trace_event->scope()
|
||||
<< "\",\"args\":{},\"dur\":" << trace_event->duration()
|
||||
<< ",\"tdur\":" << trace_event->cpu_duration() << "}";
|
||||
}
|
||||
// TODO(fmeawad): Add support for Flow Events.
|
||||
}
|
||||
|
||||
void JSONTraceWriter::Flush() {}
|
||||
|
||||
TraceWriter* TraceWriter::CreateJSONTraceWriter(std::ostream& stream) {
|
||||
return new JSONTraceWriter(stream);
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
30
src/libplatform/tracing/trace-writer.h
Normal file
30
src/libplatform/tracing/trace-writer.h
Normal file
@ -0,0 +1,30 @@
|
||||
// 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.
|
||||
|
||||
#ifndef SRC_LIBPLATFORM_TRACING_TRACE_WRITER_H_
|
||||
#define SRC_LIBPLATFORM_TRACING_TRACE_WRITER_H_
|
||||
|
||||
#include "include/libplatform/v8-tracing.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
class JSONTraceWriter : public TraceWriter {
|
||||
public:
|
||||
explicit JSONTraceWriter(std::ostream& stream);
|
||||
~JSONTraceWriter();
|
||||
void AppendTraceEvent(TraceObject* trace_event) override;
|
||||
void Flush() override;
|
||||
|
||||
private:
|
||||
std::ostream& stream_;
|
||||
bool append_comma_ = false;
|
||||
};
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
||||
|
||||
#endif // SRC_LIBPLATFORM_TRACING_TRACE_WRITER_H_
|
177
src/libplatform/tracing/tracing-controller.cc
Normal file
177
src/libplatform/tracing/tracing-controller.cc
Normal file
@ -0,0 +1,177 @@
|
||||
// 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>
|
||||
#include <string.h>
|
||||
|
||||
#include "include/libplatform/v8-tracing.h"
|
||||
|
||||
#include "src/base/platform/mutex.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
#define MAX_CATEGORY_GROUPS 200
|
||||
|
||||
// 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.
|
||||
const char* g_category_groups[MAX_CATEGORY_GROUPS] = {
|
||||
"toplevel", "tracing already shutdown",
|
||||
"tracing categories exhausted; must increase MAX_CATEGORY_GROUPS",
|
||||
"__metadata"};
|
||||
|
||||
// The enabled flag is char instead of bool so that the API can be used from C.
|
||||
unsigned char g_category_group_enabled[MAX_CATEGORY_GROUPS] = {0};
|
||||
// Indexes here have to match the g_category_groups array indexes above.
|
||||
const int g_category_already_shutdown = 1;
|
||||
const int g_category_categories_exhausted = 2;
|
||||
// Metadata category not used in V8.
|
||||
// const int g_category_metadata = 3;
|
||||
const int g_num_builtin_categories = 4;
|
||||
|
||||
// Skip default categories.
|
||||
v8::base::AtomicWord g_category_index = g_num_builtin_categories;
|
||||
|
||||
void TracingController::Initialize(TraceBuffer* trace_buffer) {
|
||||
trace_buffer_.reset(trace_buffer);
|
||||
}
|
||||
|
||||
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,
|
||||
const uint64_t* arg_values, unsigned int flags) {
|
||||
uint64_t handle;
|
||||
TraceObject* trace_object = trace_buffer_->AddTraceEvent(&handle);
|
||||
if (trace_object) {
|
||||
trace_object->Initialize(phase, category_enabled_flag, name, scope, id,
|
||||
bind_id, num_args, arg_names, arg_types,
|
||||
arg_values, flags);
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
void TracingController::UpdateTraceEventDuration(
|
||||
const uint8_t* category_enabled_flag, const char* name, uint64_t handle) {
|
||||
TraceObject* trace_object = trace_buffer_->GetEventByHandle(handle);
|
||||
if (!trace_object) return;
|
||||
trace_object->UpdateDuration();
|
||||
}
|
||||
|
||||
const uint8_t* TracingController::GetCategoryGroupEnabled(
|
||||
const char* category_group) {
|
||||
if (!trace_buffer_) {
|
||||
DCHECK(!g_category_group_enabled[g_category_already_shutdown]);
|
||||
return &g_category_group_enabled[g_category_already_shutdown];
|
||||
}
|
||||
return GetCategoryGroupEnabledInternal(category_group);
|
||||
}
|
||||
|
||||
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 +
|
||||
MAX_CATEGORY_GROUPS));
|
||||
uintptr_t category_index =
|
||||
(category_ptr - category_begin) / sizeof(g_category_group_enabled[0]);
|
||||
return g_category_groups[category_index];
|
||||
}
|
||||
|
||||
void TracingController::StartTracing(TraceConfig* trace_config) {
|
||||
trace_config_.reset(trace_config);
|
||||
mode_ = RECORDING_MODE;
|
||||
UpdateCategoryGroupEnabledFlags();
|
||||
}
|
||||
|
||||
void TracingController::StopTracing() {
|
||||
mode_ = DISABLED;
|
||||
UpdateCategoryGroupEnabledFlags();
|
||||
trace_buffer_->Flush();
|
||||
}
|
||||
|
||||
void TracingController::UpdateCategoryGroupEnabledFlag(size_t category_index) {
|
||||
unsigned char enabled_flag = 0;
|
||||
const char* category_group = g_category_groups[category_index];
|
||||
if (mode_ == RECORDING_MODE &&
|
||||
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.
|
||||
if (mode_ == RECORDING_MODE && !strcmp(category_group, "__metadata")) {
|
||||
enabled_flag |= ENABLED_FOR_RECORDING;
|
||||
}
|
||||
|
||||
g_category_group_enabled[category_index] = enabled_flag;
|
||||
}
|
||||
|
||||
void TracingController::UpdateCategoryGroupEnabledFlags() {
|
||||
size_t category_index = base::NoBarrier_Load(&g_category_index);
|
||||
for (size_t i = 0; i < category_index; i++) UpdateCategoryGroupEnabledFlag(i);
|
||||
}
|
||||
|
||||
const uint8_t* TracingController::GetCategoryGroupEnabledInternal(
|
||||
const char* category_group) {
|
||||
// Check that category groups does not contain double quote
|
||||
DCHECK(!strchr(category_group, '"'));
|
||||
|
||||
// The g_category_groups is append only, avoid using a lock for the fast path.
|
||||
size_t current_category_index = v8::base::Acquire_Load(&g_category_index);
|
||||
|
||||
// Search for pre-existing category group.
|
||||
for (size_t i = 0; i < current_category_index; ++i) {
|
||||
if (strcmp(g_category_groups[i], category_group) == 0) {
|
||||
return &g_category_group_enabled[i];
|
||||
}
|
||||
}
|
||||
|
||||
unsigned char* category_group_enabled = NULL;
|
||||
size_t category_index = base::Acquire_Load(&g_category_index);
|
||||
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.
|
||||
DCHECK(category_index < MAX_CATEGORY_GROUPS);
|
||||
if (category_index < MAX_CATEGORY_GROUPS) {
|
||||
// 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;
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
@ -2010,10 +2010,18 @@
|
||||
],
|
||||
'sources': [
|
||||
'../include/libplatform/libplatform.h',
|
||||
'../include/libplatform/v8-tracing.h',
|
||||
'libplatform/default-platform.cc',
|
||||
'libplatform/default-platform.h',
|
||||
'libplatform/task-queue.cc',
|
||||
'libplatform/task-queue.h',
|
||||
'libplatform/tracing/trace-buffer.cc',
|
||||
'libplatform/tracing/trace-buffer.h',
|
||||
'libplatform/tracing/trace-config.cc',
|
||||
'libplatform/tracing/trace-object.cc',
|
||||
'libplatform/tracing/trace-writer.cc',
|
||||
'libplatform/tracing/trace-writer.h',
|
||||
'libplatform/tracing/tracing-controller.cc',
|
||||
'libplatform/worker-thread.cc',
|
||||
'libplatform/worker-thread.h',
|
||||
],
|
||||
|
@ -102,6 +102,7 @@
|
||||
'heap/test-mark-compact.cc',
|
||||
'heap/test-page-promotion.cc',
|
||||
'heap/test-spaces.cc',
|
||||
'libplatform/test-tracing.cc',
|
||||
'libsampler/test-sampler.cc',
|
||||
'print-extension.cc',
|
||||
'profiler-extension.cc',
|
||||
|
186
test/cctest/libplatform/test-tracing.cc
Normal file
186
test/cctest/libplatform/test-tracing.cc
Normal file
@ -0,0 +1,186 @@
|
||||
// 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>
|
||||
|
||||
#include "include/libplatform/v8-tracing.h"
|
||||
#include "src/tracing/trace-event.h"
|
||||
#include "test/cctest/cctest.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace platform {
|
||||
namespace tracing {
|
||||
|
||||
TEST(TestTraceConfig) {
|
||||
LocalContext env;
|
||||
TraceConfig* trace_config = new TraceConfig();
|
||||
trace_config->EnableSampling();
|
||||
trace_config->AddIncludedCategory("v8");
|
||||
trace_config->AddIncludedCategory(TRACE_DISABLED_BY_DEFAULT("v8.runtime"));
|
||||
trace_config->AddExcludedCategory("v8.cpu_profile");
|
||||
|
||||
CHECK_EQ(trace_config->IsSamplingEnabled(), true);
|
||||
CHECK_EQ(trace_config->IsSystraceEnabled(), false);
|
||||
CHECK_EQ(trace_config->IsArgumentFilterEnabled(), false);
|
||||
CHECK_EQ(trace_config->IsCategoryGroupEnabled("v8"), true);
|
||||
CHECK_EQ(trace_config->IsCategoryGroupEnabled("v8.cpu_profile"), false);
|
||||
CHECK_EQ(trace_config->IsCategoryGroupEnabled("v8.cpu_profile.hires"), false);
|
||||
CHECK_EQ(trace_config->IsCategoryGroupEnabled(
|
||||
TRACE_DISABLED_BY_DEFAULT("v8.runtime")),
|
||||
true);
|
||||
delete trace_config;
|
||||
}
|
||||
|
||||
TEST(TestTraceObject) {
|
||||
TraceObject trace_object;
|
||||
uint8_t category_enabled_flag = 41;
|
||||
trace_object.Initialize('X', &category_enabled_flag, "Test.Trace",
|
||||
"Test.Scope", 42, 123, 0, NULL, NULL, NULL, 0);
|
||||
CHECK_EQ('X', trace_object.phase());
|
||||
CHECK_EQ(category_enabled_flag, *trace_object.category_enabled_flag());
|
||||
CHECK_EQ("Test.Trace", trace_object.name());
|
||||
CHECK_EQ("Test.Scope", trace_object.scope());
|
||||
CHECK_EQ(0, trace_object.duration());
|
||||
CHECK_EQ(0, trace_object.cpu_duration());
|
||||
}
|
||||
|
||||
class MockTraceWriter : public TraceWriter {
|
||||
public:
|
||||
void AppendTraceEvent(TraceObject* trace_event) override {
|
||||
events_.push_back(trace_event->name());
|
||||
}
|
||||
|
||||
void Flush() override {}
|
||||
|
||||
std::vector<std::string> events() { return events_; }
|
||||
|
||||
private:
|
||||
std::vector<std::string> events_;
|
||||
};
|
||||
|
||||
TEST(TestTraceBufferRingBuffer) {
|
||||
// We should be able to add kChunkSize * 2 + 1 trace events.
|
||||
const int HANDLES_COUNT = TraceBufferChunk::kChunkSize * 2 + 1;
|
||||
MockTraceWriter* writer = new MockTraceWriter();
|
||||
TraceBuffer* ring_buffer =
|
||||
TraceBuffer::CreateTraceBufferRingBuffer(2, writer);
|
||||
std::string names[HANDLES_COUNT];
|
||||
for (int i = 0; i < HANDLES_COUNT; ++i) {
|
||||
names[i] = "Test.EventNo" + std::to_string(i);
|
||||
}
|
||||
|
||||
std::vector<uint64_t> handles(HANDLES_COUNT);
|
||||
uint8_t category_enabled_flag = 41;
|
||||
for (size_t i = 0; i < handles.size(); ++i) {
|
||||
TraceObject* trace_object = ring_buffer->AddTraceEvent(&handles[i]);
|
||||
CHECK_NOT_NULL(trace_object);
|
||||
trace_object->Initialize('X', &category_enabled_flag, names[i].c_str(),
|
||||
"Test.Scope", 42, 123, 0, NULL, NULL, NULL, 0);
|
||||
trace_object = ring_buffer->GetEventByHandle(handles[i]);
|
||||
CHECK_NOT_NULL(trace_object);
|
||||
CHECK_EQ('X', trace_object->phase());
|
||||
CHECK_EQ(names[i].c_str(), trace_object->name());
|
||||
CHECK_EQ(category_enabled_flag, *trace_object->category_enabled_flag());
|
||||
}
|
||||
|
||||
// We should only be able to retrieve the last kChunkSize + 1.
|
||||
for (size_t i = 0; i < TraceBufferChunk::kChunkSize; ++i) {
|
||||
CHECK_NULL(ring_buffer->GetEventByHandle(handles[i]));
|
||||
}
|
||||
|
||||
for (size_t i = TraceBufferChunk::kChunkSize; i < handles.size(); ++i) {
|
||||
TraceObject* trace_object = ring_buffer->GetEventByHandle(handles[i]);
|
||||
CHECK_NOT_NULL(trace_object);
|
||||
// The object properties should be correct.
|
||||
CHECK_EQ('X', trace_object->phase());
|
||||
CHECK_EQ(names[i], std::string(trace_object->name()));
|
||||
CHECK_EQ(category_enabled_flag, *trace_object->category_enabled_flag());
|
||||
}
|
||||
|
||||
// Check Flush(), that the writer wrote the last kChunkSize 1 event names.
|
||||
ring_buffer->Flush();
|
||||
auto events = writer->events();
|
||||
CHECK_EQ(TraceBufferChunk::kChunkSize + 1, events.size());
|
||||
for (size_t i = TraceBufferChunk::kChunkSize; i < handles.size(); ++i) {
|
||||
CHECK_EQ(names[i], events[i - TraceBufferChunk::kChunkSize]);
|
||||
}
|
||||
delete ring_buffer;
|
||||
}
|
||||
|
||||
TEST(TestJSONTraceWriter) {
|
||||
std::ostringstream stream;
|
||||
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
|
||||
v8::Platform* default_platform = v8::platform::CreateDefaultPlatform();
|
||||
i::V8::SetPlatformForTesting(default_platform);
|
||||
// Create a scope for the tracing controller to terminate the trace writer.
|
||||
{
|
||||
TracingController tracing_controller;
|
||||
platform::SetTracingController(default_platform, &tracing_controller);
|
||||
TraceWriter* writer = TraceWriter::CreateJSONTraceWriter(stream);
|
||||
|
||||
TraceBuffer* ring_buffer =
|
||||
TraceBuffer::CreateTraceBufferRingBuffer(1, writer);
|
||||
tracing_controller.Initialize(ring_buffer);
|
||||
TraceConfig* trace_config = new TraceConfig();
|
||||
trace_config->AddIncludedCategory("v8-cat");
|
||||
tracing_controller.StartTracing(trace_config);
|
||||
|
||||
TraceObject trace_object;
|
||||
trace_object.InitializeForTesting(
|
||||
'X', tracing_controller.GetCategoryGroupEnabled("v8-cat"), "Test0",
|
||||
v8::internal::tracing::kGlobalScope, 42, 123, 0, NULL, NULL, NULL, 0,
|
||||
11, 22, 100, 50, 33, 44);
|
||||
writer->AppendTraceEvent(&trace_object);
|
||||
trace_object.InitializeForTesting(
|
||||
'Y', tracing_controller.GetCategoryGroupEnabled("v8-cat"), "Test1",
|
||||
v8::internal::tracing::kGlobalScope, 43, 456, 0, NULL, NULL, NULL, 0,
|
||||
55, 66, 110, 55, 77, 88);
|
||||
writer->AppendTraceEvent(&trace_object);
|
||||
tracing_controller.StopTracing();
|
||||
}
|
||||
|
||||
std::string trace_str = stream.str();
|
||||
std::string expected_trace_str =
|
||||
"{\"traceEvents\":[{\"pid\":11,\"tid\":22,\"ts\":100,\"tts\":50,"
|
||||
"\"ph\":\"X\",\"cat\":\"v8-cat\",\"name\":\"Test0\",\"args\":{},"
|
||||
"\"dur\":33,\"tdur\":44},{\"pid\":55,\"tid\":66,\"ts\":110,\"tts\":55,"
|
||||
"\"ph\":\"Y\",\"cat\":\"v8-cat\",\"name\":\"Test1\",\"args\":{},\"dur\":"
|
||||
"77,\"tdur\":88}]}";
|
||||
|
||||
CHECK_EQ(expected_trace_str, trace_str);
|
||||
|
||||
i::V8::SetPlatformForTesting(old_platform);
|
||||
}
|
||||
|
||||
TEST(TestTracingController) {
|
||||
v8::Platform* old_platform = i::V8::GetCurrentPlatform();
|
||||
v8::Platform* default_platform = v8::platform::CreateDefaultPlatform();
|
||||
i::V8::SetPlatformForTesting(default_platform);
|
||||
|
||||
TracingController tracing_controller;
|
||||
platform::SetTracingController(default_platform, &tracing_controller);
|
||||
|
||||
MockTraceWriter* writer = new MockTraceWriter();
|
||||
TraceBuffer* ring_buffer =
|
||||
TraceBuffer::CreateTraceBufferRingBuffer(1, writer);
|
||||
tracing_controller.Initialize(ring_buffer);
|
||||
TraceConfig* trace_config = new TraceConfig();
|
||||
trace_config->AddIncludedCategory("v8");
|
||||
tracing_controller.StartTracing(trace_config);
|
||||
|
||||
TRACE_EVENT0("v8", "v8.Test");
|
||||
// cat category is not included in default config
|
||||
TRACE_EVENT0("cat", "v8.Test2");
|
||||
TRACE_EVENT0("v8", "v8.Test3");
|
||||
tracing_controller.StopTracing();
|
||||
|
||||
CHECK_EQ(2, writer->events().size());
|
||||
CHECK_EQ("v8.Test", writer->events()[0]);
|
||||
CHECK_EQ("v8.Test3", writer->events()[1]);
|
||||
|
||||
i::V8::SetPlatformForTesting(old_platform);
|
||||
}
|
||||
|
||||
} // namespace tracing
|
||||
} // namespace platform
|
||||
} // namespace v8
|
Loading…
Reference in New Issue
Block a user