Upload Kmake

This commit is contained in:
Gorochu
2026-05-26 23:36:42 -07:00
parent ba051b2f74
commit 555ec72358
41615 changed files with 13344630 additions and 1 deletions

251
src/tracing/agent.cc Normal file
View File

@ -0,0 +1,251 @@
#include "tracing/agent.h"
#include <string>
#include "trace_event.h"
#include "tracing/node_trace_buffer.h"
#include "debug_utils-inl.h"
#include "env-inl.h"
namespace node {
namespace tracing {
class Agent::ScopedSuspendTracing {
public:
ScopedSuspendTracing(TracingController* controller, Agent* agent,
bool do_suspend = true)
: controller_(controller), agent_(do_suspend ? agent : nullptr) {
if (do_suspend) {
CHECK(agent_->started_);
controller->StopTracing();
}
}
~ScopedSuspendTracing() {
if (agent_ == nullptr) return;
TraceConfig* config = agent_->CreateTraceConfig();
if (config != nullptr) {
controller_->StartTracing(config);
}
}
private:
TracingController* controller_;
Agent* agent_;
};
namespace {
std::set<std::string> flatten(
const std::unordered_map<int, std::multiset<std::string>>& map) {
std::set<std::string> result;
for (const auto& id_value : map)
result.insert(id_value.second.begin(), id_value.second.end());
return result;
}
} // namespace
using v8::platform::tracing::TraceConfig;
using v8::platform::tracing::TraceWriter;
using std::string;
Agent::Agent() : tracing_controller_(new TracingController()) {
tracing_controller_->Initialize(nullptr);
CHECK_EQ(uv_loop_init(&tracing_loop_), 0);
CHECK_EQ(uv_async_init(&tracing_loop_,
&initialize_writer_async_,
[](uv_async_t* async) {
Agent* agent = ContainerOf(&Agent::initialize_writer_async_, async);
agent->InitializeWritersOnThread();
}), 0);
uv_unref(reinterpret_cast<uv_handle_t*>(&initialize_writer_async_));
}
void Agent::InitializeWritersOnThread() {
Mutex::ScopedLock lock(initialize_writer_mutex_);
while (!to_be_initialized_.empty()) {
AsyncTraceWriter* head = *to_be_initialized_.begin();
head->InitializeOnThread(&tracing_loop_);
to_be_initialized_.erase(head);
}
initialize_writer_condvar_.Broadcast(lock);
}
Agent::~Agent() {
categories_.clear();
writers_.clear();
StopTracing();
uv_close(reinterpret_cast<uv_handle_t*>(&initialize_writer_async_), nullptr);
uv_run(&tracing_loop_, UV_RUN_ONCE);
CheckedUvLoopClose(&tracing_loop_);
}
void Agent::Start() {
if (started_)
return;
NodeTraceBuffer* trace_buffer_ = new NodeTraceBuffer(
NodeTraceBuffer::kBufferChunks, this, &tracing_loop_);
tracing_controller_->Initialize(trace_buffer_);
// This thread should be created *after* async handles are created
// (within NodeTraceWriter and NodeTraceBuffer constructors).
// Otherwise the thread could shut down prematurely.
CHECK_EQ(0, uv_thread_create(&thread_, [](void* arg) {
Agent* agent = static_cast<Agent*>(arg);
uv_run(&agent->tracing_loop_, UV_RUN_DEFAULT);
}, this));
started_ = true;
}
AgentWriterHandle Agent::AddClient(
const std::set<std::string>& categories,
std::unique_ptr<AsyncTraceWriter> writer,
enum UseDefaultCategoryMode mode) {
Start();
const std::set<std::string>* use_categories = &categories;
std::set<std::string> categories_with_default;
if (mode == kUseDefaultCategories) {
categories_with_default.insert(categories.begin(), categories.end());
categories_with_default.insert(categories_[kDefaultHandleId].begin(),
categories_[kDefaultHandleId].end());
use_categories = &categories_with_default;
}
ScopedSuspendTracing suspend(tracing_controller_.get(), this);
int id = next_writer_id_++;
AsyncTraceWriter* raw = writer.get();
writers_[id] = std::move(writer);
categories_[id] = { use_categories->begin(), use_categories->end() };
{
Mutex::ScopedLock lock(initialize_writer_mutex_);
to_be_initialized_.insert(raw);
uv_async_send(&initialize_writer_async_);
while (to_be_initialized_.count(raw) > 0)
initialize_writer_condvar_.Wait(lock);
}
return AgentWriterHandle(this, id);
}
AgentWriterHandle Agent::DefaultHandle() {
return AgentWriterHandle(this, kDefaultHandleId);
}
void Agent::StopTracing() {
if (!started_)
return;
// Perform final Flush on TraceBuffer. We don't want the tracing controller
// to flush the buffer again on destruction of the V8::Platform.
tracing_controller_->StopTracing();
tracing_controller_->Initialize(nullptr);
started_ = false;
// Thread should finish when the tracing loop is stopped.
uv_thread_join(&thread_);
}
void Agent::Disconnect(int client) {
if (client == kDefaultHandleId) return;
{
Mutex::ScopedLock lock(initialize_writer_mutex_);
to_be_initialized_.erase(writers_[client].get());
}
ScopedSuspendTracing suspend(tracing_controller_.get(), this);
writers_.erase(client);
categories_.erase(client);
}
void Agent::Enable(int id, const std::set<std::string>& categories) {
if (categories.empty())
return;
ScopedSuspendTracing suspend(tracing_controller_.get(), this,
id != kDefaultHandleId);
categories_[id].insert(categories.begin(), categories.end());
}
void Agent::Disable(int id, const std::set<std::string>& categories) {
ScopedSuspendTracing suspend(tracing_controller_.get(), this,
id != kDefaultHandleId);
std::multiset<std::string>& writer_categories = categories_[id];
for (const std::string& category : categories) {
auto it = writer_categories.find(category);
if (it != writer_categories.end())
writer_categories.erase(it);
}
}
TraceConfig* Agent::CreateTraceConfig() const {
if (categories_.empty())
return nullptr;
TraceConfig* trace_config = new TraceConfig();
for (const auto& category : flatten(categories_)) {
trace_config->AddIncludedCategory(category.c_str());
}
return trace_config;
}
std::string Agent::GetEnabledCategories() const {
std::string categories;
for (const std::string& category : flatten(categories_)) {
if (!categories.empty())
categories += ',';
categories += category;
}
return categories;
}
void Agent::AppendTraceEvent(TraceObject* trace_event) {
for (const auto& id_writer : writers_)
id_writer.second->AppendTraceEvent(trace_event);
}
void Agent::AddMetadataEvent(std::unique_ptr<TraceObject> event) {
Mutex::ScopedLock lock(metadata_events_mutex_);
metadata_events_.push_back(std::move(event));
}
void Agent::Flush(bool blocking) {
{
Mutex::ScopedLock lock(metadata_events_mutex_);
for (const auto& event : metadata_events_)
AppendTraceEvent(event.get());
}
for (const auto& id_writer : writers_)
id_writer.second->Flush(blocking);
}
void TracingController::AddMetadataEvent(
const unsigned char* category_group_enabled,
const char* name,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const uint64_t* arg_values,
std::unique_ptr<v8::ConvertableToTraceFormat>* convertable_values,
unsigned int flags) {
std::unique_ptr<TraceObject> trace_event(new TraceObject);
trace_event->Initialize(
TRACE_EVENT_PHASE_METADATA, category_group_enabled, name,
node::tracing::kGlobalScope, // scope
node::tracing::kNoId, // id
node::tracing::kNoId, // bind_id
num_args, arg_names, arg_types, arg_values, convertable_values,
TRACE_EVENT_FLAG_NONE,
CurrentTimestampMicroseconds(),
CurrentCpuTimestampMicroseconds());
Agent* node_agent = node::tracing::TraceEventHelper::GetAgent();
if (node_agent != nullptr)
node_agent->AddMetadataEvent(std::move(trace_event));
}
} // namespace tracing
} // namespace node

195
src/tracing/agent.h Normal file
View File

