Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 230e98b

Browse filesBrowse files
joyeecheungrvagg
authored andcommitted
process: start coverage collection before bootstrap
This patch moves the dispatch of `Profiler.takePreciseCoverage` to a point before the bootstrap scripts are run to ensure that we can collect coverage data for all the scripts run after the inspector agent is ready. Before this patch `lib/internal/bootstrap/primordials.js` was not covered by `make coverage`, after this patch it is. PR-URL: #26006 Reviewed-By: Anna Henningsen <anna@addaleax.net> Reviewed-By: Ben Coe <bencoe@gmail.com> Reviewed-By: James M Snell <jasnell@gmail.com>
1 parent 76c2f4f commit 230e98b
Copy full SHA for 230e98b

File tree

Expand file treeCollapse file tree

8 files changed

+205
-58
lines changed
Open diff view settings
Filter options
Expand file treeCollapse file tree

8 files changed

+205
-58
lines changed
Open diff view settings
Collapse file

‎lib/internal/bootstrap/loaders.js‎

Copy file name to clipboardExpand all lines: lib/internal/bootstrap/loaders.js
-13Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -311,18 +311,5 @@ NativeModule.prototype.compile = function() {
311311
}
312312
};
313313

314-
// Coverage must be turned on early, so that we can collect
315-
// it for Node.js' own internal libraries.
316-
if (process.env.NODE_V8_COVERAGE) {
317-
if (internalBinding('config').hasInspector) {
318-
const coverage =
319-
NativeModule.require('internal/coverage-gen/with_profiler');
320-
// Inform the profiler to start collecting coverage
321-
coverage.startCoverageCollection();
322-
} else {
323-
process._rawDebug('NODE_V8_COVERAGE cannot be used without inspector');
324-
}
325-
}
326-
327314
// This will be passed to internal/bootstrap/node.js.
328315
return loaderExports;
Collapse file

‎lib/internal/coverage-gen/with_profiler.js‎

Copy file name to clipboardExpand all lines: lib/internal/coverage-gen/with_profiler.js
+6-40Lines changed: 6 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,9 @@
33
// Implements coverage collection exposed by the `NODE_V8_COVERAGE`
44
// environment variable which can also be used in the user land.
55

6-
let coverageConnection = null;
76
let coverageDirectory;
87

