【发布时间】:2019-08-02 15:15:05
【问题描述】:
出于某种原因,我需要确定,在我的 macOS 应用程序中,当保存面板第一次出现在新复制的文档中时,macOS 正在删除临时自动保存的文档文件,这当然会导致后来保存失败。这是一个 DTrace 成绩单,我对其进行了一些删节:
Air2 jk$ sudo dtrace -n 'syscall::unlink*:entry { printf("time=%d execname=%s arg=%s\n", timestamp/1000000000, execname, copyinstr(arg0)); ustack(100); }' -p `pgrep MyApp`
Password:
dtrace: description 'syscall::unlink*:entry' matched 4 probes
CPU ID FUNCTION:NAME
1 178 unlink:entry time=6562 execname=com.apple.appkit arg=/Users/jk/Library/Autosave Information/Unsaved MyApp Document.bmco
libsystem_kernel.dylib`__unlink+0xa
libremovefile.dylib`__removefile_tree_walker+0x147
libremovefile.dylib`removefile+0x99
Foundation`-[NSFilesystemItemRemoveOperation main]+0xba
Foundation`__NSOPERATION_IS_INVOKING_MAIN__+0x11
Foundation`-[NSOperation start]+0x2db
Foundation`-[NSFileManager removeItemAtPath:error:]+0x54
AppKit`__90-[NSDocumentController(NSInternal) _autoreopenDocumentsFromRecords:withCompletionHandler:]_block_invoke_2+0x90
AppKit`__89-[NSDocumentController reopenDocumentForURL:withContentsOfURL:display:completionHandler:]_block_invoke_2+0xa6
AppKit`___NSMainRunLoopPerformBlockInModes_block_invoke+0x19
CoreFoundation`__CFRUNLOOP_IS_CALLING_OUT_TO_A_BLOCK__+0xc
CoreFoundation`__CFRunLoopDoBlocks+0x17b
CoreFoundation`__CFRunLoopRun+0xae8
CoreFoundation`CFRunLoopRunSpecific+0x1f3
HIToolbox`RunCurrentEventLoopInMode+0x124
HIToolbox`ReceiveNextEventCommon+0x164
HIToolbox`_BlockUntilNextEventMatchingListInModeWithFilter+0x40
AppKit`_DPSNextEvent+0x3de
AppKit`-[NSApplication(NSEvent) _nextEventMatchingEventMask:untilDate:inMode:dequeue:]+0x548
ViewBridge`-[NSViewServiceApplication nextEventMatchingMask:untilDate:inMode:dequeue:]+0x5f
AppKit`-[NSApplication run]+0x292
AppKit`NSApplicationMain+0x309
libxpc.dylib`_xpc_objc_main.cold.3+0x38
libxpc.dylib`_xpc_objc_main+0x203
libxpc.dylib`_xpc_copy_xpcservice_dictionary
ViewBridge`xpc_connection_handler
ViewBridge`NSViewServiceApplicationMain+0xbff
com.apple.appkit.xpc.openAndSavePanelService`main+0xc0
libdyld.dylib`start+0x1
com.apple.appkit.xpc.openAndSavePanelService`0x1
上面的结果告诉我
(1) 这不是我的进程,而是一个名为com.apple.appkit 的进程,或者可能是com.apple.appkit.xpc.openAndSavePanelService,最终调用unlink,并且
(2) 在 -[NSDocumentController reopenDocumentForURL:withContentsOfURL:display:completionHandler:] 中执行 Objective-C 块时会发生这种情况。
我认为后者是问题的线索,因为应该没有什么可以重新打开的。所以我想知道什么叫-[NSDocumentController reopenDocumentForURL:withContentsOfURL:display:completionHandler:]。上面的结果并没有说明,因为这是一个块调用。
所以我尝试制作另一个 DTrace 命令,使用 Objective-C 探针,每当此 appkit 进程调用 -[NSDocumentController reopenDocumentForURL:withContentsOfURL:display:completionHandler:] 时将触发该探针:
sudo dtrace -n 'objc$target:NSDocumentController:-reopenDocumentForURL?withContentsOfURL?display?completionHandler?:entry { printf("time=%d arg=%s\n", timestamp/1000000000, copyinstr(arg0)); ustack(100); }' -p `pgrep appkit`
dtrace: invalid probe specifier objc$target:NSDocumentController:-reopenDocumentForURL?withContentsOfURL?display?completionHandler?:entry { printf("time=%d arg=%s\n", timestamp/1000000000, copyinstr(arg0)); ustack(100); }: extraneous argument '7135' ($1 is not referenced)
但正如您所见,它抱怨 无效的探针说明符 并且 $1 未被引用。 -p 显然正在工作,因为 7135 确实是在我的 Mac 上运行的 com.apple.appkit 进程的 pid。我认为这是被引用的 $1,并且由于某种原因,$target 没有像预期的那样引用。
所以,我改为按字面意思指定 pid (objc7135),这很有效,(matched 1 probe),但它没有捕捉到所需的调用,可能是因为调用是由进程 com.apple.appkit.xpc.openAndSavePanelService 的 second 运行实例完成的,该实例在发生保存或自动保存时启动,当然,每当我将应用程序切换到终端运行 ps 和做一个新的探针。因此,在我可以对其进行探测之前,呼叫已经发生。啊啊啊!!!
如果我可以让 DTrace 命令正常工作,我认为它会附加到新启动的 appkit 进程并给出我想要的答案。为什么我的 DTrace 命令会出现该错误?
2019 年 8 月 7 日更新:
好吧,我确定我正在寻找的原因实际上是 macOS AppKit 中的一个可重现的错误,所以我提交了一个很好的新反馈,并祈祷 Apple 修复它。但我仍然认为我的问题是一个很好的 DTrace 问题,并且很乐意接受正确的答案:)
2019 年 8 月 9 日更新:
我在 transposit.com 尝试了@ahl 的建议。意识到嵌套的 DTrace 调用将是反斜杠逃生地狱,我将原始 DTrace 调用放在名为 DTraceMe.sh 中,然后运行:
sudo dtrace -wn 'proc:::exec-success { if (execname == "com.apple.appkit") { printf("Launched: %s pid=%d\n", execname, pid); system("~/Desktop/Temp/DTraceMe.sh"); }}'
这修复了错误消息并在我的应用程序显示麻烦的保存面板时运行DTraceMe.sh。好的!但不幸的是,像往常一样,内部 DTrace 实例显然需要几秒钟来安装它的探针(并打印 dtrace: blahblah 匹配 1 个探针)。因此,当它被武装起来时,我试图跟踪的对 NSDocumentController 的麻烦调用已经完成并消失了。
好吧,我想,也许我可以停止新启动的 com.apple.appkit 进程足够长的时间,让 DTrace 运行一个 lldb 实例,该实例将附加到下一个启动的 com.apple.appkit:
(lldb) process attach --name com.apple.appkit --waitfor
令我惊讶的是,这实际上确实在目标 com.apple.appkit 启动后立即中断并停止了它。 (当然我已经禁用了系统完整性保护。)但是现在内部的 DTrace 实例立即告诉我我的探针说明符不匹配任何探针,大概是因为 com.apple.appkit 无法与 DTrace 合作,而它被 lldb 停止。伤心。
【问题讨论】:
-
一旦你将 lldb 附加到目标进程,你就不需要使用 DTrace 来找到你感兴趣的 Objective-C 调用。你可以使用一个断点。至于试图在新执行的进程上使用 DTrace 的比赛,您可以调用
stop()来暂停它。您的DTraceMe.sh脚本可以在BEGIN探测中的目标上使用pidresume()来恢复它。 (pidresume()是一个特定于 Mac 的扩展,记录在dtrace手册页中。)
标签: dtrace