@ -0,0 +1,195 @@
#ifndef SRC_TRACING_AGENT_H_
#define SRC_TRACING_AGENT_H_
#include "libplatform/v8-tracing.h"
#include "uv.h"
#include "util.h"
#include "node_mutex.h"
#include <list>
#include <set>
#include <string>
#include <unordered_map>
namespace v8 {
class ConvertableToTraceFormat;
class TracingController;
} // namespace v8
namespace node {
namespace tracing {
using v8::platform::tracing::TraceConfig;
using v8::platform::tracing::TraceObject;
class Agent;
class AsyncTraceWriter {
public:
virtual ~AsyncTraceWriter() = default;
virtual void AppendTraceEvent(TraceObject* trace_event) = 0;
virtual void Flush(bool blocking) = 0;
virtual void InitializeOnThread(uv_loop_t* loop) {}
};
class TracingController : public v8::platform::tracing::TracingController {
public:
TracingController() : v8::platform::tracing::TracingController() {}
int64_t CurrentTimestampMicroseconds() override {
return uv_hrtime() / 1000;
}
void AddMetadataEvent(
const unsigned char* category_group_enabled,
const char* name,
int num_args,
const char** arg_names,
const unsigned char* arg_types,
const uint64_t* arg_values,
std::unique_ptr<v8::ConvertableToTraceFormat>* convertable_values,
unsigned int flags);
};
class AgentWriterHandle {
public:
inline AgentWriterHandle() = default;
inline ~AgentWriterHandle() { reset(); }
inline AgentWriterHandle(AgentWriterHandle&& other);
inline AgentWriterHandle& operator=(AgentWriterHandle&& other);
inline bool empty() const { return agent_ == nullptr; }
inline void reset();
inline void Enable(const std::set<std::string>& categories);
inline void Disable(const std::set<std::string>& categories);
inline bool IsDefaultHandle();
inline Agent* agent() { return agent_; }
inline v8::TracingController* GetTracingController();
AgentWriterHandle(const AgentWriterHandle& other) = delete;
AgentWriterHandle& operator=(const AgentWriterHandle& other) = delete;
private:
inline AgentWriterHandle(Agent* agent, int id) : agent_(agent), id_(id) {}
Agent* agent_ = nullptr;
int id_ = 0;
friend class Agent;
};
class Agent {
public:
Agent();
~Agent();
TracingController* GetTracingController() {
TracingController* controller = tracing_controller_.get();
CHECK_NOT_NULL(controller);
return controller;
}
enum UseDefaultCategoryMode {
kUseDefaultCategories,
kIgnoreDefaultCategories
};
// Destroying the handle disconnects the client
AgentWriterHandle AddClient(const std::set<std::string>& categories,
std::unique_ptr<AsyncTraceWriter> writer,
enum UseDefaultCategoryMode mode);
// A handle that is only used for managing the default categories
// (which can then implicitly be used through using `USE_DEFAULT_CATEGORIES`
// when adding a client later).
AgentWriterHandle DefaultHandle();
// Returns a comma-separated list of enabled categories.
std::string GetEnabledCategories() const;
// Writes to all writers registered through AddClient().
void AppendTraceEvent(TraceObject* trace_event);
void AddMetadataEvent(std::unique_ptr<TraceObject> event);
// Flushes all writers registered through AddClient().
void Flush(bool blocking);
TraceConfig* CreateTraceConfig() const;
private:
friend class AgentWriterHandle;
void InitializeWritersOnThread();
void Start();
void StopTracing();
void Disconnect(int client);
void Enable(int id, const std::set<std::string>& categories);
void Disable(int id, const std::set<std::string>& categories);
uv_thread_t thread_;
uv_loop_t tracing_loop_;
bool started_ = false;
class ScopedSuspendTracing;
// Each individual Writer has one id.
int next_writer_id_ = 1;
enum { kDefaultHandleId = -1 };
// These maps store the original arguments to AddClient(), by id.
std::unordered_map<int, std::multiset<std::string>> categories_;
std::unordered_map<int, std::unique_ptr<AsyncTraceWriter>> writers_;
std::unique_ptr<TracingController> tracing_controller_;
// Variables related to initializing per-event-loop properties of individual
// writers, such as libuv handles.
Mutex initialize_writer_mutex_;
ConditionVariable initialize_writer_condvar_;
uv_async_t initialize_writer_async_;
std::set<AsyncTraceWriter*> to_be_initialized_;
Mutex metadata_events_mutex_;
std::list<std::unique_ptr<TraceObject>> metadata_events_;
};
void AgentWriterHandle::reset() {
if (agent_ != nullptr)
agent_->Disconnect(id_);
agent_ = nullptr;
}
AgentWriterHandle& AgentWriterHandle::operator=(AgentWriterHandle&& other) {
reset();
agent_ = other.agent_;
id_ = other.id_;
other.agent_ = nullptr;
return *this;
}
AgentWriterHandle::AgentWriterHandle(AgentWriterHandle&& other) {
*this = std::move(other);
}
void AgentWriterHandle::Enable(const std::set<std::string>& categories) {
if (agent_ != nullptr) agent_->Enable(id_, categories);
}
void AgentWriterHandle::Disable(const std::set<std::string>& categories) {
if (agent_ != nullptr) agent_->Disable(id_, categories);
}
bool AgentWriterHandle::IsDefaultHandle() {
return agent_ != nullptr && id_ == Agent::kDefaultHandleId;
}
inline v8::TracingController* AgentWriterHandle::GetTracingController() {
return agent_ != nullptr ? agent_->GetTracingController() : nullptr;
}
} // namespace tracing
} // namespace node
#endif // SRC_TRACING_AGENT_H_

View File

@ -0,0 +1,199 @@
#include "tracing/node_trace_buffer.h"
#include <memory>
#include "util-inl.h"
namespace node {
namespace tracing {
InternalTraceBuffer::InternalTraceBuffer(size_t max_chunks, uint32_t id,
Agent* agent)
: flushing_(false), max_chunks_(max_chunks),
agent_(agent), id_(id) {
chunks_.resize(max_chunks);
}
TraceObject* InternalTraceBuffer::AddTraceEvent(uint64_t* handle) {
Mutex::ScopedLock scoped_lock(mutex_);
// Create new chunk if last chunk is full or there is no chunk.
if (total_chunks_ == 0 || chunks_[total_chunks_ - 1]->IsFull()) {
auto& chunk = chunks_[total_chunks_++];
if (chunk) {
chunk->Reset(current_chunk_seq_++);
} else {
chunk = std::make_unique<TraceBufferChunk>(current_chunk_seq_++);
}
}
auto& chunk = chunks_[total_chunks_ - 1];
size_t event_index;
TraceObject* trace_object = chunk->AddTraceEvent(&event_index);
*handle = MakeHandle(total_chunks_ - 1, chunk->seq(), event_index);
return trace_object;
}
TraceObject* InternalTraceBuffer::GetEventByHandle(uint64_t handle) {
Mutex::ScopedLock scoped_lock(mutex_);
if (handle == 0) {
// A handle value of zero never has a trace event associated with it.
return nullptr;
}
size_t chunk_index, event_index;
uint32_t buffer_id, chunk_seq;
ExtractHandle(handle, &buffer_id, &chunk_index, &chunk_seq, &event_index);
if (buffer_id != id_ || chunk_index >= total_chunks_) {
// Either the chunk belongs to the other buffer, or is outside the current
// range of chunks loaded in memory (the latter being true suggests that
// the chunk has already been flushed and is no longer in memory.)
return nullptr;
}
auto& chunk = chunks_[chunk_index];
if (chunk->seq() != chunk_seq) {
// Chunk is no longer in memory.
return nullptr;
}
return chunk->GetEventAt(event_index);
}
void InternalTraceBuffer::Flush(bool blocking) {
{
Mutex::ScopedLock scoped_lock(mutex_);
if (total_chunks_ > 0) {
flushing_ = true;
for (size_t i = 0; i < total_chunks_; ++i) {
auto& chunk = chunks_[i];
for (size_t j = 0; j < chunk->size(); ++j) {
TraceObject* trace_event = chunk->GetEventAt(j);
// Another thread may have added a trace that is yet to be
// initialized. Skip such traces.
// https://github.com/nodejs/node/issues/21038.
if (trace_event->name()) {
agent_->AppendTraceEvent(trace_event);
}
}
}
total_chunks_ = 0;
flushing_ = false;
}
}
agent_->Flush(blocking);
}
uint64_t InternalTraceBuffer::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) << 1) + id_;
}
void InternalTraceBuffer::ExtractHandle(
uint64_t handle, uint32_t* buffer_id, size_t* chunk_index,
uint32_t* chunk_seq, size_t* event_index) const {
*buffer_id = static_cast<uint32_t>(handle & 0x1);
handle >>= 1;
*chunk_seq = static_cast<uint32_t>(handle / Capacity());
size_t indices = handle % Capacity();
*chunk_index = indices / TraceBufferChunk::kChunkSize;
*event_index = indices % TraceBufferChunk::kChunkSize;
}
NodeTraceBuffer::NodeTraceBuffer(size_t max_chunks,
Agent* agent, uv_loop_t* tracing_loop)
: tracing_loop_(tracing_loop),
buffer1_(max_chunks, 0, agent),
buffer2_(max_chunks, 1, agent) {
current_buf_.store(&buffer1_);
flush_signal_.data = this;
int err = uv_async_init(tracing_loop_, &flush_signal_,
NonBlockingFlushSignalCb);
CHECK_EQ(err, 0);
exit_signal_.data = this;
err = uv_async_init(tracing_loop_, &exit_signal_, ExitSignalCb);
CHECK_EQ(err, 0);
}
NodeTraceBuffer::~NodeTraceBuffer() {
uv_async_send(&exit_signal_);
Mutex::ScopedLock scoped_lock(exit_mutex_);
while (!exited_) {
exit_cond_.Wait(scoped_lock);
}
}
TraceObject* NodeTraceBuffer::AddTraceEvent(uint64_t* handle) {
// If the buffer is full, attempt to perform a flush.
if (!TryLoadAvailableBuffer()) {
// Assign a value of zero as the trace event handle.
// This is equivalent to calling InternalTraceBuffer::MakeHandle(0, 0, 0),
// and will cause GetEventByHandle to return NULL if passed as an argument.
*handle = 0;
return nullptr;
}
return current_buf_.load()->AddTraceEvent(handle);
}
TraceObject* NodeTraceBuffer::GetEventByHandle(uint64_t handle) {
return current_buf_.load()->GetEventByHandle(handle);
}
bool NodeTraceBuffer::Flush() {
buffer1_.Flush(true);
buffer2_.Flush(true);
return true;
}
// Attempts to set current_buf_ such that it references a buffer that can
// write at least one trace event. If both buffers are unavailable this
// method returns false; otherwise it returns true.
bool NodeTraceBuffer::TryLoadAvailableBuffer() {
InternalTraceBuffer* prev_buf = current_buf_.load();
if (prev_buf->IsFull()) {
uv_async_send(&flush_signal_); // trigger flush on a separate thread
InternalTraceBuffer* other_buf = prev_buf == &buffer1_ ?
&buffer2_ : &buffer1_;
if (!other_buf->IsFull()) {
current_buf_.store(other_buf);
} else {
return false;
}
}
return true;
}
// static
void NodeTraceBuffer::NonBlockingFlushSignalCb(uv_async_t* signal) {
NodeTraceBuffer* buffer = static_cast<NodeTraceBuffer*>(signal->data);
if (buffer->buffer1_.IsFull() && !buffer->buffer1_.IsFlushing()) {
buffer->buffer1_.Flush(false);
}
if (buffer->buffer2_.IsFull() && !buffer->buffer2_.IsFlushing()) {
buffer->buffer2_.Flush(false);
}
}
// static
void NodeTraceBuffer::ExitSignalCb(uv_async_t* signal) {
NodeTraceBuffer* buffer =
ContainerOf(&NodeTraceBuffer::exit_signal_, signal);
// Close both flush_signal_ and exit_signal_.
uv_close(reinterpret_cast<uv_handle_t*>(&buffer->flush_signal_),
[](uv_handle_t* signal) {
NodeTraceBuffer* buffer =
ContainerOf(&NodeTraceBuffer::flush_signal_,
reinterpret_cast<uv_async_t*>(signal));
uv_close(reinterpret_cast<uv_handle_t*>(&buffer->exit_signal_),
[](uv_handle_t* signal) {
NodeTraceBuffer* buffer =
ContainerOf(&NodeTraceBuffer::exit_signal_,
reinterpret_cast<uv_async_t*>(signal));
Mutex::ScopedLock scoped_lock(buffer->exit_mutex_);
buffer->exited_ = true;
buffer->exit_cond_.Signal(scoped_lock);
});
});
}
} // namespace tracing
} // namespace node

