forked from LeenkxTeam/Kmake
252 lines
7.4 KiB
C++
252 lines
7.4 KiB
C++
#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
|