由于某种原因,我需要确定在我的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:]
。上面的结果并不能说明这一点,因为这是一个块调用。
因此,我尝试使用一个Objective-C探针来编写另一个DTrace命令,只要此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 ),并且有效了(匹配1个探针),但是它没有捕获所需的调用,可能是因为调用由进程com.apple.appkit.xpc.openAndSavePanelService
的 second 个正在运行的实例完成,只要发生保存或自动保存,该实例就会启动,当然,只要我将应用程序切换到终端以运行{{ 1}},并进行新的探查。因此,在我可以对其设置探针之前,该呼叫已经发生。啊!!!
如果我可以使DTrace命令正常工作,我认为它会附加到新启动的appkit进程中并给出我想要的答案。为什么我的DTrace命令给出该错误?
更新2019年8月7日:
好吧,我确定我寻找的原因实际上是macOS AppKit中的可重现错误,因此我提交了一个不错的新反馈,并祈祷苹果公司对其进行了修复。但是我仍然认为我的问题在这里是一个很好的DTrace问题,并且很乐意接受正确的答案:)
更新2019年8月9日:
我在transposit.com上尝试了@ahl的建议。意识到嵌套的DTrace调用将是反斜杠转义,我将原始的DTrace调用放在了一个名为DTraceMe.sh的文件中,然后运行了此命令:
ps
当我的应用程序显示麻烦的“保存”面板时,这可以修复错误消息并运行sudo dtrace -wn 'proc:::exec-success { if (execname == "com.apple.appkit") { printf("Launched: %s pid=%d\n", execname, pid); system("~/Desktop/Temp/DTraceMe.sh"); }}'
。真好!但是不幸的是,像往常一样,内部DTrace实例显然需要几秒钟来安装其探针(并打印 dtrace:blahblah匹配了1个探针)。因此,待布防时,我试图跟踪的对NSDocumentController的麻烦调用已经完成并且消失了。
好吧,我想,也许我可以通过运行一个lldb实例来终止新启动的com.apple.appkit进程足够长的时间,以使DTrace正常运行,该实例将附加到下一个启动的com.apple.appkit:>
DTraceMe.sh
令我惊讶的是,实际上它确实中断了目标com.apple.appkit进程,并在启动后立即停止了该进程。 (当然,我已经禁用了系统完整性保护。)但是现在内部的DTrace实例立即告诉我,我的探针说明符与任何探针都不匹配,大概是因为com.apple.appkit在它同时不能与DTrace合作。由lldb停止。伤心。
答案 0 :(得分:2)
似乎pgrep appkit
返回了多个pid,它们可以解释$1 is not referenced
错误。
跟踪新创建的流程可能很棘手,具体取决于您要尝试执行的操作。在这种情况下,建议您跟踪proc:::exec-success
探针,然后使用system()
操作触发dtrace命令的另一个实例,该实例跟踪新创建的进程
答案 1 :(得分:0)
我喜欢暴力破解方法。这将捕获所有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); }'