View File

@ -0,0 +1,83 @@
#ifndef SRC_TRACING_NODE_TRACE_BUFFER_H_
#define SRC_TRACING_NODE_TRACE_BUFFER_H_
#include "tracing/agent.h"
#include "node_mutex.h"
#include "libplatform/v8-tracing.h"
#include <atomic>
namespace node {
namespace tracing {
using v8::platform::tracing::TraceBuffer;
using v8::platform::tracing::TraceBufferChunk;
using v8::platform::tracing::TraceObject;
// forward declaration
class NodeTraceBuffer;
class InternalTraceBuffer {
public:
InternalTraceBuffer(size_t max_chunks, uint32_t id, Agent* agent);
TraceObject* AddTraceEvent(uint64_t* handle);
TraceObject* GetEventByHandle(uint64_t handle);
void Flush(bool blocking);
bool IsFull() const {
return total_chunks_ == max_chunks_ && chunks_[total_chunks_ - 1]->IsFull();
}
bool IsFlushing() const {
return flushing_;
}
private:
uint64_t MakeHandle(size_t chunk_index, uint32_t chunk_seq,
size_t event_index) const;
void ExtractHandle(uint64_t handle, uint32_t* buffer_id, size_t* chunk_index,
uint32_t* chunk_seq, size_t* event_index) const;
size_t Capacity() const { return max_chunks_ * TraceBufferChunk::kChunkSize; }
Mutex mutex_;
bool flushing_;
size_t max_chunks_;
Agent* agent_;
std::vector<std::unique_ptr<TraceBufferChunk>> chunks_;
size_t total_chunks_ = 0;
uint32_t current_chunk_seq_ = 1;
uint32_t id_;
};
class NodeTraceBuffer : public TraceBuffer {
public:
NodeTraceBuffer(size_t max_chunks, Agent* agent, uv_loop_t* tracing_loop);
~NodeTraceBuffer() override;
TraceObject* AddTraceEvent(uint64_t* handle) override;
TraceObject* GetEventByHandle(uint64_t handle) override;
bool Flush() override;
static const size_t kBufferChunks = 1024;
private:
bool TryLoadAvailableBuffer();
static void NonBlockingFlushSignalCb(uv_async_t* signal);
static void ExitSignalCb(uv_async_t* signal);
uv_loop_t* tracing_loop_;
uv_async_t flush_signal_;
uv_async_t exit_signal_;
bool exited_ = false;
// Used exclusively for exit logic.
Mutex exit_mutex_;
// Used to wait until async handles have been closed.
ConditionVariable exit_cond_;
std::atomic<InternalTraceBuffer*> current_buf_;
InternalTraceBuffer buffer1_;
InternalTraceBuffer buffer2_;
};
} // namespace tracing
} // namespace node
#endif // SRC_TRACING_NODE_TRACE_BUFFER_H_

View File

