GeezerVison is showing github.com — src: implement --trace-promises · nodejs/node@c3abdc5
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 clipboardExpand all lines: src/env.cc +58Lines changed: 58 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 30 | 30 | #include <iostream> | 31 | 31 | #include <limits> | 32 | 32 | #include <memory> | | 33 | +#include <optional> | 33 | 34 | #include <unordered_map> | 34 | 35 |
| 35 | 36 | namespace node { | @@ -55,13 +56,16 @@ using v8::Number; | 55 | 56 | using v8::Object; | 56 | 57 | using v8::ObjectTemplate; | 57 | 58 | using v8::Private; | | 59 | +using v8::Promise; | | 60 | +using v8::PromiseHookType; | 58 | 61 | using v8::Script; | 59 | 62 | using v8::SnapshotCreator; | 60 | 63 | using v8::StackTrace; | 61 | 64 | using v8::String; | 62 | 65 | using v8::Symbol; | 63 | 66 | using v8::TracingController; | 64 | 67 | using v8::TryCatch; | | 68 | +using v8::Uint32; | 65 | 69 | using v8::Undefined; | 66 | 70 | using v8::Value; | 67 | 71 | using v8::WrapperDescriptor; | @@ -1839,6 +1843,60 @@ void Environment::BuildEmbedderGraph(Isolate* isolate, | 1839 | 1843 | tracker.Track(env); | 1840 | 1844 | } | 1841 | 1845 |
| | 1846 | +std::optional<uint32_t> GetPromiseId(Environment* env, Local<Promise> promise) { | | 1847 | + Local<Value> id_val; | | 1848 | + if (!promise->GetPrivate(env->context(), env->promise_trace_id()) | | 1849 | + .ToLocal(&id_val) || | | 1850 | + !id_val->IsUint32()) { | | 1851 | + return std::nullopt; | | 1852 | + } | | 1853 | + return id_val.As<Uint32>()->Value(); | | 1854 | +} | | 1855 | + | | 1856 | +void Environment::TracePromises(PromiseHookType type, | | 1857 | + Local<Promise> promise, | | 1858 | + Local<Value> parent) { | | 1859 | + // We don't care about the execution of promises, just the | | 1860 | + // creation/resolution. | | 1861 | + if (type == PromiseHookType::kBefore || type == PromiseHookType::kAfter) { | | 1862 | + return; | | 1863 | + } | | 1864 | + Isolate* isolate = Isolate::GetCurrent(); | | 1865 | + Local<Context> context = isolate->GetCurrentContext(); | | 1866 | + Environment* env = Environment::GetCurrent(context); | | 1867 | + if (env == nullptr) return; | | 1868 | + | | 1869 | + std::optional<uint32_t> parent_id; | | 1870 | + if (!parent.IsEmpty() && parent->IsPromise()) { | | 1871 | + parent_id = GetPromiseId(env, parent.As<Promise>()); | | 1872 | + } | | 1873 | + | | 1874 | + uint32_t id = 0; | | 1875 | + std::string action; | | 1876 | + if (type == PromiseHookType::kInit) { | | 1877 | + id = env->trace_promise_id_counter_++; | | 1878 | + promise->SetPrivate( | | 1879 | + context, env->promise_trace_id(), Uint32::New(isolate, id)); | | 1880 | + action = "created"; | | 1881 | + } else if (type == PromiseHookType::kResolve) { | | 1882 | + auto opt = GetPromiseId(env, promise); | | 1883 | + if (!opt.has_value()) return; | | 1884 | + id = opt.value(); | | 1885 | + action = "resolved"; | | 1886 | + } else { | | 1887 | + UNREACHABLE(); | | 1888 | + } | | 1889 | + | | 1890 | + FPrintF(stderr, "[--trace-promises] "); | | 1891 | + if (parent_id.has_value()) { | | 1892 | + FPrintF(stderr, "promise #%d ", parent_id.value()); | | 1893 | + } | | 1894 | + FPrintF(stderr, "%s promise #%d\n", action, id); | | 1895 | + // TODO(joyeecheung): we can dump the native stack trace too if the | | 1896 | + // JS stack trace is empty i.e. it may be resolved on the native side. | | 1897 | + PrintCurrentStackTrace(isolate); | | 1898 | +} | | 1899 | + | 1842 | 1900 | size_t Environment::NearHeapLimitCallback(void* data, | 1843 | 1901 | size_t current_heap_limit, | 1844 | 1902 | size_t initial_heap_limit) { | |
Collapse file Copy file name to clipboardExpand all lines: src/env.h +4Lines changed: 4 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -934,6 +934,9 @@ class Environment : public MemoryRetainer { | 934 | 934 | inline void RemoveCleanupHook(CleanupQueue::Callback cb, void* arg); | 935 | 935 | void RunCleanup(); | 936 | 936 |
| | 937 | + static void TracePromises(v8::PromiseHookType type, | | 938 | + v8::Local<v8::Promise> promise, | | 939 | + v8::Local<v8::Value> parent); | 937 | 940 | static size_t NearHeapLimitCallback(void* data, | 938 | 941 | size_t current_heap_limit, | 939 | 942 | size_t initial_heap_limit); | @@ -1094,6 +1097,7 @@ class Environment : public MemoryRetainer { | 1094 | 1097 | uint32_t module_id_counter_ = 0; | 1095 | 1098 | uint32_t script_id_counter_ = 0; | 1096 | 1099 | uint32_t function_id_counter_ = 0; | | 1100 | + uint32_t trace_promise_id_counter_ = 0; | 1097 | 1101 |
| 1098 | 1102 | AliasedInt32Array exit_info_; | 1099 | 1103 |
| |
Collapse file Copy file name to clipboardExpand all lines: src/env_properties.h +1Lines changed: 1 addition & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
| 28 | 28 | V(napi_wrapper, "node:napi:wrapper") \ | 29 | 29 | V(untransferable_object_private_symbol, "node:untransferableObject") \ | 30 | 30 | V(exit_info_private_symbol, "node:exit_info_private_symbol") \ | | 31 | + V(promise_trace_id, "node:promise_trace_id") \ | 31 | 32 | V(require_private_symbol, "node:require_private_symbol") | 32 | 33 |
| 33 | 34 | // Symbols are per-isolate primitives but Environment proxies them | |
Collapse file Copy file name to clipboardExpand all lines: src/node.cc +3Lines changed: 3 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -264,6 +264,9 @@ void Environment::InitializeDiagnostics() { | 264 | 264 | env->isolate()->SetAtomicsWaitCallback(nullptr, nullptr); | 265 | 265 | }, this); | 266 | 266 | } | | 267 | + if (options_->trace_promises) { | | 268 | + isolate_->SetPromiseHook(TracePromises); | | 269 | + } | 267 | 270 | } | 268 | 271 |
| 269 | 272 | static | |
Collapse file Copy file name to clipboardExpand all lines: src/node_errors.cc +7Lines changed: 7 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -291,6 +291,13 @@ void PrintStackTrace(Isolate* isolate, | 291 | 291 | PrintToStderrAndFlush(FormatStackTrace(isolate, stack, prefix)); | 292 | 292 | } | 293 | 293 |
| | 294 | +void PrintCurrentStackTrace(Isolate* isolate, StackTracePrefix prefix) { | | 295 | + Local<StackTrace> stack; | | 296 | + if (GetCurrentStackTrace(isolate).ToLocal(&stack)) { | | 297 | + PrintStackTrace(isolate, stack, prefix); | | 298 | + } | | 299 | +} | | 300 | + | 294 | 301 | std::string FormatCaughtException(Isolate* isolate, | 295 | 302 | Local<Context> context, | 296 | 303 | Local<Value> err, | |
Collapse file Copy file name to clipboardExpand all lines: src/node_internals.h +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -87,6 +87,8 @@ enum class StackTracePrefix { | 87 | 87 | kAt, // " at " | 88 | 88 | kNumber | 89 | 89 | }; | | 90 | +void PrintCurrentStackTrace(v8::Isolate* isolate, | | 91 | + StackTracePrefix prefix = StackTracePrefix::kAt); | 90 | 92 | void PrintStackTrace(v8::Isolate* isolate, | 91 | 93 | v8::Local<v8::StackTrace> stack, | 92 | 94 | StackTracePrefix prefix = StackTracePrefix::kAt); | |
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 |
|---|
@@ -672,6 +672,10 @@ EnvironmentOptionsParser::EnvironmentOptionsParser() { | 672 | 672 | "show stack traces on process warnings", | 673 | 673 | &EnvironmentOptions::trace_warnings, | 674 | 674 | kAllowedInEnvvar); | | 675 | + AddOption("--trace-promises", | | 676 | + "show stack traces on promise initialization and resolution", | | 677 | + &EnvironmentOptions::trace_promises, | | 678 | + kAllowedInEnvvar); | 675 | 679 | AddOption("--experimental-default-type", | 676 | 680 | "set module system to use by default", | 677 | 681 | &EnvironmentOptions::type, | |
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 |
|---|
@@ -179,6 +179,7 @@ class EnvironmentOptions : public Options { | 179 | 179 | bool trace_tls = false; | 180 | 180 | bool trace_uncaught = false; | 181 | 181 | bool trace_warnings = false; | | 182 | + bool trace_promises = false; | 182 | 183 | bool extra_info_on_fatal_exception = true; | 183 | 184 | std::string unhandled_rejections; | 184 | 185 | std::vector<std::string> userland_loaders; | |
Collapse file Copy file name to clipboardExpand all lines: test/parallel/test-process-env-allowed-flags-are-documented.js +2Lines changed: 2 additions & 0 deletions | Original file line number | Diff line number | Diff line change |
|---|
@@ -106,6 +106,8 @@ assert(undocumented.delete('--no-node-snapshot')); | 106 | 106 | assert(undocumented.delete('--loader')); | 107 | 107 | assert(undocumented.delete('--verify-base-objects')); | 108 | 108 | assert(undocumented.delete('--no-verify-base-objects')); | | 109 | +assert(undocumented.delete('--trace-promises')); | | 110 | +assert(undocumented.delete('--no-trace-promises')); | 109 | 111 |
| 110 | 112 | // Remove negated versions of the flags. | 111 | 113 | for (const flag of undocumented) { | |
|