#include "base_object-inl.h" #include "debug_utils.h" #include "inspector_agent.h" #include "node_internals.h" #include "v8-inspector.h" namespace node { namespace profiler { using v8::Context; using v8::EscapableHandleScope; using v8::Function; using v8::FunctionCallbackInfo; using v8::HandleScope; using v8::Isolate; using v8::Local; using v8::MaybeLocal; using v8::NewStringType; using v8::Object; using v8::ObjectTemplate; using v8::String; using v8::Value; using v8_inspector::StringBuffer; using v8_inspector::StringView; std::unique_ptr ToProtocolString(Isolate* isolate, Local value) { TwoByteValue buffer(isolate, value); return StringBuffer::create(StringView(*buffer, buffer.length())); } class V8ProfilerConnection : public BaseObject { public: class V8ProfilerSessionDelegate : public inspector::InspectorSessionDelegate { public: explicit V8ProfilerSessionDelegate(V8ProfilerConnection* connection) : connection_(connection) {} void SendMessageToFrontend( const v8_inspector::StringView& message) override { Environment* env = connection_->env(); Local fn = connection_->GetMessageCallback(); bool ending = !fn.IsEmpty(); Debug(env, DebugCategory::INSPECTOR_PROFILER, "Sending message to frontend, ending = %s\n", ending ? "true" : "false"); if (!ending) { return; } Isolate* isolate = env->isolate(); HandleScope handle_scope(isolate); Context::Scope context_scope(env->context()); MaybeLocal v8string = String::NewFromTwoByte(isolate, message.characters16(), NewStringType::kNormal, message.length()); Local args[] = {v8string.ToLocalChecked().As()}; USE(fn->Call( env->context(), connection_->object(), arraysize(args), args)); } private: V8ProfilerConnection* connection_; }; SET_MEMORY_INFO_NAME(V8ProfilerConnection) SET_SELF_SIZE(V8ProfilerConnection) void MemoryInfo(MemoryTracker* tracker) const override { tracker->TrackFieldWithSize( "session", sizeof(*session_), "InspectorSession"); } explicit V8ProfilerConnection(Environment* env, Local obj) : BaseObject(env, obj), session_(nullptr) { inspector::Agent* inspector = env->inspector_agent(); std::unique_ptr session = inspector->Connect( std::make_unique(this), false); session_ = std::move(session); MakeWeak(); } void DispatchMessage(Isolate* isolate, Local message) { session_->Dispatch(ToProtocolString(isolate, message)->string()); } static MaybeLocal CreateConnectionObject(Environment* env) { Isolate* isolate = env->isolate(); Local context = env->context(); EscapableHandleScope scope(isolate); Local t = ObjectTemplate::New(isolate); t->SetInternalFieldCount(1); Local obj; if (!t->NewInstance(context).ToLocal(&obj)) { return MaybeLocal(); } obj->SetAlignedPointerInInternalField(0, nullptr); return scope.Escape(obj); } void Start() { SetConnection(object()); StartProfiling(); } void End(Local callback) { SetMessageCallback(callback); EndProfiling(); } // Override this to return a JS function that gets called with the message // sent from the session. virtual Local GetMessageCallback() = 0; virtual void SetMessageCallback(Local callback) = 0; // Use DispatchMessage() to dispatch necessary inspector messages virtual void StartProfiling() = 0; virtual void EndProfiling() = 0; virtual void SetConnection(Local connection) = 0; private: std::unique_ptr session_; }; class V8CoverageConnection : public V8ProfilerConnection { public: explicit V8CoverageConnection(Environment* env) : V8ProfilerConnection(env, CreateConnectionObject(env).ToLocalChecked()) {} Local GetMessageCallback() override { return env()->on_coverage_message_function(); } void SetMessageCallback(Local callback) override { return env()->set_on_coverage_message_function(callback); } static V8ProfilerConnection* GetConnection(Environment* env) { return Unwrap(env->coverage_connection()); } void SetConnection(Local obj) override { env()->set_coverage_connection(obj); } void StartProfiling() override { Debug(env(), DebugCategory::INSPECTOR_PROFILER, "Sending Profiler.startPreciseCoverage\n"); Isolate* isolate = env()->isolate(); Local enable = FIXED_ONE_BYTE_STRING( isolate, "{\"id\": 1, \"method\": \"Profiler.enable\"}"); Local start = FIXED_ONE_BYTE_STRING( isolate, "{" "\"id\": 2," "\"method\": \"Profiler.startPreciseCoverage\"," "\"params\": {\"callCount\": true, \"detailed\": true}" "}"); DispatchMessage(isolate, enable); DispatchMessage(isolate, start); } void EndProfiling() override { Debug(env(), DebugCategory::INSPECTOR_PROFILER, "Sending Profiler.takePreciseCoverage\n"); Isolate* isolate = env()->isolate(); Local end = FIXED_ONE_BYTE_STRING(isolate, "{" "\"id\": 3," "\"method\": \"Profiler.takePreciseCoverage\"" "}"); DispatchMessage(isolate, end); } private: std::unique_ptr session_; }; void StartCoverageCollection(Environment* env) { V8CoverageConnection* connection = new V8CoverageConnection(env); connection->Start(); } static void EndCoverageCollection(const FunctionCallbackInfo& args) { Environment* env = Environment::GetCurrent(args); CHECK(args[0]->IsFunction()); Debug(env, DebugCategory::INSPECTOR_PROFILER, "Ending coverage collection\n"); V8ProfilerConnection* connection = V8CoverageConnection::GetConnection(env); CHECK_NOT_NULL(connection); connection->End(args[0].As()); } static void Initialize(Local target, Local unused, Local context, void* priv) { Environment* env = Environment::GetCurrent(context); env->SetMethod(target, "endCoverage", EndCoverageCollection); } } // namespace profiler } // namespace node NODE_MODULE_CONTEXT_AWARE_INTERNAL(profiler, node::profiler::Initialize)