@ -0,0 +1,240 @@
#include "tracing/node_trace_writer.h"
#include "util-inl.h"
#include <fcntl.h>
#include <cstring>
namespace node {
namespace tracing {
NodeTraceWriter::NodeTraceWriter(const std::string& log_file_pattern)
: log_file_pattern_(log_file_pattern) {}
void NodeTraceWriter::InitializeOnThread(uv_loop_t* loop) {
CHECK_NULL(tracing_loop_);
tracing_loop_ = loop;
flush_signal_.data = this;
int err = uv_async_init(tracing_loop_, &flush_signal_,
[](uv_async_t* signal) {
NodeTraceWriter* trace_writer =
ContainerOf(&NodeTraceWriter::flush_signal_, signal);
trace_writer->FlushPrivate();
});
CHECK_EQ(err, 0);
exit_signal_.data = this;
err = uv_async_init(tracing_loop_, &exit_signal_, ExitSignalCb);
CHECK_EQ(err, 0);
}
void NodeTraceWriter::WriteSuffix() {
// If our final log file has traces, then end the file appropriately.
// This means that if no trace events are recorded, then no trace file is
// produced.
bool should_flush = false;
{
Mutex::ScopedLock scoped_lock(stream_mutex_);
if (total_traces_ > 0) {
total_traces_ = kTracesPerFile; // Act as if we reached the file limit.
should_flush = true;
}
}
if (should_flush) {
Flush(true);
}
}
NodeTraceWriter::~NodeTraceWriter() {
WriteSuffix();
uv_fs_t req;
if (fd_ != -1) {
CHECK_EQ(0, uv_fs_close(nullptr, &req, fd_, nullptr));
uv_fs_req_cleanup(&req);
}
uv_async_send(&exit_signal_);
Mutex::ScopedLock scoped_lock(request_mutex_);
while (!exited_) {
exit_cond_.Wait(scoped_lock);
}
}
void replace_substring(std::string* target,
const std::string& search,
const std::string& insert) {
size_t pos = target->find(search);
for (; pos != std::string::npos; pos = target->find(search, pos)) {
target->replace(pos, search.size(), insert);
pos += insert.size();
}
}
void NodeTraceWriter::OpenNewFileForStreaming() {
++file_num_;
uv_fs_t req;
// Evaluate a JS-style template string, it accepts the values ${pid} and
// ${rotation}
std::string filepath(log_file_pattern_);
replace_substring(&filepath, "${pid}", std::to_string(uv_os_getpid()));
replace_substring(&filepath, "${rotation}", std::to_string(file_num_));
if (fd_ != -1) {
CHECK_EQ(uv_fs_close(nullptr, &req, fd_, nullptr), 0);
uv_fs_req_cleanup(&req);
}
fd_ = uv_fs_open(nullptr, &req, filepath.c_str(),
O_CREAT | O_WRONLY | O_TRUNC, 0644, nullptr);
uv_fs_req_cleanup(&req);
if (fd_ < 0) {
fprintf(stderr, "Could not open trace file %s: %s\n",
filepath.c_str(),
uv_strerror(fd_));
fd_ = -1;
}
}
void NodeTraceWriter::AppendTraceEvent(TraceObject* trace_event) {
Mutex::ScopedLock scoped_lock(stream_mutex_);
// If this is the first trace event, open a new file for streaming.
if (total_traces_ == 0) {
OpenNewFileForStreaming();
// Constructing a new JSONTraceWriter object appends "{\"traceEvents\":["
// to stream_.
// In other words, the constructor initializes the serialization stream
// to a state where we can start writing trace events to it.
// Repeatedly constructing and destroying json_trace_writer_ allows
// us to use V8's JSON writer instead of implementing our own.
json_trace_writer_.reset(TraceWriter::CreateJSONTraceWriter(stream_));
}
++total_traces_;
json_trace_writer_->AppendTraceEvent(trace_event);
}
void NodeTraceWriter::FlushPrivate() {
std::string str;
int highest_request_id;
{
Mutex::ScopedLock stream_scoped_lock(stream_mutex_);
if (total_traces_ >= kTracesPerFile) {
total_traces_ = 0;
// Destroying the member JSONTraceWriter object appends "]}" to
// stream_ - in other words, ending a JSON file.
json_trace_writer_.reset();
}
// str() makes a copy of the contents of the stream.
str = stream_.str();
stream_.str("");
stream_.clear();
}
{
Mutex::ScopedLock request_scoped_lock(request_mutex_);
highest_request_id = num_write_requests_;
}
WriteToFile(std::move(str), highest_request_id);
}
void NodeTraceWriter::Flush(bool blocking) {
Mutex::ScopedLock scoped_lock(request_mutex_);
{
// We need to lock the mutexes here in a nested fashion; stream_mutex_
// protects json_trace_writer_, and without request_mutex_ there might be
// a time window in which the stream state changes?
Mutex::ScopedLock stream_mutex_lock(stream_mutex_);
if (!json_trace_writer_)
return;
}
int request_id = ++num_write_requests_;
int err = uv_async_send(&flush_signal_);
CHECK_EQ(err, 0);
if (blocking) {
// Wait until data associated with this request id has been written to disk.
// This guarantees that data from all earlier requests have also been
// written.
while (request_id > highest_request_id_completed_) {
request_cond_.Wait(scoped_lock);
}
}
}
void NodeTraceWriter::WriteToFile(std::string&& str, int highest_request_id) {
if (fd_ == -1) return;
uv_buf_t buf = uv_buf_init(nullptr, 0);
{
Mutex::ScopedLock lock(request_mutex_);
write_req_queue_.emplace(WriteRequest {
std::move(str), highest_request_id
});
if (write_req_queue_.size() == 1) {
buf = uv_buf_init(
const_cast<char*>(write_req_queue_.front().str.c_str()),
write_req_queue_.front().str.length());
}
}
// Only one write request for the same file descriptor should be active at
// a time.
if (buf.base != nullptr && fd_ != -1) {
StartWrite(buf);
}
}
void NodeTraceWriter::StartWrite(uv_buf_t buf) {
int err = uv_fs_write(
tracing_loop_, &write_req_, fd_, &buf, 1, -1,
[](uv_fs_t* req) {
NodeTraceWriter* writer =
ContainerOf(&NodeTraceWriter::write_req_, req);
writer->AfterWrite();
});
CHECK_EQ(err, 0);
}
void NodeTraceWriter::AfterWrite() {
CHECK_GE(write_req_.result, 0);
uv_fs_req_cleanup(&write_req_);
uv_buf_t buf = uv_buf_init(nullptr, 0);
{
Mutex::ScopedLock scoped_lock(request_mutex_);
int highest_request_id = write_req_queue_.front().highest_request_id;
write_req_queue_.pop();
highest_request_id_completed_ = highest_request_id;
request_cond_.Broadcast(scoped_lock);
if (!write_req_queue_.empty()) {
buf = uv_buf_init(
const_cast<char*>(write_req_queue_.front().str.c_str()),
write_req_queue_.front().str.length());
}
}
if (buf.base != nullptr && fd_ != -1) {
StartWrite(buf);
}
}
// static
void NodeTraceWriter::ExitSignalCb(uv_async_t* signal) {
NodeTraceWriter* trace_writer =
ContainerOf(&NodeTraceWriter::exit_signal_, signal);
// Close both flush_signal_ and exit_signal_.
uv_close(reinterpret_cast<uv_handle_t*>(&trace_writer->flush_signal_),
[](uv_handle_t* signal) {
NodeTraceWriter* trace_writer =
ContainerOf(&NodeTraceWriter::flush_signal_,
reinterpret_cast<uv_async_t*>(signal));
uv_close(
reinterpret_cast<uv_handle_t*>(&trace_writer->exit_signal_),
[](uv_handle_t* signal) {
NodeTraceWriter* trace_writer =
ContainerOf(&NodeTraceWriter::exit_signal_,
reinterpret_cast<uv_async_t*>(signal));
Mutex::ScopedLock scoped_lock(trace_writer->request_mutex_);
trace_writer->exited_ = true;
trace_writer->exit_cond_.Signal(scoped_lock);
});
});
}
} // namespace tracing
} // namespace node

View File

@ -0,0 +1,75 @@
#ifndef SRC_TRACING_NODE_TRACE_WRITER_H_
#define SRC_TRACING_NODE_TRACE_WRITER_H_
#include <sstream>
#include <queue>
#include "libplatform/v8-tracing.h"
#include "tracing/agent.h"
#include "uv.h"
namespace node {
namespace tracing {
using v8::platform::tracing::TraceObject;
using v8::platform::tracing::TraceWriter;
class NodeTraceWriter : public AsyncTraceWriter {
public:
explicit NodeTraceWriter(const std::string& log_file_pattern);
~NodeTraceWriter() override;
void InitializeOnThread(uv_loop_t* loop) override;
void AppendTraceEvent(TraceObject* trace_event) override;
void Flush(bool blocking) override;
static const int kTracesPerFile = 1 << 19;
private:
struct WriteRequest {
std::string str;
int highest_request_id;
};
void AfterWrite();
void StartWrite(uv_buf_t buf);
void OpenNewFileForStreaming();
void WriteToFile(std::string&& str, int highest_request_id);
void WriteSuffix();
void FlushPrivate();
static void ExitSignalCb(uv_async_t* signal);
uv_loop_t* tracing_loop_ = nullptr;
// Triggers callback to initiate writing the contents of stream_ to disk.
uv_async_t flush_signal_;
// Triggers callback to close async objects, ending the tracing thread.
uv_async_t exit_signal_;
// Prevents concurrent R/W on state related to serialized trace data
// before it's written to disk, namely stream_ and total_traces_
// as well as json_trace_writer_.
Mutex stream_mutex_;
// Prevents concurrent R/W on state related to write requests.
// If both mutexes are locked, request_mutex_ has to be locked first.
Mutex request_mutex_;
// Allows blocking calls to Flush() to wait on a condition for
// trace events to be written to disk.
ConditionVariable request_cond_;
// Used to wait until async handles have been closed.
ConditionVariable exit_cond_;
int fd_ = -1;
uv_fs_t write_req_;
std::queue<WriteRequest> write_req_queue_;
int num_write_requests_ = 0;
int highest_request_id_completed_ = 0;
int total_traces_ = 0;
int file_num_ = 0;
std::string log_file_pattern_;
std::ostringstream stream_;
std::unique_ptr<TraceWriter> json_trace_writer_;
bool exited_ = false;
};
} // namespace tracing
} // namespace node
#endif // SRC_TRACING_NODE_TRACE_WRITER_H_

View File

@ -0,0 +1,42 @@
#include "tracing/trace_event.h"
#include "node.h"
namespace node {
namespace tracing {
Agent* g_agent = nullptr;
v8::TracingController* g_controller = nullptr;
void TraceEventHelper::SetAgent(Agent* agent) {
if (agent) {
g_agent = agent;
g_controller = agent->GetTracingController();
} else {
g_agent = nullptr;
g_controller = nullptr;
}
}
Agent* TraceEventHelper::GetAgent() {
return g_agent;
}
v8::TracingController* TraceEventHelper::GetTracingController() {
return g_controller;
}
void TraceEventHelper::SetTracingController(v8::TracingController* controller) {
g_controller = controller;
}
} // namespace tracing
v8::TracingController* GetTracingController() {
return tracing::TraceEventHelper::GetTracingController();
}
void SetTracingController(v8::TracingController* controller) {
tracing::TraceEventHelper::SetTracingController(controller);
}
} // namespace node

722
src/tracing/trace_event.h Normal file
View File