98
function writeCoverage() {
10-
if (!coverageConnection && coverageDirectory) {
11-
return;
12-
}
13-
149
const { join } = require('path');
1510
const { mkdirSync, writeFileSync } = require('fs');
1611
const { threadId } = require('internal/worker');
@@ -28,21 +23,14 @@ function writeCoverage() {
2823
const target = join(coverageDirectory, filename);
2924
try {
3025
disableAllAsyncHooks();
31-
let msg;
32-
coverageConnection._coverageCallback = function(_msg) {
33-
msg = _msg;
34-
};
35-
coverageConnection.dispatch(JSON.stringify({
36-
id: 3,
37-
method: 'Profiler.takePreciseCoverage'
38-
}));
39-
const coverageInfo = JSON.parse(msg).result;
40-
writeFileSync(target, JSON.stringify(coverageInfo));
26+
internalBinding('coverage').end((msg) => {
27+
const coverageInfo = JSON.parse(msg).result;
28+
if (coverageInfo) {
29+
writeFileSync(target, JSON.stringify(coverageInfo));
30+
}
31+
});
4132
} catch (err) {
4233
console.error(err);
43-
} finally {
44-
coverageConnection.disconnect();
45-
coverageConnection = null;
4634
}
4735
}
4836

@@ -52,33 +40,11 @@ function disableAllAsyncHooks() {
5240
hooks_array.forEach((hook) => { hook.disable(); });
5341
}
5442

55-
function startCoverageCollection() {
56-
const { Connection } = internalBinding('inspector');
57-
coverageConnection = new Connection((res) => {
58-
if (coverageConnection._coverageCallback) {
59-
coverageConnection._coverageCallback(res);
60-
}
61-
});
62-
coverageConnection.dispatch(JSON.stringify({
63-
id: 1,
64-
method: 'Profiler.enable'
65-
}));
66-
coverageConnection.dispatch(JSON.stringify({
67-
id: 2,
68-
method: 'Profiler.startPreciseCoverage',
69-
params: {
70-
callCount: true,
71-
detailed: true
72-
}
73-
}));
74-
}
75-
7643
function setCoverageDirectory(dir) {
7744
coverageDirectory = dir;
7845
}
7946

8047
module.exports = {
81-
startCoverageCollection,
8248
writeCoverage,
8349
setCoverageDirectory
8450
};
Collapse file

‎src/env.h‎

Copy file name to clipboardExpand all lines: src/env.h
+6-3Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
329329
V(async_wrap_ctor_template, v8::FunctionTemplate) \
330330
V(async_wrap_object_ctor_template, v8::FunctionTemplate) \
331331
V(buffer_prototype_object, v8::Object) \
332+
V(coverage_connection, v8::Object) \
332333
V(context, v8::Context) \
333334
V(crypto_key_object_constructor, v8::Function) \
334335
V(domain_callback, v8::Function) \
@@ -364,6 +365,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2;
364365
V(message_event_object_template, v8::ObjectTemplate) \
365366
V(message_port_constructor_template, v8::FunctionTemplate) \
366367
V(native_module_require, v8::Function) \
368+
V(on_coverage_message_function, v8::Function) \
367369
V(performance_entry_callback, v8::Function) \
368370
V(performance_entry_template, v8::Function) \
369371
V(pipe_constructor_template, v8::FunctionTemplate) \
@@ -448,9 +450,10 @@ struct ContextInfo {
448450

449451
// Listing the AsyncWrap provider types first enables us to cast directly
450452
// from a provider type to a debug category.
451-
#define DEBUG_CATEGORY_NAMES(V) \
452-
NODE_ASYNC_PROVIDER_TYPES(V) \
453-
V(INSPECTOR_SERVER)
453+
#define DEBUG_CATEGORY_NAMES(V) \
454+
NODE_ASYNC_PROVIDER_TYPES(V) \
455+
V(INSPECTOR_SERVER) \
456+
V(COVERAGE)
454457

455458
enum class DebugCategory {
456459
#define V(name) name,
Collapse file

‎src/inspector/node_inspector.gypi‎

Copy file name to clipboardExpand all lines: src/inspector/node_inspector.gypi
+1Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@
4545
'../../src/inspector_io.cc',
4646
'../../src/inspector_agent.h',
4747
'../../src/inspector_io.h',
48+
'../../src/inspector_coverage.cc',
4849
'../../src/inspector_js_api.cc',
4950
'../../src/inspector_socket.cc',
5051
'../../src/inspector_socket.h',
Collapse file

‎src/inspector_coverage.cc‎

Copy file name to clipboard
+168Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
#include "base_object-inl.h"
2+
#include "debug_utils.h"
3+
#include "inspector_agent.h"
4+
#include "node_internals.h"
5+
#include "v8-inspector.h"
6+
7+
namespace node {
8+
namespace coverage {
9+
10+
using v8::Context;
11+
using v8::Function;
12+
using v8::FunctionCallbackInfo;
13+
using v8::HandleScope;
14+
using v8::Isolate;
15+
using v8::Local;
16+
using v8::MaybeLocal;
17+
using v8::NewStringType;
18+
using v8::Object;
19+
using v8::ObjectTemplate;
20+
using v8::String;
21+
using v8::Value;
22+
23+
using v8_inspector::StringBuffer;
24+
using v8_inspector::StringView;
25+
26+
std::unique_ptr<StringBuffer> ToProtocolString(Isolate* isolate,
27+
Local<Value> value) {
28+
TwoByteValue buffer(isolate, value);
29+
return StringBuffer::create(StringView(*buffer, buffer.length()));
30+
}
31+
32+
class V8CoverageConnection : public BaseObject {
33+
public:
34+
class V8CoverageSessionDelegate : public inspector::InspectorSessionDelegate {
35+
public:
36+
explicit V8CoverageSessionDelegate(V8CoverageConnection* connection)
37+
: connection_(connection) {}
38+
39+
void SendMessageToFrontend(
40+
const v8_inspector::StringView& message) override {
41+
Environment* env = connection_->env();
42+
Local<Function> fn = connection_->env()->on_coverage_message_function();
43+
bool ending = !fn.IsEmpty();
44+
Debug(env,
45+
DebugCategory::COVERAGE,
46+
"Sending message to frontend, ending = %s\n",
47+
ending ? "true" : "false");
48+
if (!ending) {
49+
return;
50+
}
51+
Isolate* isolate = env->isolate();
52+
53+
HandleScope handle_scope(isolate);
54+
Context::Scope context_scope(env->context());
55+
MaybeLocal<String> v8string =
56+
String::NewFromTwoByte(isolate,
57+
message.characters16(),
58+
NewStringType::kNormal,
59+
message.length());
60+
Local<Value> args[] = {v8string.ToLocalChecked().As<Value>()};
61+
USE(MakeCallback(isolate,
62+
connection_->object(),
63+
fn,
64+
arraysize(args),
65+
args,
66+
async_context{0, 0}));
67+
}
68+
69+
private:
70+
V8CoverageConnection* connection_;
71+
};
72+
73+
SET_MEMORY_INFO_NAME(V8CoverageConnection)
74+
SET_SELF_SIZE(V8CoverageConnection)
75+
76+
void MemoryInfo(MemoryTracker* tracker) const override {
77+
tracker->TrackFieldWithSize(
78+
"session", sizeof(*session_), "InspectorSession");
79+
}
80+
81+
explicit V8CoverageConnection(Environment* env)
82+
: BaseObject(env, env->coverage_connection()), session_(nullptr) {
83+
inspector::Agent* inspector = env->inspector_agent();
84+
std::unique_ptr<inspector::InspectorSession> session = inspector->Connect(
85+
std::make_unique<V8CoverageSessionDelegate>(this), false);
86+
session_ = std::move(session);
87+
MakeWeak();
88+
}
89+
90+
void Start() {
91+
Debug(this->env(),
92+
DebugCategory::COVERAGE,
93+
"Sending Profiler.startPreciseCoverage\n");
94+
Isolate* isolate = this->env()->isolate();
95+
Local<Value> enable = FIXED_ONE_BYTE_STRING(
96+
isolate, "{\"id\": 1, \"method\": \"Profiler.enable\"}");
97+
Local<Value> start = FIXED_ONE_BYTE_STRING(
98+
isolate,
99+
"{"
100+
"\"id\": 2,"
101+
"\"method\": \"Profiler.startPreciseCoverage\","
102+
"\"params\": {\"callCount\": true, \"detailed\": true}"
103+
"}");
104+
session_->Dispatch(ToProtocolString(isolate, enable)->string());
105+
session_->Dispatch(ToProtocolString(isolate, start)->string());
106+
}
107+
108+
void End() {
109+
Debug(this->env(),
110+
DebugCategory::COVERAGE,
111+
"Sending Profiler.takePreciseCoverage\n");
112+
Isolate* isolate = this->env()->isolate();
113+
Local<Value> end =
114+
FIXED_ONE_BYTE_STRING(isolate,
115+
"{"
116+
"\"id\": 3,"
117+
"\"method\": \"Profiler.takePreciseCoverage\""
118+
"}");
119+
session_->Dispatch(ToProtocolString(isolate, end)->string());
120+
}
121+
122+
friend class V8CoverageSessionDelegate;
123+
124+
private:
125+
std::unique_ptr<inspector::InspectorSession> session_;
126+
};
127+
128+
bool StartCoverageCollection(Environment* env) {
129+
HandleScope scope(env->isolate());
130+
131+
Local<ObjectTemplate> t = ObjectTemplate::New(env->isolate());
132+
t->SetInternalFieldCount(1);
133+
Local<Object> obj;
134+
if (!t->NewInstance(env->context()).ToLocal(&obj)) {
135+
return false;
136+
}
137+
138+
obj->SetAlignedPointerInInternalField(0, nullptr);
139+
140+
CHECK(env->coverage_connection().IsEmpty());
141+
env->set_coverage_connection(obj);
142+
V8CoverageConnection* connection = new V8CoverageConnection(env);
143+
connection->Start();
144+
return true;
145+
}
146+
147+
static void EndCoverageCollection(const FunctionCallbackInfo<Value>& args) {
148+
Environment* env = Environment::GetCurrent(args);
149+
CHECK(args[0]->IsFunction());
150+
Debug(env, DebugCategory::COVERAGE, "Ending coverage collection\n");
151+
env->set_on_coverage_message_function(args[0].As<Function>());
152+
V8CoverageConnection* connection =
153+
Unwrap<V8CoverageConnection>(env->coverage_connection());
154+
CHECK_NOT_NULL(connection);
155+
connection->End();
156+
}
157+
158+
static void Initialize(Local<Object> target,
159+
Local<Value> unused,
160+
Local<Context> context,
161+
void* priv) {
162+
Environment* env = Environment::GetCurrent(context);
163+
env->SetMethod(target, "end", EndCoverageCollection);
164+
}
165+
} // namespace coverage
166+
} // namespace node
167+
168+
NODE_MODULE_CONTEXT_AWARE_INTERNAL(coverage, node::coverage::Initialize)
Collapse file

‎src/node.cc‎

Copy file name to clipboardExpand all lines: src/node.cc
+13Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
2020
// USE OR OTHER DEALINGS IN THE SOFTWARE.
2121

22+
#include "debug_utils.h"
2223
#include "node_binding.h"
2324
#include "node_buffer.h"
2425
#include "node_constants.h"
@@ -230,6 +231,18 @@ MaybeLocal<Value> RunBootstrapping(Environment* env) {
230231
Isolate* isolate = env->isolate();
231232
Local<Context> context = env->context();
232233

234+
std::string coverage;
235+
bool rc = credentials::SafeGetenv("NODE_V8_COVERAGE", &coverage);
236+
if (rc && !coverage.empty()) {
237+
#if HAVE_INSPECTOR
238+
if (!coverage::StartCoverageCollection(env)) {
239+
return MaybeLocal<Value>();
240+
}
241+
#else
242+
fprintf(stderr, "NODE_V8_COVERAGE cannot be used without inspector");
243+
#endif // HAVE_INSPECTOR
244+
}
245+
233246
// Add a reference to the global object
234247
Local<Object> global = context->Global();
235248

Collapse file

‎src/node_binding.cc‎

Copy file name to clipboardExpand all lines: src/node_binding.cc
+8-1Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,12 @@
2121
#define NODE_BUILTIN_REPORT_MODULES(V)
2222
#endif
2323

24+
#if HAVE_INSPECTOR
25+
#define NODE_BUILTIN_COVERAGE_MODULES(V) V(coverage)
26+
#else
27+
#define NODE_BUILTIN_COVERAGE_MODULES(V)
28+
#endif
29+
2430
// A list of built-in modules. In order to do module registration
2531
// in node::Init(), need to add built-in modules in the following list.
2632
// Then in binding::RegisterBuiltinModules(), it calls modules' registration
@@ -77,7 +83,8 @@
7783
NODE_BUILTIN_STANDARD_MODULES(V) \
7884
NODE_BUILTIN_OPENSSL_MODULES(V) \
7985
NODE_BUILTIN_ICU_MODULES(V) \
80-
NODE_BUILTIN_REPORT_MODULES(V)
86+
NODE_BUILTIN_REPORT_MODULES(V) \
87+
NODE_BUILTIN_COVERAGE_MODULES(V)
8188

8289
// This is used to load built-in modules. Instead of using
8390
// __attribute__((constructor)), we call the _register_<modname>
Collapse file

‎src/node_internals.h‎

Copy file name to clipboardExpand all lines: src/node_internals.h
+3-1Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -269,7 +269,9 @@ void DefineZlibConstants(v8::Local<v8::Object> target);
269269
v8::MaybeLocal<v8::Value> RunBootstrapping(Environment* env);
270270
v8::MaybeLocal<v8::Value> StartExecution(Environment* env,
271271
const char* main_script_id);
272-
272+
namespace coverage {
273+
bool StartCoverageCollection(Environment* env);
274+
}
273275
} // namespace node
274276

275277
#endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.