#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 #include #include #include 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* 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& categories); inline void Disable(const std::set& 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_; 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& categories, std::unique_ptr 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 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& categories); void Disable(int id, const std::set& 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> categories_; std::unordered_map> writers_; std::unique_ptr 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 to_be_initialized_; Mutex metadata_events_mutex_; std::list> 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& categories) { if (agent_ != nullptr) agent_->Enable(id_, categories); } void AgentWriterHandle::Disable(const std::set& 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_