@ -0,0 +1,722 @@
// Copyright 2015 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_TRACING_TRACE_EVENT_H_
#define SRC_TRACING_TRACE_EVENT_H_
#include "v8-platform.h"
#include "tracing/agent.h"
#include "trace_event_common.h"
#include <atomic>
// This header file defines implementation details of how the trace macros in
// trace_event_common.h collect and store trace events. Anything not
// implementation-specific should go in trace_macros_common.h instead of here.
// The pointer returned from GetCategoryGroupEnabled() 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 trace_log.h in
// chromium.
enum CategoryGroupEnabledFlags {
// Category group enabled for the recording mode.
kEnabledForRecording_CategoryGroupEnabledFlags = 1 << 0,
// Category group enabled by SetEventCallbackEnabled().
kEnabledForEventCallback_CategoryGroupEnabledFlags = 1 << 2,
// Category group enabled to export events to ETW.
kEnabledForETWExport_CategoryGroupEnabledFlags = 1 << 3,
};
// By default, const char* argument values are assumed to have long-lived scope
// and will not be copied. Use this macro to force a const char* to be copied.
#define TRACE_STR_COPY(str) node::tracing::TraceStringWithCopy(str)
// By default, uint64 ID argument values are not mangled with the Process ID in
// TRACE_EVENT_ASYNC macros. Use this macro to force Process ID mangling.
#define TRACE_ID_MANGLE(id) node::tracing::TraceID::ForceMangle(id)
// By default, pointers are mangled with the Process ID in TRACE_EVENT_ASYNC
// macros. Use this macro to prevent Process ID mangling.
#define TRACE_ID_DONT_MANGLE(id) node::tracing::TraceID::DontMangle(id)
// By default, trace IDs are eventually converted to a single 64-bit number. Use
// this macro to add a scope string.
#define TRACE_ID_WITH_SCOPE(scope, id) \
trace_event_internal::TraceID::WithScope(scope, id)
#define INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE() \
*INTERNAL_TRACE_EVENT_UID(category_group_enabled) & \
(kEnabledForRecording_CategoryGroupEnabledFlags | \
kEnabledForEventCallback_CategoryGroupEnabledFlags)
// The following macro has no implementation, but it needs to exist since
// it gets called from scoped trace events. It cannot call UNIMPLEMENTED()
// since an empty implementation is a valid one.
#define INTERNAL_TRACE_MEMORY(category, name)
////////////////////////////////////////////////////////////////////////////////
// Implementation specific tracing API definitions.
// Get a pointer to the enabled state of the given trace category. Only
// long-lived literal strings should be given as the category group. The
// returned pointer can be held permanently in a local static for example. If
// the unsigned char is non-zero, tracing is enabled. If tracing is enabled,
// TRACE_EVENT_API_ADD_TRACE_EVENT can be called. It's OK if tracing is disabled
// between the load of the tracing state and the call to
// TRACE_EVENT_API_ADD_TRACE_EVENT, because this flag only provides an early out
// for best performance when tracing is disabled.
// const uint8_t*
// TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(const char* category_group)
#define TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED \
node::tracing::TraceEventHelper::GetCategoryGroupEnabled
// Get the number of times traces have been recorded. This is used to implement
// the TRACE_EVENT_IS_NEW_TRACE facility.
// unsigned int TRACE_EVENT_API_GET_NUM_TRACES_RECORDED()
#define TRACE_EVENT_API_GET_NUM_TRACES_RECORDED UNIMPLEMENTED()
// Add a trace event to the platform tracing system.
// uint64_t TRACE_EVENT_API_ADD_TRACE_EVENT(
// char phase,
// const uint8_t* category_group_enabled,
// 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)
#define TRACE_EVENT_API_ADD_TRACE_EVENT node::tracing::AddTraceEventImpl
// Add a trace event to the platform tracing system.
// uint64_t TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP(
// char phase,
// const uint8_t* category_group_enabled,
// 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,
// int64_t timestamp)
#define TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP \
node::tracing::AddTraceEventWithTimestampImpl
// Set the duration field of a COMPLETE trace event.
// void TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(
// const uint8_t* category_group_enabled,
// const char* name,
// uint64_t id)
#define TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION \
if (auto controller = \
node::tracing::TraceEventHelper::GetTracingController()) \
controller->UpdateTraceEventDuration
// Adds a metadata event to the trace log. The |AppendValueAsTraceFormat| method
// on the convertable value will be called at flush time.
// TRACE_EVENT_API_ADD_METADATA_EVENT(
// const unsigned char* category_group_enabled,
// const char* event_name,
// const char* arg_name,
// std::unique_ptr<ConvertableToTraceFormat> arg_value)
#define TRACE_EVENT_API_ADD_METADATA_EVENT node::tracing::AddMetadataEvent
// Defines atomic operations used internally by the tracing system.
#define TRACE_EVENT_API_ATOMIC_WORD std::atomic<intptr_t>
#define TRACE_EVENT_API_ATOMIC_WORD_VALUE intptr_t
#define TRACE_EVENT_API_ATOMIC_LOAD(var) (var).load()
#define TRACE_EVENT_API_ATOMIC_STORE(var, value) (var).store(value)
////////////////////////////////////////////////////////////////////////////////
// Implementation detail: trace event macros create temporary variables
// to keep instrumentation overhead low. These macros give each temporary
// variable a unique name based on the line number to prevent name collisions.
#define INTERNAL_TRACE_EVENT_UID3(a, b) trace_event_unique_##a##b
#define INTERNAL_TRACE_EVENT_UID2(a, b) INTERNAL_TRACE_EVENT_UID3(a, b)
#define INTERNAL_TRACE_EVENT_UID(name_prefix) \
INTERNAL_TRACE_EVENT_UID2(name_prefix, __LINE__)
// Implementation detail: internal macro to create static category.
// No barriers are needed, because this code is designed to operate safely
// even when the unsigned char* points to garbage data (which may be the case
// on processors without cache coherency).
// TODO(fmeawad): This implementation contradicts that we can have a different
// configuration for each isolate,
// https://code.google.com/p/v8/issues/detail?id=4563
#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( \
category_group, atomic, category_group_enabled) \
category_group_enabled = \
reinterpret_cast<const uint8_t*>(TRACE_EVENT_API_ATOMIC_LOAD(atomic)); \
if (!category_group_enabled) { \
category_group_enabled = \
TRACE_EVENT_API_GET_CATEGORY_GROUP_ENABLED(category_group); \
TRACE_EVENT_API_ATOMIC_STORE( \
atomic, reinterpret_cast<TRACE_EVENT_API_ATOMIC_WORD_VALUE>( \
category_group_enabled)); \
}
#define INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group) \
static TRACE_EVENT_API_ATOMIC_WORD INTERNAL_TRACE_EVENT_UID(atomic) {0}; \
const uint8_t* INTERNAL_TRACE_EVENT_UID(category_group_enabled); \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO_CUSTOM_VARIABLES( \
category_group, INTERNAL_TRACE_EVENT_UID(atomic), \
INTERNAL_TRACE_EVENT_UID(category_group_enabled));
// Implementation detail: internal macro to create static category and add
// event if the category is enabled.
#define INTERNAL_TRACE_EVENT_ADD(phase, category_group, name, flags, ...) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
node::tracing::AddTraceEvent( \
phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
node::tracing::kGlobalScope, node::tracing::kNoId, \
node::tracing::kNoId, flags, ##__VA_ARGS__); \
} \
} while (0)
// Implementation detail: internal macro to create static category and add begin
// event if the category is enabled. Also adds the end event when the scope
// ends.
#define INTERNAL_TRACE_EVENT_ADD_SCOPED(category_group, name, ...) \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
node::tracing::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \
if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
uint64_t h = node::tracing::AddTraceEvent( \
TRACE_EVENT_PHASE_COMPLETE, \
INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
node::tracing::kGlobalScope, node::tracing::kNoId, \
node::tracing::kNoId, TRACE_EVENT_FLAG_NONE, ##__VA_ARGS__); \
INTERNAL_TRACE_EVENT_UID(tracer) \
.Initialize(INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
h); \
}
#define INTERNAL_TRACE_EVENT_ADD_SCOPED_WITH_FLOW(category_group, name, \
bind_id, flow_flags, ...) \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
node::tracing::ScopedTracer INTERNAL_TRACE_EVENT_UID(tracer); \
if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
unsigned int trace_event_flags = flow_flags; \
node::tracing::TraceID trace_event_bind_id(bind_id, \
&trace_event_flags); \
uint64_t h = node::tracing::AddTraceEvent( \
TRACE_EVENT_PHASE_COMPLETE, \
INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
node::tracing::kGlobalScope, node::tracing::kNoId, \
trace_event_bind_id.raw_id(), trace_event_flags, ##__VA_ARGS__); \
INTERNAL_TRACE_EVENT_UID(tracer) \
.Initialize(INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
h); \
}
// Implementation detail: internal macro to create static category and add
// event if the category is enabled.
#define INTERNAL_TRACE_EVENT_ADD_WITH_ID(phase, category_group, name, id, \
flags, ...) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
unsigned int trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
node::tracing::TraceID trace_event_trace_id(id, \
&trace_event_flags); \
node::tracing::AddTraceEvent( \
phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
trace_event_trace_id.scope(), trace_event_trace_id.raw_id(), \
node::tracing::kNoId, trace_event_flags, ##__VA_ARGS__); \
} \
} while (0)
// Adds a trace event with a given timestamp.
#define INTERNAL_TRACE_EVENT_ADD_WITH_TIMESTAMP(phase, category_group, name, \
timestamp, flags, ...) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
node::tracing::AddTraceEventWithTimestamp( \
phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
node::tracing::kGlobalScope, node::tracing::kNoId, \
node::tracing::kNoId, flags, timestamp, ##__VA_ARGS__); \
} \
} while (0)
// Adds a trace event with a given id and timestamp. Not Implemented.
#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_AND_TIMESTAMP( \
phase, category_group, name, id, timestamp, flags, ...) \
UNIMPLEMENTED()
// Adds a trace event with a given id, thread_id, and timestamp. Not
// Implemented.
#define INTERNAL_TRACE_EVENT_ADD_WITH_ID_TID_AND_TIMESTAMP( \
phase, category_group, name, id, thread_id, timestamp, flags, ...) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
unsigned int trace_event_flags = flags | TRACE_EVENT_FLAG_HAS_ID; \
node::tracing::TraceID trace_event_trace_id(id, \
&trace_event_flags); \
node::tracing::AddTraceEventWithTimestamp( \
phase, INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
trace_event_trace_id.scope(), trace_event_trace_id.raw_id(), \
node::tracing::kNoId, trace_event_flags, timestamp, ##__VA_ARGS__);\
} \
} while (0)
#define INTERNAL_TRACE_EVENT_METADATA_ADD(category_group, name, ...) \
do { \
INTERNAL_TRACE_EVENT_GET_CATEGORY_INFO(category_group); \
if (INTERNAL_TRACE_EVENT_CATEGORY_GROUP_ENABLED_FOR_RECORDING_MODE()) { \
TRACE_EVENT_API_ADD_METADATA_EVENT( \
INTERNAL_TRACE_EVENT_UID(category_group_enabled), name, \
##__VA_ARGS__); \
} \
} while(0)
// Enter and leave a context based on the current scope.
#define INTERNAL_TRACE_EVENT_SCOPED_CONTEXT(category_group, name, context) \
struct INTERNAL_TRACE_EVENT_UID(ScopedContext) { \
public: \
INTERNAL_TRACE_EVENT_UID(ScopedContext)(uint64_t cid) : cid_(cid) { \
TRACE_EVENT_ENTER_CONTEXT(category_group, name, cid_); \
} \
~INTERNAL_TRACE_EVENT_UID(ScopedContext)() { \
TRACE_EVENT_LEAVE_CONTEXT(category_group, name, cid_); \
} \
\
private: \
/* Local class friendly DISALLOW_COPY_AND_ASSIGN */ \
INTERNAL_TRACE_EVENT_UID(ScopedContext) \
(const INTERNAL_TRACE_EVENT_UID(ScopedContext)&) {} \
void operator=(const INTERNAL_TRACE_EVENT_UID(ScopedContext)&) {} \
uint64_t cid_; \
}; \
INTERNAL_TRACE_EVENT_UID(ScopedContext) \
INTERNAL_TRACE_EVENT_UID(scoped_context)(context);
namespace node {
namespace tracing {
// Specify these values when the corresponding argument of AddTraceEvent is not
// used.
const int kZeroNumArgs = 0;
const decltype(nullptr) kGlobalScope = nullptr;
const uint64_t kNoId = 0;
class TraceEventHelper {
public:
static v8::TracingController* GetTracingController();
static void SetTracingController(v8::TracingController* controller);
static Agent* GetAgent();
static void SetAgent(Agent* agent);
static inline const uint8_t* GetCategoryGroupEnabled(const char* group) {
v8::TracingController* controller = GetTracingController();
static const uint8_t disabled = 0;
if (controller == nullptr) [[unlikely]] {
return &disabled;
}
return controller->GetCategoryGroupEnabled(group);
}
};
// TraceID encapsulates an ID that can either be an integer or pointer. Pointers
// are by default mangled with the Process ID so that they are unlikely to
// collide when the same pointer is used on different processes.
class TraceID {
public:
class WithScope {
public:
WithScope(const char* scope, uint64_t raw_id)
: scope_(scope), raw_id_(raw_id) {}
uint64_t raw_id() const { return raw_id_; }
const char* scope() const { return scope_; }
private:
const char* scope_ = nullptr;
uint64_t raw_id_;
};
class DontMangle {
public:
explicit DontMangle(const void* raw_id)
: raw_id_(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(raw_id))) {}
explicit DontMangle(uint64_t raw_id) : raw_id_(raw_id) {}
explicit DontMangle(unsigned int raw_id) : raw_id_(raw_id) {}
explicit DontMangle(uint16_t raw_id) : raw_id_(raw_id) {}
explicit DontMangle(unsigned char raw_id) : raw_id_(raw_id) {}
explicit DontMangle(int64_t raw_id)
: raw_id_(static_cast<uint64_t>(raw_id)) {}
explicit DontMangle(int raw_id) : raw_id_(static_cast<uint64_t>(raw_id)) {}
explicit DontMangle(int16_t raw_id)
: raw_id_(static_cast<uint64_t>(raw_id)) {}
explicit DontMangle(signed char raw_id)
: raw_id_(static_cast<uint64_t>(raw_id)) {}
explicit DontMangle(WithScope scoped_id)
: scope_(scoped_id.scope()), raw_id_(scoped_id.raw_id()) {}
const char* scope() const { return scope_; }
uint64_t raw_id() const { return raw_id_; }
private:
const char* scope_ = nullptr;
uint64_t raw_id_;
};
class ForceMangle {
public:
explicit ForceMangle(uint64_t raw_id) : raw_id_(raw_id) {}
explicit ForceMangle(unsigned int raw_id) : raw_id_(raw_id) {}
explicit ForceMangle(uint16_t raw_id) : raw_id_(raw_id) {}
explicit ForceMangle(unsigned char raw_id) : raw_id_(raw_id) {}
explicit ForceMangle(int64_t raw_id)
: raw_id_(static_cast<uint64_t>(raw_id)) {}
explicit ForceMangle(int raw_id) : raw_id_(static_cast<uint64_t>(raw_id)) {}
explicit ForceMangle(int16_t raw_id)
: raw_id_(static_cast<uint64_t>(raw_id)) {}
explicit ForceMangle(signed char raw_id)
: raw_id_(static_cast<uint64_t>(raw_id)) {}
uint64_t raw_id() const { return raw_id_; }
private:
uint64_t raw_id_;
};
TraceID(const void* raw_id, unsigned int* flags)
: raw_id_(static_cast<uint64_t>(reinterpret_cast<uintptr_t>(raw_id))) {
*flags |= TRACE_EVENT_FLAG_MANGLE_ID;
}
TraceID(ForceMangle raw_id, unsigned int* flags) : raw_id_(raw_id.raw_id()) {
*flags |= TRACE_EVENT_FLAG_MANGLE_ID;
}
TraceID(DontMangle maybe_scoped_id, unsigned int* flags)
: scope_(maybe_scoped_id.scope()), raw_id_(maybe_scoped_id.raw_id()) {}
TraceID(uint64_t raw_id, unsigned int* flags) : raw_id_(raw_id) {
(void)flags;
}
TraceID(unsigned int raw_id, unsigned int* flags) : raw_id_(raw_id) {
(void)flags;
}
TraceID(uint16_t raw_id, unsigned int* flags) : raw_id_(raw_id) {
(void)flags;
}
TraceID(unsigned char raw_id, unsigned int* flags) : raw_id_(raw_id) {
(void)flags;
}
TraceID(int64_t raw_id, unsigned int* flags)
: raw_id_(static_cast<uint64_t>(raw_id)) {
(void)flags;
}
TraceID(int raw_id, unsigned int* flags)
: raw_id_(static_cast<uint64_t>(raw_id)) {
(void)flags;
}
TraceID(int16_t raw_id, unsigned int* flags)
: raw_id_(static_cast<uint64_t>(raw_id)) {
(void)flags;
}
TraceID(signed char raw_id, unsigned int* flags)
: raw_id_(static_cast<uint64_t>(raw_id)) {
(void)flags;
}
TraceID(WithScope scoped_id, unsigned int* flags)
: scope_(scoped_id.scope()), raw_id_(scoped_id.raw_id()) {}
uint64_t raw_id() const { return raw_id_; }
const char* scope() const { return scope_; }
private:
const char* scope_ = nullptr;
uint64_t raw_id_;
};
// Simple union to store various types as uint64_t.
union TraceValueUnion {
bool as_bool;
uint64_t as_uint;
int64_t as_int;
double as_double;
const void* as_pointer;
const char* as_string;
};
// Simple container for const char* that should be copied instead of retained.
class TraceStringWithCopy {
public:
explicit TraceStringWithCopy(const char* str) : str_(str) {}
operator const char*() const { return str_; }
private:
const char* str_;
};
static inline uint64_t AddTraceEventImpl(
char phase, const uint8_t* category_group_enabled, 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) {
std::unique_ptr<v8::ConvertableToTraceFormat> arg_convertibles[2];
if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) {
arg_convertibles[0].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
static_cast<intptr_t>(arg_values[0])));
}
if (num_args > 1 && arg_types[1] == TRACE_VALUE_TYPE_CONVERTABLE) {
arg_convertibles[1].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
static_cast<intptr_t>(arg_values[1])));
}
// DCHECK(num_args, 2);
v8::TracingController* controller =
node::tracing::TraceEventHelper::GetTracingController();
if (controller == nullptr) return 0;
return controller->AddTraceEvent(phase, category_group_enabled, name, scope, id,
bind_id, num_args, arg_names, arg_types,
arg_values, arg_convertibles, flags);
}
static V8_INLINE uint64_t AddTraceEventWithTimestampImpl(
char phase, const uint8_t* category_group_enabled, 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, int64_t timestamp) {
std::unique_ptr<v8::ConvertableToTraceFormat> arg_convertibles[2];
if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) {
arg_convertibles[0].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
static_cast<intptr_t>(arg_values[0])));
}
if (num_args > 1 && arg_types[1] == TRACE_VALUE_TYPE_CONVERTABLE) {
arg_convertibles[1].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
static_cast<intptr_t>(arg_values[1])));
}
// DCHECK_LE(num_args, 2);
v8::TracingController* controller =
node::tracing::TraceEventHelper::GetTracingController();
if (controller == nullptr) return 0;
return controller->AddTraceEventWithTimestamp(
phase, category_group_enabled, name, scope, id, bind_id, num_args,
arg_names, arg_types, arg_values, arg_convertibles, flags, timestamp);
}
static V8_INLINE void AddMetadataEventImpl(
const uint8_t* category_group_enabled, const char* name, int32_t num_args,
const char** arg_names, const uint8_t* arg_types,
const uint64_t* arg_values, unsigned int flags) {
std::unique_ptr<v8::ConvertableToTraceFormat> arg_convertibles[2];
if (num_args > 0 && arg_types[0] == TRACE_VALUE_TYPE_CONVERTABLE) {
arg_convertibles[0].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
static_cast<intptr_t>(arg_values[0])));
}
if (num_args > 1 && arg_types[1] == TRACE_VALUE_TYPE_CONVERTABLE) {
arg_convertibles[1].reset(reinterpret_cast<v8::ConvertableToTraceFormat*>(
static_cast<intptr_t>(arg_values[1])));
}
node::tracing::Agent* agent =
node::tracing::TraceEventHelper::GetAgent();
if (agent == nullptr) return;
return agent->GetTracingController()->AddMetadataEvent(
category_group_enabled, name, num_args, arg_names, arg_types, arg_values,
arg_convertibles, flags);
}
// Define SetTraceValue for each allowed type. It stores the type and
// value in the return arguments. This allows this API to avoid declaring any
// structures so that it is portable to third_party libraries.
#define INTERNAL_DECLARE_SET_TRACE_VALUE(actual_type, union_member, \
value_type_id) \
static inline void SetTraceValue(actual_type arg, unsigned char* type, \
uint64_t* value) { \
TraceValueUnion type_value; \
type_value.union_member = arg; \
*type = value_type_id; \
*value = type_value.as_uint; \
}
// Simpler form for int types that can be safely casted.
#define INTERNAL_DECLARE_SET_TRACE_VALUE_INT(actual_type, value_type_id) \
static inline void SetTraceValue(actual_type arg, unsigned char* type, \
uint64_t* value) { \
*type = value_type_id; \
*value = static_cast<uint64_t>(arg); \
}
INTERNAL_DECLARE_SET_TRACE_VALUE_INT(uint64_t, TRACE_VALUE_TYPE_UINT)
INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned int, TRACE_VALUE_TYPE_UINT)
INTERNAL_DECLARE_SET_TRACE_VALUE_INT(uint16_t, TRACE_VALUE_TYPE_UINT)
INTERNAL_DECLARE_SET_TRACE_VALUE_INT(unsigned char, TRACE_VALUE_TYPE_UINT)
INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int64_t, TRACE_VALUE_TYPE_INT)
INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int, TRACE_VALUE_TYPE_INT)
INTERNAL_DECLARE_SET_TRACE_VALUE_INT(int16_t, TRACE_VALUE_TYPE_INT)
INTERNAL_DECLARE_SET_TRACE_VALUE_INT(signed char, TRACE_VALUE_TYPE_INT)
INTERNAL_DECLARE_SET_TRACE_VALUE(bool, as_bool, TRACE_VALUE_TYPE_BOOL)
INTERNAL_DECLARE_SET_TRACE_VALUE(double, as_double, TRACE_VALUE_TYPE_DOUBLE)
INTERNAL_DECLARE_SET_TRACE_VALUE(const void*, as_pointer,
TRACE_VALUE_TYPE_POINTER)
INTERNAL_DECLARE_SET_TRACE_VALUE(const char*, as_string,
TRACE_VALUE_TYPE_STRING)
INTERNAL_DECLARE_SET_TRACE_VALUE(const TraceStringWithCopy&, as_string,
TRACE_VALUE_TYPE_COPY_STRING)
#undef INTERNAL_DECLARE_SET_TRACE_VALUE
#undef INTERNAL_DECLARE_SET_TRACE_VALUE_INT
static inline void SetTraceValue(v8::ConvertableToTraceFormat* convertable_value,
unsigned char* type, uint64_t* value) {
*type = TRACE_VALUE_TYPE_CONVERTABLE;
*value = static_cast<uint64_t>(reinterpret_cast<intptr_t>(convertable_value));
}
template <typename T>
static inline typename std::enable_if<
std::is_convertible<T*, v8::ConvertableToTraceFormat*>::value>::type
SetTraceValue(std::unique_ptr<T> ptr, unsigned char* type, uint64_t* value) {
SetTraceValue(ptr.release(), type, value);
}
// These AddTraceEvent template
// function is defined here instead of in the macro, because the arg_values
// could be temporary objects, such as std::string. In order to store
// pointers to the internal c_str and pass through to the tracing API,
// the arg_values must live throughout these procedures.
static inline uint64_t AddTraceEvent(char phase,
const uint8_t* category_group_enabled,
const char* name, const char* scope,
uint64_t id, uint64_t bind_id,
unsigned int flags) {
return TRACE_EVENT_API_ADD_TRACE_EVENT(phase, category_group_enabled, name,
scope, id, bind_id, kZeroNumArgs,
nullptr, nullptr, nullptr, flags);
}
template <class ARG1_TYPE>
static inline uint64_t AddTraceEvent(
char phase, const uint8_t* category_group_enabled, const char* name,
const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags,
const char* arg1_name, ARG1_TYPE&& arg1_val) {
const int num_args = 1;
uint8_t arg_type;
uint64_t arg_value;
SetTraceValue(std::forward<ARG1_TYPE>(arg1_val), &arg_type, &arg_value);
return TRACE_EVENT_API_ADD_TRACE_EVENT(
phase, category_group_enabled, name, scope, id, bind_id, num_args,
&arg1_name, &arg_type, &arg_value, flags);
}
template <class ARG1_TYPE, class ARG2_TYPE>
static inline uint64_t AddTraceEvent(
char phase, const uint8_t* category_group_enabled, const char* name,
const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags,
const char* arg1_name, ARG1_TYPE&& arg1_val, const char* arg2_name,
ARG2_TYPE&& arg2_val) {
const int num_args = 2;
const char* arg_names[2] = {arg1_name, arg2_name};
unsigned char arg_types[2];
uint64_t arg_values[2];
SetTraceValue(std::forward<ARG1_TYPE>(arg1_val), &arg_types[0],
&arg_values[0]);
SetTraceValue(std::forward<ARG2_TYPE>(arg2_val), &arg_types[1],
&arg_values[1]);
return TRACE_EVENT_API_ADD_TRACE_EVENT(
phase, category_group_enabled, name, scope, id, bind_id, num_args,
arg_names, arg_types, arg_values, flags);
}
static V8_INLINE uint64_t AddTraceEventWithTimestamp(
char phase, const uint8_t* category_group_enabled, const char* name,
const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags,
int64_t timestamp) {
return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP(
phase, category_group_enabled, name, scope, id, bind_id, kZeroNumArgs,
nullptr, nullptr, nullptr, flags, timestamp);
}
template <class ARG1_TYPE>
static V8_INLINE uint64_t AddTraceEventWithTimestamp(
char phase, const uint8_t* category_group_enabled, const char* name,
const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags,
int64_t timestamp, const char* arg1_name, ARG1_TYPE&& arg1_val) {
const int num_args = 1;
uint8_t arg_type;
uint64_t arg_value;
SetTraceValue(std::forward<ARG1_TYPE>(arg1_val), &arg_type, &arg_value);
return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP(
phase, category_group_enabled, name, scope, id, bind_id, num_args,
&arg1_name, &arg_type, &arg_value, flags, timestamp);
}
template <class ARG1_TYPE, class ARG2_TYPE>
static V8_INLINE uint64_t AddTraceEventWithTimestamp(
char phase, const uint8_t* category_group_enabled, const char* name,
const char* scope, uint64_t id, uint64_t bind_id, unsigned int flags,
int64_t timestamp, const char* arg1_name, ARG1_TYPE&& arg1_val,
const char* arg2_name, ARG2_TYPE&& arg2_val) {
const int num_args = 2;
const char* arg_names[2] = {arg1_name, arg2_name};
unsigned char arg_types[2];
uint64_t arg_values[2];
SetTraceValue(std::forward<ARG1_TYPE>(arg1_val), &arg_types[0],
&arg_values[0]);
SetTraceValue(std::forward<ARG2_TYPE>(arg2_val), &arg_types[1],
&arg_values[1]);
return TRACE_EVENT_API_ADD_TRACE_EVENT_WITH_TIMESTAMP(
phase, category_group_enabled, name, scope, id, bind_id, num_args,
arg_names, arg_types, arg_values, flags, timestamp);
}
template <class ARG1_TYPE>
static V8_INLINE void AddMetadataEvent(
const uint8_t* category_group_enabled, const char* name,
const char* arg1_name, ARG1_TYPE&& arg1_val) {
const int num_args = 1;
uint8_t arg_type;
uint64_t arg_value;
SetTraceValue(std::forward<ARG1_TYPE>(arg1_val), &arg_type, &arg_value);
AddMetadataEventImpl(
category_group_enabled, name, num_args, &arg1_name, &arg_type, &arg_value,
TRACE_EVENT_FLAG_NONE);
}
// Used by TRACE_EVENTx macros. Do not use directly.
class ScopedTracer {
public:
// Note: members of data_ intentionally left uninitialized. See Initialize.
ScopedTracer() : p_data_(nullptr) {}
~ScopedTracer() {
if (p_data_ && *data_.category_group_enabled)
TRACE_EVENT_API_UPDATE_TRACE_EVENT_DURATION(
data_.category_group_enabled, data_.name, data_.event_handle);
}
void Initialize(const uint8_t* category_group_enabled, const char* name,
uint64_t event_handle) {
data_.category_group_enabled = category_group_enabled;
data_.name = name;
data_.event_handle = event_handle;
p_data_ = &data_;
}
private:
// This Data struct workaround is to avoid initializing all the members
// in Data during construction of this object, since this object is always
// constructed, even when tracing is disabled. If the members of Data were
// members of this class instead, compiler warnings occur about potential
// uninitialized accesses.
struct Data {
const uint8_t* category_group_enabled;
const char* name;
uint64_t event_handle;
};
Data* p_data_;
Data data_;
};
} // namespace tracing
} // namespace node
#endif // SRC_TRACING_TRACE_EVENT_H_

