GeezerVison is showing github.com — cli: add `--trace-atomics-wait` flag · nodejs/node@2dc5db8
File treeExpand file treeCollapse file tree Open diff view settings Expand file treeCollapse file tree Open diff view settings Collapse file Copy file name to clipboard +15Lines changed: 15 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | +/* global SharedArrayBuffer */ | | 3 | + | | 4 | +const common = require('../common.js'); | | 5 | +const bench = common.createBenchmark(main, { | | 6 | + n: [1e7] | | 7 | +}); | | 8 | + | | 9 | +function main({ n }) { | | 10 | + const i32arr = new Int32Array(new SharedArrayBuffer(4)); | | 11 | + bench.start(); | | 12 | + for (let i = 0; i < n; i++) | | 13 | + Atomics.wait(i32arr, 0, 1); // Will return immediately. | | 14 | + bench.end(n); | | 15 | +} |
Collapse file Copy file name to clipboardExpand all lines: doc/api/cli.md +30Lines changed: 30 additions & 0 deletions - Display the source diff
- Display the rich diff
| Original file line number | Diff line number | Diff line change |
|---|
@@ -814,6 +814,33 @@ added: v12.0.0 | 814 | 814 | Set default [`tls.DEFAULT_MIN_VERSION`][] to 'TLSv1.3'. Use to disable support | 815 | 815 | for TLSv1.2, which is not as secure as TLSv1.3. | 816 | 816 |
| | 817 | +### `--trace-atomics-wait` | | 818 | +<!-- YAML | | 819 | +added: REPLACEME | | 820 | +--> | | 821 | + | | 822 | +Print short summaries of calls to [`Atomics.wait()`][] to stderr. | | 823 | +The output could look like this: | | 824 | + | | 825 | +```text | | 826 | +(node:15701) [Thread 0] Atomics.wait(<address> + 0, 1, inf) started | | 827 | +(node:15701) [Thread 0] Atomics.wait(<address> + 0, 1, inf) did not wait because the values mismatched | | 828 | +(node:15701) [Thread 0] Atomics.wait(<address> + 0, 0, 10) started | | 829 | +(node:15701) [Thread 0] Atomics.wait(<address> + 0, 0, 10) timed out | | 830 | +(node:15701) [Thread 0] Atomics.wait(<address> + 4, 0, inf) started | | 831 | +(node:15701) [Thread 1] Atomics.wait(<address> + 4, -1, inf) started | | 832 | +(node:15701) [Thread 0] Atomics.wait(<address> + 4, 0, inf) was woken up by another thread | | 833 | +(node:15701) [Thread 1] Atomics.wait(<address> + 4, -1, inf) was woken up by another thread | | 834 | +``` | | 835 | + | | 836 | +The fields here correspond to: | | 837 | + | | 838 | +* The thread id as given by [`worker_threads.threadId`][] | | 839 | +* The base address of the `SharedArrayBuffer` in question, as well as the | | 840 | + byte offset corresponding to the index passed to `Atomics.wait()` | | 841 | +* The expected value that was passed to `Atomics.wait()` | | 842 | +* The timeout passed to `Atomics.wait` | | 843 | + | 817 | 844 | ### `--trace-deprecation` | 818 | 845 | <!-- YAML | 819 | 846 | added: v0.8.0 | @@ -1203,6 +1230,7 @@ Node.js options that are allowed are: | 1203 | 1230 | * `--tls-min-v1.1` | 1204 | 1231 | * `--tls-min-v1.2` | 1205 | 1232 | * `--tls-min-v1.3` | | 1233 | +* `--trace-atomics-wait` | 1206 | 1234 | * `--trace-deprecation` | 1207 | 1235 | * `--trace-event-categories` | 1208 | 1236 | * `--trace-event-file-pattern` | @@ -1472,12 +1500,14 @@ $ node --max-old-space-size=1536 index.js | 1472 | 1500 | ``` | 1473 | 1501 |
| 1474 | 1502 | [`--openssl-config`]: #cli_openssl_config_file | | 1503 | +[`Atomics.wait()`]: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Atomics/wait | 1475 | 1504 | [`Buffer`]: buffer.html#buffer_class_buffer | 1476 | 1505 | [`SlowBuffer`]: buffer.html#buffer_class_slowbuffer | 1477 | 1506 | [`process.setUncaughtExceptionCaptureCallback()`]: process.html#process_process_setuncaughtexceptioncapturecallback_fn | 1478 | 1507 | [`tls.DEFAULT_MAX_VERSION`]: tls.html#tls_tls_default_max_version | 1479 | 1508 | [`tls.DEFAULT_MIN_VERSION`]: tls.html#tls_tls_default_min_version | 1480 | 1509 | [`unhandledRejection`]: process.html#process_event_unhandledrejection | | 1510 | +[`worker_threads.threadId`]: worker_threads.html##worker_threads_worker_threadid | 1481 | 1511 | [Chrome DevTools Protocol]: https://chromedevtools.github.io/devtools-protocol/ | 1482 | 1512 | [REPL]: repl.html | 1483 | 1513 | [ScriptCoverage]: https://chromedevtools.github.io/devtools-protocol/tot/Profiler#type-ScriptCoverage | |
Collapse file Copy file name to clipboardExpand all lines: doc/node.1 +4Lines changed: 4 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -363,6 +363,10 @@ but the option is supported for compatibility with older Node.js versions. | 363 | 363 | Set default minVersion to 'TLSv1.3'. Use to disable support for TLSv1.2 in | 364 | 364 | favour of TLSv1.3, which is more secure. | 365 | 365 | . | | 366 | +.It Fl -trace-atomics-wait | | 367 | +Print short summaries of calls to | | 368 | +.Sy Atomics.wait() . | | 369 | +. | 366 | 370 | .It Fl -trace-deprecation | 367 | 371 | Print stack traces for deprecations. | 368 | 372 | . | |
Collapse file Copy file name to clipboardExpand all lines: src/node.cc +45Lines changed: 45 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -227,11 +227,56 @@ int Environment::InitializeInspector( | 227 | 227 | } | 228 | 228 | #endif // HAVE_INSPECTOR && NODE_USE_V8_PLATFORM | 229 | 229 |
| | 230 | +#define ATOMIC_WAIT_EVENTS(V) \ | | 231 | + V(kStartWait, "started") \ | | 232 | + V(kWokenUp, "was woken up by another thread") \ | | 233 | + V(kTimedOut, "timed out") \ | | 234 | + V(kTerminatedExecution, "was stopped by terminated execution") \ | | 235 | + V(kAPIStopped, "was stopped through the embedder API") \ | | 236 | + V(kNotEqual, "did not wait because the values mismatched") \ | | 237 | + | | 238 | +static void AtomicsWaitCallback(Isolate::AtomicsWaitEvent event, | | 239 | + Local<v8::SharedArrayBuffer> array_buffer, | | 240 | + size_t offset_in_bytes, int64_t value, | | 241 | + double timeout_in_ms, | | 242 | + Isolate::AtomicsWaitWakeHandle* stop_handle, | | 243 | + void* data) { | | 244 | + Environment* env = static_cast<Environment*>(data); | | 245 | + | | 246 | + const char* message; | | 247 | + switch (event) { | | 248 | +#define V(key, msg) \ | | 249 | + case Isolate::AtomicsWaitEvent::key: \ | | 250 | + message = msg; \ | | 251 | + break; | | 252 | + ATOMIC_WAIT_EVENTS(V) | | 253 | +#undef V | | 254 | + } | | 255 | + | | 256 | + fprintf(stderr, | | 257 | + "(node:%d) [Thread %" PRIu64 "] Atomics.wait(%p + %zx, %" PRId64 | | 258 | + ", %.f) %s\n", | | 259 | + static_cast<int>(uv_os_getpid()), | | 260 | + env->thread_id(), | | 261 | + array_buffer->GetBackingStore()->Data(), | | 262 | + offset_in_bytes, | | 263 | + value, | | 264 | + timeout_in_ms, | | 265 | + message); | | 266 | +} | | 267 | + | 230 | 268 | void Environment::InitializeDiagnostics() { | 231 | 269 | isolate_->GetHeapProfiler()->AddBuildEmbedderGraphCallback( | 232 | 270 | Environment::BuildEmbedderGraph, this); | 233 | 271 | if (options_->trace_uncaught) | 234 | 272 | isolate_->SetCaptureStackTraceForUncaughtExceptions(true); | | 273 | + if (options_->trace_atomics_wait) { | | 274 | + isolate_->SetAtomicsWaitCallback(AtomicsWaitCallback, this); | | 275 | + AddCleanupHook([](void* data) { | | 276 | + Environment* env = static_cast<Environment*>(data); | | 277 | + env->isolate()->SetAtomicsWaitCallback(nullptr, nullptr); | | 278 | + }, this); | | 279 | + } | 235 | 280 |
| 236 | 281 | #if defined HAVE_DTRACE || defined HAVE_ETW | 237 | 282 | InitDTrace(this); | |
Collapse file Copy file name to clipboardExpand all lines: src/node_options.cc +4Lines changed: 4 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -435,6 +435,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { | 435 | 435 | "throw an exception on deprecations", | 436 | 436 | &EnvironmentOptions::throw_deprecation, | 437 | 437 | kAllowedInEnvironment); | | 438 | + AddOption("--trace-atomics-wait", | | 439 | + "trace Atomics.wait() operations", | | 440 | + &EnvironmentOptions::trace_atomics_wait, | | 441 | + kAllowedInEnvironment); | 438 | 442 | AddOption("--trace-deprecation", | 439 | 443 | "show stack traces on deprecations", | 440 | 444 | &EnvironmentOptions::trace_deprecation, | |
Collapse file Copy file name to clipboardExpand all lines: src/node_options.h +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -140,6 +140,7 @@ class EnvironmentOptions : public Options { | 140 | 140 | std::string redirect_warnings; | 141 | 141 | bool test_udp_no_try_send = false; | 142 | 142 | bool throw_deprecation = false; | | 143 | + bool trace_atomics_wait = false; | 143 | 144 | bool trace_deprecation = false; | 144 | 145 | bool trace_exit = false; | 145 | 146 | bool trace_sync_io = false; | |
Collapse file Copy file name to clipboard +79Lines changed: 79 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| | 1 | +'use strict'; | | 2 | +require('../common'); | | 3 | +const assert = require('assert'); | | 4 | +const child_process = require('child_process'); | | 5 | +const { Worker } = require('worker_threads'); | | 6 | + | | 7 | +if (process.argv[2] === 'child') { | | 8 | + const i32arr = new Int32Array(new SharedArrayBuffer(8)); | | 9 | + assert.strictEqual(Atomics.wait(i32arr, 0, 1), 'not-equal'); | | 10 | + assert.strictEqual(Atomics.wait(i32arr, 0, 0, 10), 'timed-out'); | | 11 | + | | 12 | + new Worker(` | | 13 | + const i32arr = require('worker_threads').workerData; | | 14 | + Atomics.store(i32arr, 1, -1); | | 15 | + Atomics.notify(i32arr, 1); | | 16 | + Atomics.wait(i32arr, 1, -1); | | 17 | + `, { eval: true, workerData: i32arr }); | | 18 | + | | 19 | + Atomics.wait(i32arr, 1, 0); | | 20 | + assert.strictEqual(Atomics.load(i32arr, 1), -1); | | 21 | + Atomics.store(i32arr, 1, 0); | | 22 | + Atomics.notify(i32arr, 1); | | 23 | + return; | | 24 | +} | | 25 | + | | 26 | +const proc = child_process.spawnSync( | | 27 | + process.execPath, | | 28 | + [ '--trace-atomics-wait', __filename, 'child' ], | | 29 | + { encoding: 'utf8', stdio: [ 'inherit', 'inherit', 'pipe' ] }); | | 30 | + | | 31 | +if (proc.status !== 0) console.log(proc); | | 32 | +assert.strictEqual(proc.status, 0); | | 33 | + | | 34 | +const SABAddress = proc.stderr.match(/Atomics\.wait\((?<SAB>.+) \+/).groups.SAB; | | 35 | +const actualTimeline = proc.stderr | | 36 | + .replace(new RegExp(SABAddress, 'g'), '<address>') | | 37 | + .replace(new RegExp(`\\(node:${proc.pid}\\) `, 'g'), '') | | 38 | + .replace(/\binf(inity)?\b/gi, 'inf') | | 39 | + .replace(/\r/g, '') | | 40 | + .trim(); | | 41 | +console.log('+++ normalized stdout +++'); | | 42 | +console.log(actualTimeline); | | 43 | +console.log('--- normalized stdout ---'); | | 44 | + | | 45 | +const begin = | | 46 | +`[Thread 0] Atomics.wait(<address> + 0, 1, inf) started | | 47 | +[Thread 0] Atomics.wait(<address> + 0, 1, inf) did not wait because the \ | | 48 | +values mismatched | | 49 | +[Thread 0] Atomics.wait(<address> + 0, 0, 10) started | | 50 | +[Thread 0] Atomics.wait(<address> + 0, 0, 10) timed out`; | | 51 | + | | 52 | +const expectedTimelines = [ | | 53 | + `${begin} | | 54 | +[Thread 0] Atomics.wait(<address> + 4, 0, inf) started | | 55 | +[Thread 1] Atomics.wait(<address> + 4, -1, inf) started | | 56 | +[Thread 0] Atomics.wait(<address> + 4, 0, inf) was woken up by another thread | | 57 | +[Thread 1] Atomics.wait(<address> + 4, -1, inf) was woken up by another thread`, | | 58 | + `${begin} | | 59 | +[Thread 0] Atomics.wait(<address> + 4, 0, inf) started | | 60 | +[Thread 0] Atomics.wait(<address> + 4, 0, inf) was woken up by another thread | | 61 | +[Thread 1] Atomics.wait(<address> + 4, -1, inf) started | | 62 | +[Thread 1] Atomics.wait(<address> + 4, -1, inf) did not wait because the \ | | 63 | +values mismatched`, | | 64 | + `${begin} | | 65 | +[Thread 0] Atomics.wait(<address> + 4, 0, inf) started | | 66 | +[Thread 1] Atomics.wait(<address> + 4, -1, inf) started | | 67 | +[Thread 0] Atomics.wait(<address> + 4, 0, inf) was woken up by another thread | | 68 | +[Thread 1] Atomics.wait(<address> + 4, -1, inf) did not wait because the \ | | 69 | +values mismatched`, | | 70 | + `${begin} | | 71 | +[Thread 0] Atomics.wait(<address> + 4, 0, inf) started | | 72 | +[Thread 0] Atomics.wait(<address> + 4, 0, inf) did not wait because the \ | | 73 | +values mismatched | | 74 | +[Thread 1] Atomics.wait(<address> + 4, -1, inf) started | | 75 | +[Thread 1] Atomics.wait(<address> + 4, -1, inf) did not wait because the \ | | 76 | +values mismatched` | | 77 | +]; | | 78 | + | | 79 | +assert(expectedTimelines.includes(actualTimeline)); |
|