| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent 76c2f4f commit 230e98b
8 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -311,18 +311,5 @@ NativeModule.prototype.compile = function() { | |||
| 311 | 311 | } | |
| 312 | 312 | }; | |
| 313 | 313 | ||
| 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 | - | ||
| 327 | 314 | // This will be passed to internal/bootstrap/node.js. | |
| 328 | 315 | return loaderExports; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3,14 +3,9 @@ | |||
| 3 | 3 | // Implements coverage collection exposed by the `NODE_V8_COVERAGE` | |
| 4 | 4 | // environment variable which can also be used in the user land. | |
| 5 | 5 | ||
| 6 | - let coverageConnection = null; | ||
| 7 | 6 | let coverageDirectory; | |
| 8 | 7 | ||
| 9 | 8 | function writeCoverage() { | |
| 10 | - if (!coverageConnection && coverageDirectory) { | ||
| 11 | - return; | ||
| 12 | - } | ||
| 13 | - | ||
| 14 | 9 | const { join } = require('path'); | |
| 15 | 10 | const { mkdirSync, writeFileSync } = require('fs'); | |
| 16 | 11 | const { threadId } = require('internal/worker'); | |
@@ -28,21 +23,14 @@ function writeCoverage() { | |||
| 28 | 23 | const target = join(coverageDirectory, filename); | |
| 29 | 24 | try { | |
| 30 | 25 | 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 | + }); | ||
| 41 | 32 | } catch (err) { | |
| 42 | 33 | console.error(err); | |
| 43 | - } finally { | ||
| 44 | - coverageConnection.disconnect(); | ||
| 45 | - coverageConnection = null; | ||
| 46 | 34 | } | |
| 47 | 35 | } | |
| 48 | 36 | ||
@@ -52,33 +40,11 @@ function disableAllAsyncHooks() { | |||
| 52 | 40 | hooks_array.forEach((hook) => { hook.disable(); }); | |
| 53 | 41 | } | |
| 54 | 42 | ||
| 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 | - | ||
| 76 | 43 | function setCoverageDirectory(dir) { | |
| 77 | 44 | coverageDirectory = dir; | |
| 78 | 45 | } | |
| 79 | 46 | ||
| 80 | 47 | module.exports = { | |
| 81 | - startCoverageCollection, | ||
| 82 | 48 | writeCoverage, | |
| 83 | 49 | setCoverageDirectory | |
| 84 | 50 | }; | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -329,6 +329,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2; | |||
| 329 | 329 | V(async_wrap_ctor_template, v8::FunctionTemplate) \ | |
| 330 | 330 | V(async_wrap_object_ctor_template, v8::FunctionTemplate) \ | |
| 331 | 331 | V(buffer_prototype_object, v8::Object) \ | |
| 332 | + V(coverage_connection, v8::Object) \ | ||
| 332 | 333 | V(context, v8::Context) \ | |
| 333 | 334 | V(crypto_key_object_constructor, v8::Function) \ | |
| 334 | 335 | V(domain_callback, v8::Function) \ | |
@@ -364,6 +365,7 @@ constexpr size_t kFsStatsBufferLength = kFsStatsFieldsNumber * 2; | |||
| 364 | 365 | V(message_event_object_template, v8::ObjectTemplate) \ | |
| 365 | 366 | V(message_port_constructor_template, v8::FunctionTemplate) \ | |
| 366 | 367 | V(native_module_require, v8::Function) \ | |
| 368 | + V(on_coverage_message_function, v8::Function) \ | ||
| 367 | 369 | V(performance_entry_callback, v8::Function) \ | |
| 368 | 370 | V(performance_entry_template, v8::Function) \ | |
| 369 | 371 | V(pipe_constructor_template, v8::FunctionTemplate) \ | |
@@ -448,9 +450,10 @@ struct ContextInfo { | |||
| 448 | 450 | ||
| 449 | 451 | // Listing the AsyncWrap provider types first enables us to cast directly | |
| 450 | 452 | // 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) | ||
| 454 | 457 | ||
| 455 | 458 | enum class DebugCategory { | |
| 456 | 459 | #define V(name) name, | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -45,6 +45,7 @@ | |||
| 45 | 45 | '../../src/inspector_io.cc', | |
| 46 | 46 | '../../src/inspector_agent.h', | |
| 47 | 47 | '../../src/inspector_io.h', | |
| 48 | + '../../src/inspector_coverage.cc', | ||
| 48 | 49 | '../../src/inspector_js_api.cc', | |
| 49 | 50 | '../../src/inspector_socket.cc', | |
| 50 | 51 | '../../src/inspector_socket.h', | |
| Original file line number | Diff line number | Diff 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) | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,6 +19,7 @@ | |||
| 19 | 19 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE | |
| 20 | 20 | // USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| 21 | 21 | ||
| 22 | + #include "debug_utils.h" | ||
| 22 | 23 | #include "node_binding.h" | |
| 23 | 24 | #include "node_buffer.h" | |
| 24 | 25 | #include "node_constants.h" | |
@@ -230,6 +231,18 @@ MaybeLocal<Value> RunBootstrapping(Environment* env) { | |||
| 230 | 231 | Isolate* isolate = env->isolate(); | |
| 231 | 232 | Local<Context> context = env->context(); | |
| 232 | 233 | ||
| 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 | + | ||
| 233 | 246 | // Add a reference to the global object | |
| 234 | 247 | Local<Object> global = context->Global(); | |
| 235 | 248 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -21,6 +21,12 @@ | |||
| 21 | 21 | #define NODE_BUILTIN_REPORT_MODULES(V) | |
| 22 | 22 | #endif | |
| 23 | 23 | ||
| 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 | + | ||
| 24 | 30 | // A list of built-in modules. In order to do module registration | |
| 25 | 31 | // in node::Init(), need to add built-in modules in the following list. | |
| 26 | 32 | // Then in binding::RegisterBuiltinModules(), it calls modules' registration | |
@@ -77,7 +83,8 @@ | |||
| 77 | 83 | NODE_BUILTIN_STANDARD_MODULES(V) \ | |
| 78 | 84 | NODE_BUILTIN_OPENSSL_MODULES(V) \ | |
| 79 | 85 | NODE_BUILTIN_ICU_MODULES(V) \ | |
| 80 | - NODE_BUILTIN_REPORT_MODULES(V) | ||
| 86 | + NODE_BUILTIN_REPORT_MODULES(V) \ | ||
| 87 | + NODE_BUILTIN_COVERAGE_MODULES(V) | ||
| 81 | 88 | ||
| 82 | 89 | // This is used to load built-in modules. Instead of using | |
| 83 | 90 | // __attribute__((constructor)), we call the _register_<modname> | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -269,7 +269,9 @@ void DefineZlibConstants(v8::Local<v8::Object> target); | |||
| 269 | 269 | v8::MaybeLocal<v8::Value> RunBootstrapping(Environment* env); | |
| 270 | 270 | v8::MaybeLocal<v8::Value> StartExecution(Environment* env, | |
| 271 | 271 | const char* main_script_id); | |
| 272 | - | ||
| 272 | + namespace coverage { | ||
| 273 | + bool StartCoverageCollection(Environment* env); | ||
| 274 | + } | ||
| 273 | 275 | } // namespace node | |
| 274 | 276 | ||
| 275 | 277 | #endif // defined(NODE_WANT_INTERNALS) && NODE_WANT_INTERNALS | |
| Back | FazBrowse Home | New Git URL |
0 commit comments