File diff suppressed because it is too large Load Diff

222
src/tracing/traced_value.cc Normal file
View File

@ -0,0 +1,222 @@
// 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 "tracing/traced_value.h"
#if defined(NODE_HAVE_I18N_SUPPORT)
#include <unicode/utf8.h>
#include <unicode/utypes.h>
#endif
#include <cmath>
#include <cstdio>
#include <sstream>
#include <string>
#if defined(_STLP_VENDOR_CSTD)
// STLPort doesn't import fpclassify into the std namespace.
#define FPCLASSIFY_NAMESPACE
#else
#define FPCLASSIFY_NAMESPACE std
#endif
namespace node {
namespace tracing {
namespace {
std::string EscapeString(const char* value) {
std::string result;
result += '"';
char number_buffer[10];
#if defined(NODE_HAVE_I18N_SUPPORT)
int32_t len = strlen(value);
int32_t p = 0;
int32_t i = 0;
for (; i < len; p = i) {
UChar32 c;
U8_NEXT_OR_FFFD(value, i, len, c);
switch (c) {
case '\b': result += "\\b"; break;
case '\f': result += "\\f"; break;
case '\n': result += "\\n"; break;
case '\r': result += "\\r"; break;
case '\t': result += "\\t"; break;
case '\\': result += "\\\\"; break;
case '"': result += "\\\""; break;
default:
if (c < 32 || c > 126) {
snprintf(
number_buffer, arraysize(number_buffer), "\\u%04X",
static_cast<uint16_t>(static_cast<uint16_t>(c)));
result += number_buffer;
} else {
result.append(value + p, i - p);
}
}
}
#else
// If we do not have ICU, use a modified version of the non-UTF8 aware
// code from V8's own TracedValue implementation. Note, however, This
// will not produce correctly serialized results for UTF8 values.
while (*value) {
char c = *value++;
switch (c) {
case '\b': result += "\\b"; break;
case '\f': result += "\\f"; break;
case '\n': result += "\\n"; break;
case '\r': result += "\\r"; break;
case '\t': result += "\\t"; break;
case '\\': result += "\\\\"; break;
case '"': result += "\\\""; break;
default:
if (c < '\x20') {
snprintf(
number_buffer, arraysize(number_buffer), "\\u%04X",
static_cast<unsigned>(static_cast<unsigned char>(c)));
result += number_buffer;
} else {
result += c;
}
}
}
#endif // defined(NODE_HAVE_I18N_SUPPORT)
result += '"';
return result;
}
std::string DoubleToCString(double v) {
switch (FPCLASSIFY_NAMESPACE::fpclassify(v)) {
case FP_NAN: return "\"NaN\"";
case FP_INFINITE: return (v < 0.0 ? "\"-Infinity\"" : "\"Infinity\"");
case FP_ZERO: return "0";
default:
// This is a far less sophisticated version than the one used inside v8.
std::ostringstream stream;
stream.imbue(std::locale::classic()); // Ignore current locale
stream << v;
return stream.str();
}
}
} // namespace
std::unique_ptr<TracedValue> TracedValue::Create() {
return std::unique_ptr<TracedValue>(new TracedValue(false));
}
std::unique_ptr<TracedValue> TracedValue::CreateArray() {
return std::unique_ptr<TracedValue>(new TracedValue(true));
}
TracedValue::TracedValue(bool root_is_array) :
first_item_(true), root_is_array_(root_is_array) {}
void TracedValue::SetInteger(const char* name, int value) {
WriteName(name);
data_ += std::to_string(value);
}
void TracedValue::SetDouble(const char* name, double value) {
WriteName(name);
data_ += DoubleToCString(value);
}
void TracedValue::SetBoolean(const char* name, bool value) {
WriteName(name);
data_ += value ? "true" : "false";
}
void TracedValue::SetNull(const char* name) {
WriteName(name);
data_ += "null";
}
void TracedValue::SetString(const char* name, const char* value) {
WriteName(name);
data_ += EscapeString(value);
}
void TracedValue::BeginDictionary(const char* name) {
WriteName(name);
data_ += '{';
first_item_ = true;
}
void TracedValue::BeginArray(const char* name) {
WriteName(name);
data_ += '[';
first_item_ = true;
}
void TracedValue::AppendInteger(int value) {
WriteComma();
data_ += std::to_string(value);
}
void TracedValue::AppendDouble(double value) {
WriteComma();
data_ += DoubleToCString(value);
}
void TracedValue::AppendBoolean(bool value) {
WriteComma();
data_ += value ? "true" : "false";
}
void TracedValue::AppendNull() {
WriteComma();
data_ += "null";
}
void TracedValue::AppendString(const char* value) {
WriteComma();
data_ += EscapeString(value);
}
void TracedValue::BeginDictionary() {
WriteComma();
data_ += '{';
first_item_ = true;
}
void TracedValue::BeginArray() {
WriteComma();
data_ += '[';
first_item_ = true;
}
void TracedValue::EndDictionary() {
data_ += '}';
first_item_ = false;
}
void TracedValue::EndArray() {
data_ += ']';
first_item_ = false;
}
void TracedValue::WriteComma() {
if (first_item_) {
first_item_ = false;
} else {
data_ += ',';
}
}
void TracedValue::WriteName(const char* name) {
WriteComma();
data_ += '"';
data_ += name;
data_ += "\":";
}
void TracedValue::AppendAsTraceFormat(std::string* out) const {
*out += root_is_array_ ? '[' : '{';
*out += data_;
*out += root_is_array_ ? ']' : '}';
}
} // namespace tracing
} // namespace node

