关于3.8中resolveAfterPromiseResolved和rejectAfterPromiseResolved错误

fix: 修复promise相关错误,issue参考https://forum.cocos.org/t/topic/158288/20 by nai6514531 · Pull Request #4357 · cocos/engine-native · GitHub 这个pr,crash信息是下面这个
0 SIGSEGV:SEGV_ACCERR
1 0 CocosCreator v8::internal::GlobalHandles::NodeSpacev8::internal::GlobalHandles::Node::Release(v8::internal::GlobalHandles::Node*) + 24
2 1 CocosCreator se::ScriptEngine::handlePromiseExceptions() + 128
3 2 CocosCreator se::ScriptEngine::handlePromiseExceptions() + 128
4 3 CocosCreator cocos2d::EventDispatcher::dispatchTickEvent(float) + 244
5 4 CocosCreator -[MainLoop doCaller:] + 240
6 5 QuartzCore CA::Display::DisplayLink::dispatch_items(unsigned long long, unsigned long long, unsigned long long) + 832
7 6 QuartzCore display_timer_callback(__CFMachPort*, void*, long, void*) + 368
8 7 CoreFoundation CFMachPortPerform + 172
9 8 CoreFoundation CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE1_PERFORM_FUNCTION
+ 56
10 9 CoreFoundation _CFRunLoopDoSource1 + 512
11 10 CoreFoundation _CFRunLoopRun + 2332
12 11 CoreFoundation _CFRunLoopRunSpecific + 584
13 12 GraphicsServices _GSEventRunModal + 160

你有没有存项目的 symbol 文件,有的话,可以还原出 handlePromiseExceptions 哪行的问题。

只在线上出现吗?本地模拟一个 promise 错误,能否触发这个问题?

我们灰度数据大概有900个进入游戏的,crash发生一次。本地测试的时候模拟了很多promise的错误,触发不了类似的crash

项目的c++crash问题只能定位到函数,需要自己分析函数的实现。暂时定位不到具体哪一行

能够在这个函数中打一些日志,辅助排查吗?
崩溃系统候,应该能够收集到崩溃时候的日志吧?

这个会不会是线程安全的问题。极端情况下比如触发handlePromiseExceptions的时候,又触发的onPromiseRejectCallback,这样2个线程会在同一时间修改_promiseArray。这样是不是可能引发crash?

应该不会是线程问题,onPromiseRejectCallback 只可能在主线程回调。JS 执行环境本身也是单线程的。
崩溃堆栈也是某个线程的堆栈,不同线程的堆栈是不会掺杂在一起的。

那如果要在这个函数中打印一些日志,有什么建议吗?不太清楚打印什么可以帮助排查这个crash问题。

可以在 handlePromiseExceptions 头和尾把 _promiseArray 打印一下,函数中间把 exceptions.event.c_str(), exceptions.stackTrace.c_str() 也打印一下。

这样加的话,基本上每帧都会输出日志,对性能影响大嘛?

判断 _promiseArray 不为空 后面再打,如果为空,就不打。只有在有 promise 异常的时候才会打印的。

Fixed in 3.8.4:

也重构了输出堆栈的格式,报错信息也更加详细了。

点赞:+1::+1::+1:

2.4的版本也可以增加一下吗。

暂时没有这个计划,可以参考我发的 PR 改下。

找到了复现crash的场景,就是调用cc.game.restart的时候,游戏中新打开另一个游戏。

我打印的日志输出了绿色箭头,红色箭头那里就crash了。crash堆栈如下,会不会是restart的时候把v8的全局句柄释放,新new了一个v8句柄。
v8::internal::GlobalHandles::NodeSpacev8::internal::GlobalHandles::Node::Release(v8::internal::GlobalHandles::Node*)
:?
v8::PersistentBasev8::Promise::Reset()
cocos2d-x/external/android/arm64-v8a/include/v8/v8.h:10498
cocos2d::EventDispatcher::dispatchTickEvent(float)
cocos2d-x/cocos/scripting/js-bindings/event/EventDispatcher.cpp:292
Java_org_cocos2dx_lib_Cocos2dxRenderer_nativeRender
cocos2d-x/cocos/platform/android/jni/JniImp.cpp:287

详细说下怎么打开另一个游戏?
有复现方式的话,能出个demo吗?

我们的crash信号的SIGSEGV(SEGV_MAPERR),尝试在访问无效的内存地址,crash的具体代码又是std::get<0>(exceptionsPair).get()->Reset(),然后结合crash的复现场景是在调用cc.gam.restart。看了restart的逻辑,会调用cc.Object._deferredDestroy();清除所有对象,所以是因为js对象被v8回收了,然后再调用promise -> Reset导致的SIGSEGV(SEGV_MAPERR),

也就是说,ScriptEngine::handlePromiseException 有可能在 ScriptEngine 被销毁后还还被调用?