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.openAndSavePanelServicesecond 运行实例完成的,该实例在发生保存或自动保存时启动,当然,每当我将应用程序切换到终端运行 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 未被引用”的主要内容,如果未能解决你的问题,请参考以下文章

Leopard 上的 DTrace:没有指定探针,即使我指定了探针

是否可以在 dtrace 中获取 objc 方法的结果?

DTrace - 如何在返回探针中正确检索初始参数

请问dtrace启用了哪些探针?

在 C++ 中使用 Dtrace 设置我自己的探针

从 C 程序访问 dtrace 探针