blob: 942a1d148a2f79b1586c08be0669ab7e7d1d5933 [file] [edit]
#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<StringBuffer> ToProtocolString(Isolate* isolate,
Local<Value> 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<Function> 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<String> v8string =
String::NewFromTwoByte(isolate,
message.characters16(),
NewStringType::kNormal,
message.length());
Local<Value> args[] = {v8string.ToLocalChecked().As<Value>()};
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<Object> obj)
: BaseObject(env, obj), session_(nullptr) {
inspector::Agent* inspector = env->inspector_agent();
std::unique_ptr<inspector::InspectorSession> session = inspector->Connect(
std::make_unique<V8ProfilerSessionDelegate>(this), false);
session_ = std::move(session);
MakeWeak();
}
void DispatchMessage(Isolate* isolate, Local<String> message) {
session_->Dispatch(ToProtocolString(isolate, message)->string());
}
static MaybeLocal<Object> CreateConnectionObject(Environment* env) {
Isolate* isolate = env->isolate();
Local<Context> context = env->context();
EscapableHandleScope scope(isolate);
Local<ObjectTemplate> t = ObjectTemplate::New(isolate);
t->SetInternalFieldCount(1);
Local<Object> obj;
if (!t->NewInstance(context).ToLocal(&obj)) {
return MaybeLocal<Object>();
}
obj->SetAlignedPointerInInternalField(0, nullptr);
return scope.Escape(obj);
}
void Start() {
SetConnection(object());
StartProfiling();
}
void End(Local<Function> callback) {
SetMessageCallback(callback);
EndProfiling();
}
// Override this to return a JS function that gets called with the message
// sent from the session.
virtual Local<Function> GetMessageCallback() = 0;
virtual void SetMessageCallback(Local<Function> callback) = 0;
// Use DispatchMessage() to dispatch necessary inspector messages
virtual void StartProfiling() = 0;
virtual void EndProfiling() = 0;
virtual void SetConnection(Local<Object> connection) = 0;
private:
std::unique_ptr<inspector::InspectorSession> session_;
};
class V8CoverageConnection : public V8ProfilerConnection {
public:
explicit V8CoverageConnection(Environment* env)
: V8ProfilerConnection(env,
CreateConnectionObject(env).ToLocalChecked()) {}
Local<Function> GetMessageCallback() override {
return env()->on_coverage_message_function();
}
void SetMessageCallback(Local<Function> callback) override {
return env()->set_on_coverage_message_function(callback);
}
static V8ProfilerConnection* GetConnection(Environment* env) {
return Unwrap<V8CoverageConnection>(env->coverage_connection());
}
void SetConnection(Local<Object> obj) override {
env()->set_coverage_connection(obj);
}
void StartProfiling() override {
Debug(env(),
DebugCategory::INSPECTOR_PROFILER,
"Sending Profiler.startPreciseCoverage\n");
Isolate* isolate = env()->isolate();
Local<String> enable = FIXED_ONE_BYTE_STRING(
isolate, "{\"id\": 1, \"method\": \"Profiler.enable\"}");
Local<String> 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<String> end =
FIXED_ONE_BYTE_STRING(isolate,
"{"
"\"id\": 3,"
"\"method\": \"Profiler.takePreciseCoverage\""
"}");
DispatchMessage(isolate, end);
}
private:
std::unique_ptr<inspector::InspectorSession> session_;
};
void StartCoverageCollection(Environment* env) {
V8CoverageConnection* connection = new V8CoverageConnection(env);
connection->Start();
}
static void EndCoverageCollection(const FunctionCallbackInfo<Value>& 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<Function>());
}
static void Initialize(Local<Object> target,
Local<Value> unused,
Local<Context> 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)