DTrace objc:无效的探针说明符:“$1 未被引用”
Posted
技术标签:
【中文标题】DTrace objc:无效的探针说明符:“$1 未被引用”【英文标题】:DTrace objc : invalid probe specifier : "$1 is not referenced" 【发布时间】: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
手册页中。)
【参考方案1】:
pgrep appkit
似乎返回了多个 pid,这可以解释 $1 is not referenced
错误。
跟踪新创建的流程可能会很棘手,具体取决于您正在尝试执行的操作。在这种情况下,我建议您跟踪 proc:::exec-success
探针并从那里使用 system()
操作来触发 dtrace 命令的 另一个 实例,该实例跟踪新创建的进程。
【讨论】:
谢谢你,@ahl。请参阅我的问题中的 UPDATE 2019-AUG-09。 当你有一个新创建的进程时,它可能还没有加载库。尝试将-Z
选项添加到 dtrace 命令。这忽略了“零探针匹配”失败和当新库出现时,DTrace 应该尝试检测它们。【参考方案2】:
我喜欢蛮力方法。这将捕获所有unlink
调用(除非某些东西正在清理大量文件,否则文件取消链接实际上非常罕见):
dtrace -n 'syscall::unlink*:entry printf("time=%d arg=%s\n",
timestamp/1000000000, copyinstr(arg0)); ustack(100); '
将输出保存到文件并浏览它。
或者,如果您知道可执行文件的名称,请添加类似 / execname == "com.apple.appkit" /
谓词的内容,以将探测限制为仅与谓词匹配的事件:
dtrace -n 'syscall::unlink*:entry / execname == "com.apple.appkit" /
printf("time=%d arg=%s\n", timestamp/1000000000, copyinstr(arg0)); ustack(100); '
【讨论】:
谢谢你,Andrew,但我已经这样做了,并且有调用unlink
的调用堆栈。它被 --[NSDocumentController:reopenDocumentForURL:withContentsOfURL:display:completionHandler:] 中的 Objective-C 块调用。所以我需要知道什么在调用 that,为此我需要使用 DTrace 的 objc 提供程序。尽管默认情况下 syscall 提供程序会触发来自任何进程的调用,但据我了解,objc 提供程序仅在给定进程中安装探针。这就是困境。以上是关于DTrace objc:无效的探针说明符:“$1 未被引用”的主要内容,如果未能解决你的问题,请参考以下文章