1#include "tracing_agent.h" 2#include "main_thread_interface.h" 3#include "node_internals.h" 4#include "node_v8_platform-inl.h" 5#include "v8.h" 6 7#include <set> 8#include <sstream> 9 10namespace node { 11namespace inspector { 12namespace protocol { 13 14namespace { 15using v8::platform::tracing::TraceWriter; 16 17class DeletableFrontendWrapper : public Deletable { 18 public: 19 explicit DeletableFrontendWrapper( 20 std::weak_ptr<NodeTracing::Frontend> frontend) 21 : frontend_(frontend) {} 22 23 // This should only be called from the main thread, meaning frontend should 24 // not be destroyed concurrently. 25 NodeTracing::Frontend* get() { return frontend_.lock().get(); } 26 27 private: 28 std::weak_ptr<NodeTracing::Frontend> frontend_; 29}; 30 31class CreateFrontendWrapperRequest : public Request { 32 public: 33 CreateFrontendWrapperRequest(int object_id, 34 std::weak_ptr<NodeTracing::Frontend> frontend) 35 : object_id_(object_id) { 36 frontend_wrapper_ = std::make_unique<DeletableFrontendWrapper>(frontend); 37 } 38 39 void Call(MainThreadInterface* thread) override { 40 thread->AddObject(object_id_, std::move(frontend_wrapper_)); 41 } 42 43 private: 44 int object_id_; 45 std::unique_ptr<DeletableFrontendWrapper> frontend_wrapper_; 46}; 47 48class DestroyFrontendWrapperRequest : public Request { 49 public: 50 explicit DestroyFrontendWrapperRequest(int object_id) 51 : object_id_(object_id) {} 52 53 void Call(MainThreadInterface* thread) override { 54 thread->RemoveObject(object_id_); 55 } 56 57 private: 58 int object_id_; 59}; 60 61class SendMessageRequest : public Request { 62 public: 63 explicit SendMessageRequest(int object_id, const std::string& message) 64 : object_id_(object_id), message_(message) {} 65 66 void Call(MainThreadInterface* thread) override { 67 DeletableFrontendWrapper* frontend_wrapper = 68 static_cast<DeletableFrontendWrapper*>( 69 thread->GetObjectIfExists(object_id_)); 70 if (frontend_wrapper == nullptr) return; 71 auto frontend = frontend_wrapper->get(); 72 if (frontend != nullptr) { 73 frontend->sendRawJSONNotification(message_); 74 } 75 } 76 77 private: 78 int object_id_; 79 std::string message_; 80}; 81 82class InspectorTraceWriter : public node::tracing::AsyncTraceWriter { 83 public: 84 explicit InspectorTraceWriter(int frontend_object_id, 85 std::shared_ptr<MainThreadHandle> main_thread) 86 : frontend_object_id_(frontend_object_id), main_thread_(main_thread) {} 87 88 void AppendTraceEvent( 89 v8::platform::tracing::TraceObject* trace_event) override { 90 if (!json_writer_) 91 json_writer_.reset(TraceWriter::CreateJSONTraceWriter(stream_, "value")); 92 json_writer_->AppendTraceEvent(trace_event); 93 } 94 95 void Flush(bool) override { 96 if (!json_writer_) 97 return; 98 json_writer_.reset(); 99 std::ostringstream result( 100 "{\"method\":\"NodeTracing.dataCollected\",\"params\":", 101 std::ostringstream::ate); 102 result << stream_.str(); 103 result << "}"; 104 main_thread_->Post(std::make_unique<SendMessageRequest>(frontend_object_id_, 105 result.str())); 106 stream_.str(""); 107 } 108 109 private: 110 std::unique_ptr<TraceWriter> json_writer_; 111 std::ostringstream stream_; 112 int frontend_object_id_; 113 std::shared_ptr<MainThreadHandle> main_thread_; 114}; 115} // namespace 116 117TracingAgent::TracingAgent(Environment* env, 118 std::shared_ptr<MainThreadHandle> main_thread) 119 : env_(env), main_thread_(main_thread) {} 120 121TracingAgent::~TracingAgent() { 122 trace_writer_.reset(); 123 main_thread_->Post( 124 std::make_unique<DestroyFrontendWrapperRequest>(frontend_object_id_)); 125} 126 127void TracingAgent::Wire(UberDispatcher* dispatcher) { 128 // Note that frontend is still owned by TracingAgent 129 frontend_ = std::make_shared<NodeTracing::Frontend>(dispatcher->channel()); 130 frontend_object_id_ = main_thread_->newObjectId(); 131 main_thread_->Post(std::make_unique<CreateFrontendWrapperRequest>( 132 frontend_object_id_, frontend_)); 133 NodeTracing::Dispatcher::wire(dispatcher, this); 134} 135 136DispatchResponse TracingAgent::start( 137 std::unique_ptr<protocol::NodeTracing::TraceConfig> traceConfig) { 138 if (!trace_writer_.empty()) { 139 return DispatchResponse::Error( 140 "Call NodeTracing::end to stop tracing before updating the config"); 141 } 142 if (!env_->owns_process_state()) { 143 return DispatchResponse::Error( 144 "Tracing properties can only be changed through main thread sessions"); 145 } 146 147 std::set<std::string> categories_set; 148 protocol::Array<std::string>* categories = 149 traceConfig->getIncludedCategories(); 150 for (size_t i = 0; i < categories->length(); i++) 151 categories_set.insert(categories->get(i)); 152 153 if (categories_set.empty()) 154 return DispatchResponse::Error("At least one category should be enabled"); 155 156 tracing::AgentWriterHandle* writer = GetTracingAgentWriter(); 157 if (writer != nullptr) { 158 trace_writer_ = 159 writer->agent()->AddClient(categories_set, 160 std::make_unique<InspectorTraceWriter>( 161 frontend_object_id_, main_thread_), 162 tracing::Agent::kIgnoreDefaultCategories); 163 } 164 return DispatchResponse::OK(); 165} 166 167DispatchResponse TracingAgent::stop() { 168 trace_writer_.reset(); 169 frontend_->tracingComplete(); 170 return DispatchResponse::OK(); 171} 172 173DispatchResponse TracingAgent::getCategories( 174 std::unique_ptr<protocol::Array<String>>* categories) { 175 *categories = Array<String>::create(); 176 protocol::Array<String>* categories_list = categories->get(); 177 // In alphabetical order 178 categories_list->addItem("node"); 179 categories_list->addItem("node.async_hooks"); 180 categories_list->addItem("node.bootstrap"); 181 categories_list->addItem("node.console"); 182 categories_list->addItem("node.dns.native"); 183 categories_list->addItem("node.environment"); 184 categories_list->addItem("node.fs.async"); 185 categories_list->addItem("node.fs.sync"); 186 categories_list->addItem("node.fs_dir.async"); 187 categories_list->addItem("node.fs_dir.sync"); 188 categories_list->addItem("node.http"); 189 categories_list->addItem("node.net.native"); 190 categories_list->addItem("node.perf"); 191 categories_list->addItem("node.perf.timerify"); 192 categories_list->addItem("node.perf.usertiming"); 193 categories_list->addItem("node.promises.rejections"); 194 categories_list->addItem("node.threadpoolwork.async"); 195 categories_list->addItem("node.threadpoolwork.sync"); 196 categories_list->addItem("node.vm.script"); 197 categories_list->addItem("v8"); 198 return DispatchResponse::OK(); 199} 200 201} // namespace protocol 202} // namespace inspector 203} // namespace node 204