```❯ marginnote4 软件再次卡住了, 转圈圈, 无法执行操作, 如何排查问题?◐ The user says MarginNote 4 is frozen again right now (spinning wheel, can’t do anything). This is a live debugging situation. I should capture diagnostics immediately while it’s frozen - get a spindump/sample of the process to see what themain thread is blocked on.● Get MarginNote PID and current state (shell)││ PID: 30236│ PID %CPU %MEM RSS STAT│ 30236 3.5 0.6 380464 S│ ││ pid=$(pgrep -x “MarginNote 4”); echo “PID: $pid”; ps -p $pid -o pid,pcpu,pmem,rss,state | cat└ 4 lines● Sample MarginNote threads for 5 seconds while frozen (shell)││ Output too large to read at once (675.2 KB). Saved to: /var/folders/51/y020gjv14kx9mpdqmw912t580000gn/T/copilot-tool-output-1778673590546-a97yzb.txt│ Consider using tools like grep (for searching), head/tail (for viewing start/end), view with view_range (for specific sections), or jq (for JSON) to examine portions of the output.││ Preview (first 500 chars):││ Analysis of sampling MarginNote 4 (pid 30236) every 1 millisecond│ Process: MarginNote 4 [30236]│ Path: /Applications/MarginNote 4.app/Contents/MacOS/MarginNote 4│ Load Address: 0x100f38000│ Identifier: QReader.MarginStudy.easy│ Version: 4.3.2 (32002)│ Code Type: ARM64│ Platform: Catalyst│ Parent Process: launchd [1]│ Target Type: live task││ Date/Time: 2026-05-13 19:59:44.624 +0800│ Launch Time: 2026-05-13 17:40:28.761 +0800│ OS Version:│ ││ sample 30236 5 2>/dev/null└ 20 lines● Extract main thread stack from sample (shell)││ 3818 Thread_2266225: Main Thread DispatchQueue_│ + 3818 start (in dyld) + 6992 [0x1811d7da4]│ + 3818 ??? (in MarginNote 4) load address 0x100f38000 + 0x6c11dc [0x1015f91dc]│ + 3818 UIApplicationMain (in UIKitCore) + 144 [0x1b9da4ed4]│ + 3818 UINSApplicationMain (in UIKitMacHelper) + 972 [0x19f93c1bc]│ + 3818 _NSApplicationMainWithInfoDictionary (in AppKit) + 24 [0x185c8d930]│ + 3818 NSApplicationMain (in AppKit) + 880 [0x185a467b0]│ + 3818 -[NSApplication run] (in AppKit) + 368 [0x185a6e13c]│ + 3818 -[NSApplication(NSEventRouting) nextEventMatchingMask:untilDate:inMode:dequeue:] (in AppKit) + 72 [0x1866103a8]│ + 3818 -[NSApplication(NSEventRouting) _nextEventMatchingEventMask:untilDate:inMode:dequeue:] (in AppKit) + 688 [0x18661069c]│ + 3818 _DPSNextEvent (in AppKit) + 576 [0x185a7b084]│ + 3818 _DPSBlockUntilNextEventMatchingListInMode (in AppKit) + 228 [0x1861271a4]│ + 3818 _BlockUntilNextEventMatchingListInMode (in HIToolbox) + 48 [0x18e5b113c]│ + 3818 ReceiveNextEventCommon (in HIToolbox) + 272 [0x18e4277e4]│ + 3818 RunCurrentEventLoopInMode (in HIToolbox) + 320 [0x18e424560]│ + 3818 _CFRunLoopRunSpecificWithOptions (in CoreFoundation) + 532 [0x181721be0]│ + 3818 __CFRunLoopRun (in CoreFoundation) + 820 [0x18164f4e4]│ + 3818 __CFRunLoopDoSources0 (in CoreFoundation) + 232 [0x1816508c0]│ + 3818 __CFRunLoopDoSource0 (in CoreFoundation) + 172 [0x181650b54]│ + 3818 CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION (in CoreFoundation) + 28 [0x181650bc0]│ + 3818 WTF::RunLoop::performWork(void*) (in JavaScriptCore) + 36 [0x1d0b5edd8]│ + 3818 WTF::RunLoop::performWork() (in JavaScriptCore) + 460 [0x1d0b5d830]│ + 3818 WTF::Detail::CallableWrapper::call() (in WebKit) + 652│ [0x1d3ccba34]│ + 3818 IPC::Connection::SyncMessageState::ConnectionAndIncomingMessage::dispatch() (in WebKit) + 92 [0x1d3cac8d4]│ + 3818 IPC::Connection::dispatchMessage(WTF::UniqueRefIPC::Decoder) (in WebKit) + 792 [0x1d3cb0f80]│ + 3818 WebKit::WebProcessProxy::didReceiveSyncMessage(IPC::Connection&, IPC::Decoder&, WTF::UniqueRefIPC::Encoder&) (in WebKit) + 444 [0x1d3383548]│ + 3818 WebKit::WebProcessProxy::dispatchSyncMessage(IPC::Connection&, IPC::Decoder&, WTF::UniqueRefIPC::Encoder&) (in WebKit) + 44 [0x1d3880dbc]│ + 3818 IPC::MessageReceiverMap::dispatchSyncMessage(IPC::Connection&, IPC::Decoder&, WTF::UniqueRefIPC::Encoder&) (in WebKit) + 176 [0x1d3ccecdc]│ + 3818 WebKit::WebPageProxy::didReceiveSyncMessage(IPC::Connection&, IPC::Decoder&, WTF::UniqueRefIPC::Encoder&) (in WebKit) + 6148 [0x1d331c30c]│ + 3818 WebKit::WebPageProxy::interpretKeyEvent(WebKit::EditorState&&, WebKit::KeyEventInterpretationContext&&, WTF::CompletionHandler&&) (in WebKit) + 192│ [0x1d367c1a4]│ + 3818 WebKit::PageClientImpl::interpretKeyEvent(WebKit::NativeWebKeyboardEvent const&, WebKit::KeyEventInterpretationContext&&) (in WebKit) + 52 [0x1d364a628]│ + 3818 -[WKContentView(WKInteraction) _interpretKeyEvent:withContext:] (in WebKit) + 328 [0x1d3c55d6c]│ + 3818 -[_UIKeyboardStateManager handleKeyTextCommandForCurrentEvent] (in UIKitCore) + 96 [0x1ba7ec264]│ + 3818 -[UIApplication _currentKeyboardShortcutInvocationForKeyboardEvent:] (in UIKitCore) + 80 [0x1bb16dec4]│ + 3818 -[UIResponder _keyboardShortcutInvocationForKeyboardEvent:] (in UIKitCore) + 592 [0x1bb17ce3c]│ + 3818 -[UIResponder _enumerateKeyboardShortcutsInChainWithOptions:usingBlock:] (in UIKitCore) + 1268 [0x1bb17d99c]│ + 3818 __59-[UIResponder _keyboardShortcutInvocationForKeyboardEvent:]_block_invoke_2 (in UIKitCore) + 216 [0x1bb17d280]│ + 3818 __59-[UIResponder _keyboardShortcutInvocationForKeyboardEvent:]_block_invoke (in UIKitCore) + 76 [0x1bb17d0f4]│ + 3818 -[UICommand _resolvedTargetFromFirstTarget:sender:] (in UIKitCore) + 128 [0x1bb39d8e4]│ + 3818 -[WKWebView(WKViewInternalIOS) targetForAction:withSender:] (in WebKit) + 1640 [0x1d342f620]│ + 3818 _UIResponderTargetForActionWithTestBlock (in UIKitCore) + 1684 [0x1ba8cbe50]│ + 3818 _UIResponderTargetForActionWithTestBlock (in UIKitCore) + 1684 [0x1ba8cbe50]│ + 3818 _UIResponderTargetForActionWithTestBlock (in UIKitCore) + 632 [0x1ba8cba34]│ + 3818 _UIResponderCanPerformAction (in UIKitCore) + 80 [0x1ba8ccdec]│ + 3818 ??? (in MarginNote 4) load address 0x100f38000 + 0x215f1c [0x10114df1c]│ + 3818 ??? (in MarginNote 4) load address 0x100f38000 + 0xc0a7d8 [0x101b427d8]│ + 3818 -[NSRunLoop(NSRunLoop) runMode:beforeDate:] (in Foundation) + 212 [0x182e98b44]│ + 3818 _CFRunLoopRunSpecificWithOptions (in CoreFoundation) + 532 [0x181721be0]│ + 3818 __CFRunLoopRun (in CoreFoundation) + 924 [0x18164f54c]│ + 3818 __CFRunLoopDoObservers (in CoreFoundation) + 648 [0x18164fea0]│ + 3818 CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION (in CoreFoundation) + 36 [0x18164ffa4]│ + 3818 ??? (in Gestures) load address 0x24c0f8000 + 0x25734 [0x24c11d734]│ + 3818 ??? (in Gestures) load address 0x24c0f8000 + 0x25640 [0x24c11d640]│ + 3818 ??? (in Gestures) load address 0x24c0f8000 + 0x26548 [0x24c11e548]│ + 3818 ??? (in Gestures) load address 0x24c0f8000 + 0x657a4 [0x24c15d7a4]│ + 3818 ??? (in Gestures) load address 0x24c0f8000 + 0x63a6c [0x24c15ba6c]│ + 3818 ??? (in Gestures) load address 0x24c0f8000 + 0x32334 [0x24c12a334]│ + 3818 ??? (in Gestures) load address 0x24c0f8000 + 0x10ec4 [0x24c108ec4]│ + 3818 -[UIGestureRecognizer gestureNode:didUpdatePhase:] (in UIKitCore) + 308 [0x1bac8e900]│ + 3818 -[UIGestureRecognizer _updateGestureForActiveEvents] (in UIKitCore) + 468 [0x1bac8ac80]│ + 3818 _UIGestureRecognizerSendActions (in UIKitCore) + 272 [0x1b9f9aaa8]│ + 3818 _UIGestureRecognizerSendTargetActions (in UIKitCore) + 96 [0x1b9f9acf4]│ + 3818 -[UIGestureRecognizerTarget _sendActionWithGestureRecognizer:] (in UIKitCore) + 124 [0x1b9f9ae80]│ + 3818 -[UITextMultiTapRecognizer onStateUpdate:] (in UIKitCore) + 284 [0x1babb23ec]│ + 3818 -[UIApplication sendAction:to:from:forEvent:] (in UIKitCore) + 100 [0x1b9fc2950]│ + 3818 -[UITextSelectionInteraction _handleMultiTapGesture:] (in UIKitCore) + 928 [0x1bb3d7264]│ + 3818 -[UITextSelectionInteraction _checkForRepeatedTap:gestureLocationOut:] (in UIKitCore) + 260│ [0x1bb3d6484]│ + 3818 -[UITextInteraction _beginSelectionChange] (in UIKitCore) + 140 [0x1bb3ce04c]│ + 3818 -[WKContentView(WKInteraction) beginSelectionChange] (in WebKit) + 56 [0x1d3c51498]│ + 3818 -[_UIKeyboardStateManager selectionWillChange:] (in UIKitCore) + 72 [0x1ba7db14c]│ + 3818 -[_UIKeyboardStateManager prepareForSelectionChange] (in UIKitCore) + 140 [0x1ba7dae5c]│ + 3818 -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] (in UIKitCore) + 164│ [0x1baf55070]│ + 3818 -[UIKeyboardTaskQueue lockWhenReadyForMainThread] (in UIKitCore) + 176│ [0x1baf5480c]│ + 3818 __42-[_UIKeyboardStateManager handleKeyEvent:]_block_invoke_3 (in UIKitCore) + 28│ [0x1ba8095e0]│ + 3818 -[UIKeyboardTaskQueue _lockWhenReadyForMainThread] (in UIKitCore) + 364│ [0x1baf549b0]│ + 3818 __126-[_UIKeyboardStateManager│ _handleWebKeyEvent:withEventType:withInputString:withInputStringIgnoringModifiers:executionContext:]_block_invoke (in UIKitCore) + 76 [0x1ba8076d8]│ + 3818 -[UIApplication(PrivateWebKit) _performDefaultBehaviorForEvent:] (in│ UIKitCore) + 128 [0x1bb72b630]│ + 3818 __31-[UINSApplicationDelegate init]_block_invoke_2 (in UIKitMacHelper) +│ 72 [0x19f942f78]│ + 3818 -[UINSInputView performDefaultBehaviorForEvent:] (in UIKitMacHelper) +│ 108 [0x19f9684b0]│ + 3818 -[NSApplication(UINSApplicationSwizzling) uinsSwizzledSendEvent:] (in│ UIKitMacHelper) + 256 [0x19f950a38]│ ││ SAMPLE_FILE=“/var/folders/51/y020gjv14kx9mpdqmw912t580000gn/T/copilot-tool-output-1778673590546-a97yzb.txt”│ # Extract main thread call stack│ grep -A 80 "Thread.*Main Thread" “$SAMPLE_FILE” | head -80└ 81 lines● Check memory and heaviest stack info (shell)││ Physical footprint: 731.3M│ Physical footprint (peak): 1.5G│ ││ SAMPLE_FILE=“/var/folders/51/y020gjv14kx9mpdqmw912t580000gn/T/copilot-tool-output-1778673590546-a97yzb.txt”│ # Check memory and CPU context│ grep -E “Physical footprint|CPU|Heaviest” “$SAMPLE_FILE” | head -10└ 3 lines◐ Now I can see the issue clearly! The main thread call stack shows a deadlock pattern:Main thread is handling a keyboard event (key press)It goes through WebKit::WebPageProxy::interpretKeyEvent → _UIKeyboardStateManager handleKeyTextCommandForCurrentEventIn handling the key command, it calls _UIResponderCanPerformAction which triggers something in MarginNote’s code at offset 0x215f1cMarginNote’s code then calls [NSRunLoop(NSRunLoop) runMode:beforeDate:] - running a nested run loop synchronously on the main thread!This nested run loop triggers a gesture recognizer that initiates text selection, which then tries to wait for the keyboard state manager to finish its tasksBut the keyboard state manager is already blocked, waiting for the nested run loop to complete - creating a circular dependency where each component is waiting on the otherThe deadlock emerges from MarginNote’s canPerformAction check spawning a nested run loop that inadvertently blocks the keyboard handler that initiated it in the first place.Beyond the deadlock, there’s a serious memory problem: the app is consuming 731MB of physical memory with a peak of 1.5GB, suggesting significant memory pressure that could be contributing to the overall instability. I should also revisit theCloudKit sync behavior to see if that’s exacerbating things.The root cause is clear now: MarginNote’s implementation of canPerformAction is invoking a nested run loop via runMode:beforeDate:, which creates a chain reaction. That nested loop triggers gesture recognizers and text selectioninteractions that try to synchronize with the keyboard state manager, but the keyboard state manager is still blocked waiting for the original event handling to complete—creating a perfect deadlock scenario. This is a fundamentalarchitectural flaw in how MarginNote 4 handles action validation.● 找到了!主线程发生了明确的死锁。● Check thread count and CloudKit activity (shell)││ 160│ —│ 3818 Thread_2266225: Main Thread DispatchQueue_│ 3818 Thread_2266315: com.apple.NSEventThread│ 3818 Thread_2266321: com.apple.uikit.eventfetch-thread│ + 3818 NSThread__start (in Foundation) + 732 [0x182e92a90]│ 3818 Thread_2266361: com.google.firebase.crashlytics.MachExceptionServer│ 3818 Thread_2266401│ + 3818 NSThread__start (in Foundation) + 732 [0x182e92a90]│ 3818 Thread_2266402│ + 3818 NSThread__start (in Foundation) + 732 [0x182e92a90]│ 3818 Thread_2266403│ + 3818 NSThread__start (in Foundation) + 732 [0x182e92a90]│ 3818 Thread_2266404│ + 3818 NSThread__start (in Foundation) + 732 [0x182e92a90]│ 3818 Thread_2266405│ + 3818 NSThread__start (in Foundation) + 732 [0x182e92a90]│ 3818 Thread_2266406│ + 3818 NSThread__start (in Foundation) + 732 [0x182e92a90]│ 3818 Thread_2266407│ + 3818 NSThread__start (in Foundation) + 732 [0x182e92a90]│ 3818 Thread_2266408│ ││ SAMPLE_FILE=“/var/folders/51/y020gjv14kx9mpdqmw912t580000gn/T/copilot-tool-output-1778673590546-a97yzb.txt”│ # Check how many CloudKit ops and memory│ grep -E “CloudKit|CKModify” “$SAMPLE_FILE” | wc -l│ echo “—”│ # Check other heavy threads│ grep “Thread_” “$SAMPLE_FILE” | head -20└ 23 lines● 诊断结果:找到死锁根因 主线程死锁调用链键盘事件(按键/点击)→ WebKit::WebPageProxy::interpretKeyEvent→ UIKeyboardStateManager handleKeyTextCommandForCurrentEvent→ UIResponder _keyboardShortcutInvocationForKeyboardEvent→ WKWebView targetForAction:withSender:→ _UIResponderCanPerformAction→ MarginNote 4 自身代码 (offset 0x215f1c)→ [NSRunLoop runMode:beforeDate:] ← 在主线程启动嵌套 RunLoop!→ UITextMultiTapRecognizer 触发手势→ UITextSelectionInteraction _beginSelectionChange→ UIKeyboardTaskQueue waitUntilAllTasksAreFinished DEADLOCK:等待原始键盘事件完成但原始键盘事件正被上面的嵌套 RunLoop 阻塞问题本质MarginNote 在 canPerformAction: 回调里(检查菜单是否可用时)调用了 [NSRunLoop runMode:beforeDate:] ——在主线程同步等待,这是典型的错误用法,导致死锁。内存也在告警┌──────────┬────────────┐│ 指标 │ 当前值 │├──────────┼────────────┤│ 物理内存 │ 731 MB │├──────────┼────────────┤│ 历史峰值 │ 1.5 GB │└──────────┴────────────┘触发条件卡住往往发生在:文本编辑区域内双击/三击选词,或输入快捷键时触发右键菜单检查。建议避免触发条件:在 MarginNote 文本区域内减少双击选词操作强制停止恢复:kill -CONT 30236(如果卡住超过 30 秒)上报 bug:这是 MarginNote4.3.2 的明确 bug,在 canPerformAction: 里不应该运行嵌套 RunLoop