View File

@ -0,0 +1,70 @@
// 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_TRACING_TRACED_VALUE_H_
#define SRC_TRACING_TRACED_VALUE_H_
#include "node.h"
#include "util.h"
#include "v8.h"
#include <cstddef>
#include <memory>
#include <string>
namespace node {
namespace tracing {
class TracedValue : public v8::ConvertableToTraceFormat {
public:
~TracedValue() override = default;
static std::unique_ptr<TracedValue> Create();
static std::unique_ptr<TracedValue> CreateArray();
void EndDictionary();
void EndArray();
// These methods assume that |name| is a long lived "quoted" string.
void SetInteger(const char* name, int value);
void SetDouble(const char* name, double value);
void SetBoolean(const char* name, bool value);
void SetNull(const char* name);
void SetString(const char* name, const char* value);
void SetString(const char* name, const std::string& value) {
SetString(name, value.c_str());
}
void BeginDictionary(const char* name);
void BeginArray(const char* name);
void AppendInteger(int);
void AppendDouble(double);
void AppendBoolean(bool);
void AppendNull();
void AppendString(const char*);
void AppendString(const std::string& value) { AppendString(value.c_str()); }
void BeginArray();
void BeginDictionary();
// ConvertableToTraceFormat implementation.
void AppendAsTraceFormat(std::string* out) const override;
TracedValue(const TracedValue&) = delete;
TracedValue& operator=(const TracedValue&) = delete;
private:
explicit TracedValue(bool root_is_array = false);
void WriteComma();
void WriteName(const char* name);
std::string data_;
bool first_item_;
bool root_is_array_;
};
} // namespace tracing
} // namespace node
#endif // SRC_TRACING_TRACED_VALUE_H_