| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1394,6 +1394,33 @@ setTimeout(() => { | |||
| 1394 | 1394 | }, 1000); | |
| 1395 | 1395 | ``` | |
| 1396 | 1396 | ||
| 1397 | + ## Class: `CPUProfileHandle` | ||
| 1398 | + | ||
| 1399 | + <!-- YAML | ||
| 1400 | + added: REPLACEME | ||
| 1401 | + --> | ||
| 1402 | + | ||
| 1403 | + ### `cpuProfileHandle.stop()` | ||
| 1404 | + | ||
| 1405 | + <!-- YAML | ||
| 1406 | + added: REPLACEME | ||
| 1407 | + --> | ||
| 1408 | + | ||
| 1409 | + * Returns: {Promise} | ||
| 1410 | + | ||
| 1411 | + Stopping collecting the profile, then return a Promise that fulfills with an error or the | ||
| 1412 | + profile data. | ||
| 1413 | + | ||
| 1414 | + ### `cpuProfileHandle[Symbol.asyncDispose]()` | ||
| 1415 | + | ||
| 1416 | + <!-- YAML | ||
| 1417 | + added: REPLACEME | ||
| 1418 | + --> | ||
| 1419 | + | ||
| 1420 | + * Returns: {Promise} | ||
| 1421 | + | ||
| 1422 | + Stopping collecting the profile and the profile will be discarded. | ||
| 1423 | + | ||
| 1397 | 1424 | ## `v8.isStringOneByteRepresentation(content)` | |
| 1398 | 1425 | ||
| 1399 | 1426 | <!-- YAML | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1953,19 +1953,16 @@ this matches its values. | |||
| 1953 | 1953 | ||
| 1954 | 1954 | If the worker has stopped, the return value is an empty object. | |
| 1955 | 1955 | ||
| 1956 | - ### `worker.startCpuProfile(name)` | ||
| 1956 | + ### `worker.startCpuProfile()` | ||
| 1957 | 1957 | ||
| 1958 | 1958 | <!-- YAML | |
| 1959 | 1959 | added: REPLACEME | |
| 1960 | 1960 | --> | |
| 1961 | 1961 | ||
| 1962 | - * name: {string} | ||
| 1963 | 1962 | * Returns: {Promise} | |
| 1964 | 1963 | ||
| 1965 | - Starting a CPU profile with the given `name`, then return a Promise that fulfills | ||
| 1966 | - with an error or an object which has a `stop` method. Calling the `stop` method will | ||
| 1967 | - stop collecting the profile, then return a Promise that fulfills with an error or the | ||
| 1968 | - profile data. | ||
| 1964 | + Starting a CPU profile then return a Promise that fulfills with an error | ||
| 1965 | + or an `CPUProfileHandle` object. This API supports `await using` syntax. | ||
| 1969 | 1966 | ||
| 1970 | 1967 | ```cjs | |
| 1971 | 1968 | const { Worker } = require('node:worker_threads'); | |
@@ -1976,13 +1973,29 @@ const worker = new Worker(` | |||
| 1976 | 1973 | `, { eval: true }); | |
| 1977 | 1974 | ||
| 1978 | 1975 | worker.on('online', async () => { | |
| 1979 | - const handle = await worker.startCpuProfile('demo'); | ||
| 1976 | + const handle = await worker.startCpuProfile(); | ||
| 1980 | 1977 | const profile = await handle.stop(); | |
| 1981 | 1978 | console.log(profile); | |
| 1982 | 1979 | worker.terminate(); | |
| 1983 | 1980 | }); | |
| 1984 | 1981 | ``` | |
| 1985 | 1982 | ||
| 1983 | + `await using` example. | ||
| 1984 | + | ||
| 1985 | + ```cjs | ||
| 1986 | + const { Worker } = require('node::worker_threads'); | ||
| 1987 | + | ||
| 1988 | + const w = new Worker(` | ||
| 1989 | + const { parentPort } = require('worker_threads'); | ||
| 1990 | + parentPort.on('message', () => {}); | ||
| 1991 | + `, { eval: true }); | ||
| 1992 | + | ||
| 1993 | + w.on('online', async () => { | ||
| 1994 | + // Stop profile automatically when return and profile will be discarded | ||
| 1995 | + await using handle = await w.startCpuProfile(); | ||
| 1996 | + }); | ||
| 1997 | + ``` | ||
| 1998 | + | ||
| 1986 | 1999 | ### `worker.stderr` | |
| 1987 | 2000 | ||
| 1988 | 2001 | <!-- YAML | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -133,6 +133,37 @@ function assignEnvironmentData(data) { | |||
| 133 | 133 | }); | |
| 134 | 134 | } | |
| 135 | 135 | ||
| 136 | + class CPUProfileHandle { | ||
| 137 | + #worker = null; | ||
| 138 | + #id = null; | ||
| 139 | + #promise = null; | ||
| 140 | + | ||
| 141 | + constructor(worker, id) { | ||
| 142 | + this.#worker = worker; | ||
| 143 | + this.#id = id; | ||
| 144 | + } | ||
| 145 | + | ||
| 146 | + stop() { | ||
| 147 | + if (this.#promise) { | ||
| 148 | + return this.#promise; | ||
| 149 | + } | ||
| 150 | + const stopTaker = this.#worker[kHandle]?.stopCpuProfile(this.#id); | ||
| 151 | + return this.#promise = new Promise((resolve, reject) => { | ||
| 152 | + if (!stopTaker) return reject(new ERR_WORKER_NOT_RUNNING()); | ||
| 153 | + stopTaker.ondone = (err, profile) => { | ||
| 154 | + if (err) { | ||
| 155 | + return reject(err); | ||
| 156 | + } | ||
| 157 | + resolve(profile); | ||
| 158 | + }; | ||
| 159 | + }); | ||
| 160 | + }; | ||
| 161 | + | ||
| 162 | + async [SymbolAsyncDispose]() { | ||
| 163 | + await this.stop(); | ||
| 164 | + } | ||
| 165 | + } | ||
| 166 | + | ||
| 136 | 167 | class Worker extends EventEmitter { | |
| 137 | 168 | constructor(filename, options = kEmptyObject) { | |
| 138 | 169 | throwIfBuildingSnapshot('Creating workers'); | |
@@ -516,37 +547,15 @@ class Worker extends EventEmitter { | |||
| 516 | 547 | } | |
| 517 | 548 | ||
| 518 | 549 | // TODO(theanarkh): add options, such as sample_interval, CpuProfilingMode | |
| 519 | - startCpuProfile(name) { | ||
| 520 | - validateString(name, 'name'); | ||
| 521 | - const startTaker = this[kHandle]?.startCpuProfile(name); | ||
| 550 | + startCpuProfile() { | ||
| 551 | + const startTaker = this[kHandle]?.startCpuProfile(); | ||
| 522 | 552 | return new Promise((resolve, reject) => { | |
| 523 | 553 | if (!startTaker) return reject(new ERR_WORKER_NOT_RUNNING()); | |
| 524 | - startTaker.ondone = (err) => { | ||
| 554 | + startTaker.ondone = (err, id) => { | ||
| 525 | 555 | if (err) { | |
| 526 | 556 | return reject(err); | |
| 527 | 557 | } | |
| 528 | - let promise = null; | ||
| 529 | - const stop = () => { | ||
| 530 | - if (promise) { | ||
| 531 | - return promise; | ||
| 532 | - } | ||
| 533 | - const stopTaker = this[kHandle]?.stopCpuProfile(name); | ||
| 534 | - return promise = new Promise((resolve, reject) => { | ||
| 535 | - if (!stopTaker) return reject(new ERR_WORKER_NOT_RUNNING()); | ||
| 536 | - stopTaker.ondone = (status, profile) => { | ||
| 537 | - if (err) { | ||
| 538 | - return reject(err); | ||
| 539 | - } | ||
| 540 | - resolve(profile); | ||
| 541 | - }; | ||
| 542 | - }); | ||
| 543 | - }; | ||
| 544 | - resolve({ | ||
| 545 | - stop, | ||
| 546 | - async [SymbolAsyncDispose]() { | ||
| 547 | - await stop(); | ||
| 548 | - }, | ||
| 549 | - }); | ||
| 558 | + resolve(new CPUProfileHandle(this, id)); | ||
| 550 | 559 | }; | |
| 551 | 560 | }); | |
| 552 | 561 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1060,7 +1060,7 @@ Environment::~Environment() { | |||
| 1060 | 1060 | delete external_memory_accounter_; | |
| 1061 | 1061 | if (cpu_profiler_) { | |
| 1062 | 1062 | for (auto& it : pending_profiles_) { | |
| 1063 | - cpu_profiler_->Stop(it.second); | ||
| 1063 | + cpu_profiler_->Stop(it); | ||
| 1064 | 1064 | } | |
| 1065 | 1065 | cpu_profiler_->Dispose(); | |
| 1066 | 1066 | cpu_profiler_ = nullptr; | |
@@ -2229,30 +2229,30 @@ void Environment::RunWeakRefCleanup() { | |||
| 2229 | 2229 | isolate()->ClearKeptObjects(); | |
| 2230 | 2230 | } | |
| 2231 | 2231 | ||
| 2232 | - v8::CpuProfilingResult Environment::StartCpuProfile(std::string_view name) { | ||
| 2232 | + v8::CpuProfilingResult Environment::StartCpuProfile() { | ||
| 2233 | 2233 | HandleScope handle_scope(isolate()); | |
| 2234 | 2234 | if (!cpu_profiler_) { | |
| 2235 | 2235 | cpu_profiler_ = v8::CpuProfiler::New(isolate()); | |
| 2236 | 2236 | } | |
| 2237 | - Local<Value> title = | ||
| 2238 | - node::ToV8Value(context(), name, isolate()).ToLocalChecked(); | ||
| 2239 | - v8::CpuProfilingResult result = | ||
| 2240 | - cpu_profiler_->Start(title.As<String>(), true); | ||
| 2237 | + v8::CpuProfilingResult result = cpu_profiler_->Start( | ||
| 2238 | + v8::CpuProfilingOptions{v8::CpuProfilingMode::kLeafNodeLineNumbers, | ||
| 2239 | + v8::CpuProfilingOptions::kNoSampleLimit}); | ||
| 2241 | 2240 | if (result.status == v8::CpuProfilingStatus::kStarted) { | |
| 2242 | - pending_profiles_.emplace(name, result.id); | ||
| 2241 | + pending_profiles_.push_back(result.id); | ||
| 2243 | 2242 | } | |
| 2244 | 2243 | return result; | |
| 2245 | 2244 | } | |
| 2246 | 2245 | ||
| 2247 | - v8::CpuProfile* Environment::StopCpuProfile(std::string_view name) { | ||
| 2246 | + v8::CpuProfile* Environment::StopCpuProfile(v8::ProfilerId profile_id) { | ||
| 2248 | 2247 | if (!cpu_profiler_) { | |
| 2249 | 2248 | return nullptr; | |
| 2250 | 2249 | } | |
| 2251 | - auto it = pending_profiles_.find(std::string(name)); | ||
| 2250 | + auto it = | ||
| 2251 | + std::find(pending_profiles_.begin(), pending_profiles_.end(), profile_id); | ||
| 2252 | 2252 | if (it == pending_profiles_.end()) { | |
| 2253 | 2253 | return nullptr; | |
| 2254 | 2254 | } | |
| 2255 | - v8::CpuProfile* profile = cpu_profiler_->Stop(it->second); | ||
| 2255 | + v8::CpuProfile* profile = cpu_profiler_->Stop(*it); | ||
| 2256 | 2256 | pending_profiles_.erase(it); | |
| 2257 | 2257 | return profile; | |
| 2258 | 2258 | } | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1052,8 +1052,8 @@ class Environment final : public MemoryRetainer { | |||
| 1052 | 1052 | ||
| 1053 | 1053 | inline void RemoveHeapSnapshotNearHeapLimitCallback(size_t heap_limit); | |
| 1054 | 1054 | ||
| 1055 | - v8::CpuProfilingResult StartCpuProfile(std::string_view name); | ||
| 1056 | - v8::CpuProfile* StopCpuProfile(std::string_view name); | ||
| 1055 | + v8::CpuProfilingResult StartCpuProfile(); | ||
| 1056 | + v8::CpuProfile* StopCpuProfile(v8::ProfilerId profile_id); | ||
| 1057 | 1057 | ||
| 1058 | 1058 | // Field identifiers for exit_info_ | |
| 1059 | 1059 | enum ExitInfoField { | |
@@ -1254,7 +1254,7 @@ class Environment final : public MemoryRetainer { | |||
| 1254 | 1254 | released_allocated_buffers_; | |
| 1255 | 1255 | ||
| 1256 | 1256 | v8::CpuProfiler* cpu_profiler_ = nullptr; | |
| 1257 | - std::unordered_map<std::string, v8::ProfilerId> pending_profiles_; | ||
| 1257 | + std::vector<v8::ProfilerId> pending_profiles_; | ||
| 1258 | 1258 | }; | |
| 1259 | 1259 | ||
| 1260 | 1260 | } // namespace node | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -48,7 +48,6 @@ void OOMErrorHandler(const char* location, const v8::OOMDetails& details); | |||
| 48 | 48 | V(ERR_CLOSED_MESSAGE_PORT, Error) \ | |
| 49 | 49 | V(ERR_CONSTRUCT_CALL_REQUIRED, TypeError) \ | |
| 50 | 50 | V(ERR_CONSTRUCT_CALL_INVALID, TypeError) \ | |
| 51 | - V(ERR_CPU_PROFILE_ALREADY_STARTED, Error) \ | ||
| 52 | 51 | V(ERR_CPU_PROFILE_NOT_STARTED, Error) \ | |
| 53 | 52 | V(ERR_CPU_PROFILE_TOO_MANY, Error) \ | |
| 54 | 53 | V(ERR_CRYPTO_CUSTOM_ENGINE_NOT_SUPPORTED, Error) \ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -915,9 +915,6 @@ void Worker::StartCpuProfile(const FunctionCallbackInfo<Value>& args) { | |||
| 915 | 915 | ASSIGN_OR_RETURN_UNWRAP(&w, args.This()); | |
| 916 | 916 | Environment* env = w->env(); | |
| 917 | 917 | ||
| 918 | - CHECK(args[0]->IsString()); | ||
| 919 | - node::Utf8Value name(env->isolate(), args[0]); | ||
| 920 | - | ||
| 921 | 918 | AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w); | |
| 922 | 919 | Local<Object> wrap; | |
| 923 | 920 | if (!env->worker_cpu_profile_taker_template() | |
@@ -930,25 +927,23 @@ void Worker::StartCpuProfile(const FunctionCallbackInfo<Value>& args) { | |||
| 930 | 927 | MakeDetachedBaseObject<WorkerCpuProfileTaker>(env, wrap); | |
| 931 | 928 | ||
| 932 | 929 | bool scheduled = w->RequestInterrupt([taker = std::move(taker), | |
| 933 | - name = name.ToString(), | ||
| 934 | 930 | env](Environment* worker_env) mutable { | |
| 935 | - CpuProfilingResult result = worker_env->StartCpuProfile(name); | ||
| 931 | + CpuProfilingResult result = worker_env->StartCpuProfile(); | ||
| 936 | 932 | env->SetImmediateThreadsafe( | |
| 937 | - [taker = std::move(taker), | ||
| 938 | - status = result.status](Environment* env) mutable { | ||
| 933 | + [taker = std::move(taker), result = result](Environment* env) mutable { | ||
| 939 | 934 | Isolate* isolate = env->isolate(); | |
| 940 | 935 | HandleScope handle_scope(isolate); | |
| 941 | 936 | Context::Scope context_scope(env->context()); | |
| 942 | 937 | AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(taker.get()); | |
| 943 | 938 | Local<Value> argv[] = { | |
| 944 | - Null(isolate), // error | ||
| 939 | + Null(isolate), // error | ||
| 940 | + Undefined(isolate), // profile id | ||
| 945 | 941 | }; | |
| 946 | - if (status == CpuProfilingStatus::kAlreadyStarted) { | ||
| 947 | - argv[0] = ERR_CPU_PROFILE_ALREADY_STARTED( | ||
| 948 | - isolate, "CPU profile already started"); | ||
| 949 | - } else if (status == CpuProfilingStatus::kErrorTooManyProfilers) { | ||
| 942 | + if (result.status == CpuProfilingStatus::kErrorTooManyProfilers) { | ||
| 950 | 943 | argv[0] = ERR_CPU_PROFILE_TOO_MANY( | |
| 951 | 944 | isolate, "There are too many CPU profiles"); | |
| 945 | + } else if (result.status == CpuProfilingStatus::kStarted) { | ||
| 946 | + argv[1] = Number::New(isolate, result.id); | ||
| 952 | 947 | } | |
| 953 | 948 | taker->MakeCallback(env->ondone_string(), arraysize(argv), argv); | |
| 954 | 949 | }, | |
@@ -965,8 +960,8 @@ void Worker::StopCpuProfile(const FunctionCallbackInfo<Value>& args) { | |||
| 965 | 960 | ASSIGN_OR_RETURN_UNWRAP(&w, args.This()); | |
| 966 | 961 | ||
| 967 | 962 | Environment* env = w->env(); | |
| 968 | - CHECK(args[0]->IsString()); | ||
| 969 | - node::Utf8Value name(env->isolate(), args[0]); | ||
| 963 | + CHECK(args[0]->IsUint32()); | ||
| 964 | + uint32_t profile_id = args[0]->Uint32Value(env->context()).FromJust(); | ||
| 970 | 965 | ||
| 971 | 966 | AsyncHooks::DefaultTriggerAsyncIdScope trigger_id_scope(w); | |
| 972 | 967 | Local<Object> wrap; | |
@@ -980,11 +975,11 @@ void Worker::StopCpuProfile(const FunctionCallbackInfo<Value>& args) { | |||
| 980 | 975 | MakeDetachedBaseObject<WorkerCpuProfileTaker>(env, wrap); | |
| 981 | 976 | ||
| 982 | 977 | bool scheduled = w->RequestInterrupt([taker = std::move(taker), | |
| 983 | - name = name.ToString(), | ||
| 978 | + profile_id = profile_id, | ||
| 984 | 979 | env](Environment* worker_env) mutable { | |
| 985 | 980 | bool found = false; | |
| 986 | 981 | auto json_out_stream = std::make_unique<node::JSONOutputStream>(); | |
| 987 | - CpuProfile* profile = worker_env->StopCpuProfile(name); | ||
| 982 | + CpuProfile* profile = worker_env->StopCpuProfile(profile_id); | ||
| 988 | 983 | if (profile) { | |
| 989 | 984 | profile->Serialize(json_out_stream.get(), | |
| 990 | 985 | CpuProfile::SerializationFormat::kJSON); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -8,40 +8,18 @@ const worker = new Worker(` | |||
| 8 | 8 | parentPort.on('message', () => {}); | |
| 9 | 9 | `, { eval: true }); | |
| 10 | 10 | ||
| 11 | - [ | ||
| 12 | - -1, | ||
| 13 | - 1.1, | ||
| 14 | - NaN, | ||
| 15 | - undefined, | ||
| 16 | - {}, | ||
| 17 | - [], | ||
| 18 | - null, | ||
| 19 | - function() {}, | ||
| 20 | - Symbol(), | ||
| 21 | - true, | ||
| 22 | - Infinity, | ||
| 23 | - ].forEach((name) => { | ||
| 24 | - try { | ||
| 25 | - worker.startCpuProfile(name); | ||
| 26 | - } catch (e) { | ||
| 27 | - assert.ok(/ERR_INVALID_ARG_TYPE/i.test(e.code)); | ||
| 28 | - } | ||
| 29 | - }); | ||
| 30 | - | ||
| 31 | - const name = 'demo'; | ||
| 32 | - | ||
| 33 | 11 | worker.on('online', common.mustCall(async () => { | |
| 34 | 12 | { | |
| 35 | - const handle = await worker.startCpuProfile(name); | ||
| 13 | + const handle = await worker.startCpuProfile(); | ||
| 36 | 14 | JSON.parse(await handle.stop()); | |
| 37 | 15 | // Stop again | |
| 38 | 16 | JSON.parse(await handle.stop()); | |
| 39 | 17 | } | |
| 40 | 18 | ||
| 41 | 19 | { | |
| 42 | 20 | const [handle1, handle2] = await Promise.all([ | |
| 43 | - worker.startCpuProfile('demo1'), | ||
| 44 | - worker.startCpuProfile('demo2'), | ||
| 21 | + worker.startCpuProfile(), | ||
| 22 | + worker.startCpuProfile(), | ||
| 45 | 23 | ]); | |
| 46 | 24 | const [profile1, profile2] = await Promise.all([ | |
| 47 | 25 | handle1.stop(), | |
@@ -52,22 +30,14 @@ worker.on('online', common.mustCall(async () => { | |||
| 52 | 30 | } | |
| 53 | 31 | ||
| 54 | 32 | { | |
| 55 | - // Calling startCpuProfile twice with same name will throw an error | ||
| 56 | - await worker.startCpuProfile(name); | ||
| 57 | - try { | ||
| 58 | - await worker.startCpuProfile(name); | ||
| 59 | - } catch (e) { | ||
| 60 | - assert.ok(/ERR_CPU_PROFILE_ALREADY_STARTED/i.test(e.code)); | ||
| 61 | - } | ||
| 62 | - // Does not need to stop the profile because it will be stopped | ||
| 63 | - // automatically when the worker is terminated | ||
| 33 | + await worker.startCpuProfile(); | ||
| 34 | + // It will be stopped automatically when the worker is terminated | ||
| 64 | 35 | } | |
| 65 | - | ||
| 66 | 36 | worker.terminate(); | |
| 67 | 37 | })); | |
| 68 | 38 | ||
| 69 | 39 | worker.once('exit', common.mustCall(async () => { | |
| 70 | - await assert.rejects(worker.startCpuProfile(name), { | ||
| 40 | + await assert.rejects(worker.startCpuProfile(), { | ||
| 71 | 41 | code: 'ERR_WORKER_NOT_RUNNING' | |
| 72 | 42 | }); | |
| 73 | 43 | })); | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -343,6 +343,7 @@ const customTypesMap = { | |||
| 343 | 343 | 'Lock': 'worker_threads.html#class-lock', | |
| 344 | 344 | 'LockManager': 'worker_threads.html#class-lockmanager', | |
| 345 | 345 | 'LockManagerSnapshot': 'https://developer.mozilla.org/en-US/docs/Web/API/LockManagerSnapshot', | |
| 346 | + 'CPUProfileHandle': 'v8.html#class-cpuprofilehandle', | ||
| 346 | 347 | }; | |
| 347 | 348 | ||
| 348 | 349 | const arrayPart = /(?:\[])+$/; | |
| Back | FazBrowse Home | New Git URL |
0 commit comments