nodejs / nodejs/node-addon-api
`napi_adjust_external_memory` crashes when `NAPI_EXPERIMENTAL` is defined
Chưa có ai nhận issue này.
- Ngôn ngữ chính
- C++
- Star
- 2.4k
- Fork
- 499
- Merge trung bình
- 2 ngày 11 giờ
- Pull request đã merge (30 ngày)
- 2
Mô tả
Background
In node-canvas we've had a lot of people report memory leaks due to destructors getting called asynchronously, so I want to use the new "nogc" APIs. But when I define NAPI_EXPERIMENTAL, our test suite quickly crashes during GC. It's trying to execute some JS, which I know is illegal, but I can't figure out what is scheduling the JS, and I didn't think that should be possible if I make an API call with napi_basic_env.
Questions
I should be able to call any API that takes basic_env both inside and outside of GC, right?
If I add the Finalizer(Napi::Env) method, node-addon-api switches to calling the destructor asynchronously with node_api_post_finalizer, which does work. That makes me think that it's crashing in the synchronous delete instance of a previous canvas instance. But I can't find anything we're calling with the extended env.
To repro, just run node repro.js from the sync-gc branch. More context here.
LLDB stack trace
[I] caleb@2024-MacBook-Pro ~/C/node-canvas (sync-gc) [SIGSEGV]> lldb node repro.js
(lldb) target create "node"
Current executable set to '/opt/homebrew/bin/node' (arm64).
(lldb) settings set -- target.run-args "repro.js"
(lldb) run
Process 98580 launched: '/opt/homebrew/bin/node' (arm64)
Process 98580 stopped
* thread #1, name = 'MainThread', queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x8e4803f0bdb0)
frame #0: 0x0000000103fd7668 libnode.141.dylib`v8impl::Reference::WeakCallback(v8::WeakCallbackInfo<v8impl::Reference> const&) + 36
libnode.141.dylib`v8impl::Reference::WeakCallback:
-> 0x103fd7668 <+36>: ldr x1, [x8, #0x30]
0x103fd766c <+40>: mov x0, x19
0x103fd7670 <+44>: ldp x29, x30, [sp, #0x10]
0x103fd7674 <+48>: ldp x20, x19, [sp], #0x20
Target 0: (node) stopped.
(lldb) bt
* thread #1, name = 'MainThread', queue = 'com.apple.main-thread', stop reason = EXC_BAD_ACCESS (code=1, address=0x8e4803f0bdb0)
* frame #0: 0x0000000103fd7668 libnode.141.dylib`v8impl::Reference::WeakCallback(v8::WeakCallbackInfo<v8impl::Reference> const&) + 36
frame #1: 0x000000010434db80 libnode.141.dylib`v8::internal::GlobalHandles::InvokeFirstPassWeakCallbacks() + 384
frame #2: 0x00000001043b0da8 libnode.141.dylib`v8::internal::Heap::PerformGarbageCollection(v8::internal::GarbageCollector, v8::internal::GarbageCollectionReason, char const*) + 996
frame #3: 0x00000001043bf610 libnode.141.dylib`v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags, v8::internal::PerformHeapLimitCheck)::$_1::operator()() const + 512
frame #4: 0x00000001043bf3f8 libnode.141.dylib`void heap::base::Stack::SetMarkerAndCallbackImpl<v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags, v8::internal::PerformHeapLimitCheck)::$_1>(heap::base::Stack*, void*, void const*) + 40
frame #5: 0x00000001049e31d8 libnode.141.dylib`PushAllRegistersAndIterateStack + 40
frame #6: 0x00000001043addc4 libnode.141.dylib`v8::internal::Heap::CollectGarbage(v8::internal::AllocationSpace, v8::internal::GarbageCollectionReason, v8::GCCallbackFlags, v8::internal::PerformHeapLimitCheck) + 436
frame #7: 0x00000001043af1bc libnode.141.dylib`v8::internal::Heap::HandleExternalMemoryInterrupt() + 364
frame #8: 0x00000001042172bc libnode.141.dylib`v8::Isolate::AdjustAmountOfExternalAllocatedMemoryImpl(long long) + 104
frame #9: 0x0000000103fe0a24 libnode.141.dylib`napi_adjust_external_memory + 36
frame #10: 0x0000000101349f80 canvas.node`Canvas::ensureSurface() [inlined] Napi::MemoryManagement::AdjustExternalMemory(env=<unavailable>, change_in_bytes=<unavailable>) at napi-inl.h:6977:7 [opt]
frame #11: 0x0000000101349f78 canvas.node`Canvas::ensureSurface(this=0x0000600003d90b40) at Canvas.cc:988:5 [opt]
frame #12: 0x00000001013494f4 canvas.node`Canvas::Canvas(this=0x0000600003d90b40, info=0x000000016fdfd5e8) at Canvas.cc:133:48 [opt]
frame #13: 0x0000000101354dbc canvas.node`Napi::ObjectWrap<Canvas>::ConstructorCallbackWrapper(napi_env__*, napi_callback_info__*)::'lambda0'()::operator()() const [inlined] Canvas::Canvas(this=0x0000600003d90b40, info=<unavailable>) at Canvas.cc:85:98 [opt]
frame #14: 0x0000000101354db4 canvas.node`Napi::ObjectWrap<Canvas>::ConstructorCallbackWrapper(this=<unavailable>)::'lambda0'()::operator()() const at napi-inl.h:5237:23 [opt]
frame #15: 0x0000000101354830 canvas.node`Napi::ObjectWrap<Canvas>::ConstructorCallbackWrapper(napi_env__*, napi_callback_info__*) [inlined] napi_value__* Napi::details::WrapCallback<Napi::ObjectWrap<Canvas>::ConstructorCallbackWrapper(napi_env__*, napi_callback_info__*)::'lambda0'()>((null)=<unavailable>, callback=(unnamed class) @ 0x000000016fdfd6b0) at napi-inl.h:113:10 [opt]
frame #16: 0x0000000101354828 canvas.node`Napi::ObjectWrap<Canvas>::ConstructorCallbackWrapper(env=<unavailable>, info=0x000000016fdfd740) at napi-inl.h:5235:24 [opt]
frame #17: 0x0000000103fe0d8c libnode.141.dylib`v8impl::(anonymous namespace)::FunctionCallbackWrapper::Invoke(v8::FunctionCallbackInfo<v8::Value> const&) + 80
frame #18: 0x000000010423c664 libnode.141.dylib`v8::internal::FunctionCallbackArguments::CallOrConstruct(v8::internal::Tagged<v8::internal::FunctionTemplateInfo>, bool) + 276
frame #19: 0x000000010423c23c libnode.141.dylib`v8::internal::MaybeHandle<v8::internal::Object> v8::internal::(anonymous namespace)::HandleApiCallHelper<true>(v8::internal::Isolate*, v8::internal::DirectHandle<v8::internal::HeapObject>, v8::internal::DirectHandle<v8::internal::FunctionTemplateInfo>, v8::internal::DirectHandle<v8::internal::Object>, unsigned long*, int) + 392
frame #20: 0x000000010423bc88 libnode.141.dylib`v8::internal::Builtin_HandleApiConstruct(int, unsigned long*, v8::internal::Isolate*) + 140
frame #21: 0x0000000103e29394 libnode.141.dylib`Builtins_CEntry_Return1_ArgvOnStack_BuiltinExit + 84
frame #22: 0x0000000103d89f90 libnode.141.dylib`Builtins_InterpreterPushArgsThenFastConstructFunction + 752
frame #23: 0x0000000103f19020 libnode.141.dylib`Builtins_ConstructHandler + 864
frame #24: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #25: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #26: 0x0000000103e40fa4 libnode.141.dylib`Builtins_ArrayForEach + 804
frame #27: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #28: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #29: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #30: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #31: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #32: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #33: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #34: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #35: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #36: 0x0000000103d895ec libnode.141.dylib`Builtins_InterpreterEntryTrampoline + 268
frame #37: 0x0000000103d8696c libnode.141.dylib`Builtins_JSEntryTrampoline + 172
frame #38: 0x0000000103d86610 libnode.141.dylib`Builtins_JSEntry + 176
frame #39: 0x0000000104317df0 libnode.141.dylib`v8::internal::(anonymous namespace)::Invoke(v8::internal::Isolate*, v8::internal::(anonymous namespace)::InvokeParams const&) + 1544
frame #40: 0x00000001043177d4 libnode.141.dylib`v8::internal::Execution::Call(v8::internal::Isolate*, v8::internal::DirectHandle<v8::internal::Object>, v8::internal::DirectHandle<v8::internal::Object>, v8::base::Vector<v8::internal::DirectHandle<v8::internal::Object> const>) + 88
frame #41: 0x0000000104f2272c libnode.141.dylib`v8::Function::Call(v8::Isolate*, v8::Local<v8::Context>, v8::Local<v8::Value>, int, v8::Local<v8::Value>*) + 172
frame #42: 0x0000000103fff560 libnode.141.dylib`node::builtins::BuiltinLoader::CompileAndCall(v8::Local<v8::Context>, char const*, node::Realm*) + 460
frame #43: 0x0000000104eb83c4 libnode.141.dylib`node::Realm::ExecuteBootstrapper(char const*) + 76
frame #44: 0x0000000104e7bc84 libnode.141.dylib`node::StartExecution(node::Environment*, char const*) (.cold.1) + 28
frame #45: 0x0000000103fea608 libnode.141.dylib`node::StartExecution(node::Environment*, char const*) + 60
frame #46: 0x0000000103fea5a0 libnode.141.dylib`node::StartExecution(node::Environment*, std::__1::function<v8::MaybeLocal<v8::Value> (node::StartExecutionCallbackInfo const&)>) + 1492
frame #47: 0x0000000103f66c4c libnode.141.dylib`node::LoadEnvironment(node::Environment*, std::__1::function<v8::MaybeLocal<v8::Value> (node::StartExecutionCallbackInfo const&)>, std::__1::function<void (node::Environment*, v8::Local<v8::Value>, v8::Local<v8::Value>)>) + 204
frame #48: 0x0000000104048950 libnode.141.dylib`node::NodeMainInstance::Run(node::ExitCode*, node::Environment*) + 92
frame #49: 0x00000001040486e0 libnode.141.dylib`node::NodeMainInstance::Run() + 152
frame #50: 0x0000000103fed5c8 libnode.141.dylib`node::Start(int, char**) + 612
frame #51: 0x0000000198ebeb98 dyld`start + 6076
Hướng dẫn đóng góp
Bắt đầu từ đâu
- Đọc hết issue, rồi đọc hướng dẫn đóng góp của dự án.
- Bình luận trên issue rằng bạn sẽ nhận — tránh hai người làm cùng một việc.
- Fork repository và làm thay đổi trên một nhánh.
- Mở pull request có tham chiếu số hiệu của issue.
Hướng nghiên cứu
Chạy node repro.js từ branch sync-gc và so sánh lỗi với napi-inl.h:6977, napi_adjust_external_memory và LLDB stack trace. Theo dõi xem lệnh gọi basic_env có đi đến JavaScript trong GC hay không, sau đó ghi lại hoặc kiểm thử hành vi mong đợi để reproducer không còn bị crash.
Do mô hình lập chỉ mục viết ra từ nội dung của issue.
Đánh giá
- Công nghệ
- cpp, node.js
- Lĩnh vực
- api
- Loại issue
- Lỗi
- Độ khó
- 4/5
- Thời gian dự kiến
- 3-5 ngày
- Mức độ hoạt động
- Sôi nổi
- Độ rõ ràng
- Khá rõ ràng
- Mức phù hợp với người mới
- 48/100