在我用Objective-C编写的旧macOS应用程序中,我正在调试一个可复制的问题,其中在系统框架调用期间过早删除文件包。为了获得线索,我希望每当删除文件时调试器都中断。为此,我在Xcode中的以下符号处设置了符号断点:
unlink
unlinkat
-[NSFileManager removeItemAtPath:error:]
-[NSFileManager removeItemAtURL:error:]
所有这些断点均按预期解析为实际断点,并且在按预期方式删除文件时它们会按预期中断。但是在麻烦的过早删除文件的过程中,没有中断。
macOS中是否还有其他功能可以删除文件,我应该为此添加断点?
背景信息:
在新复制的文件(如 File> Duplicate )中调用[super saveDocument]
但从未保存过的文档包时,此问题发生在我的自定义NSDocument子类中。此类文档包位于~/Library/Autosave Information/
中,并且当一切正常时,请保留在那里,直到出现“保存”面板并随后将其关闭。但是,在发生错误的情况下,当用户单击File > Save
(或发生自动保存)时,包会立即消失,这显然会导致以后出现错误,表明已删除的包无法移至“保存面板”返回的路径。
我也尝试过在该软件包出现后,并且在单击 File> Save 之前,将该软件包的POSIX权限更改为八进制500。我的想法是无法删除它,并且我还打开了它。我所有的异常和错误断点,希望神秘的删除程序会进入调试器控制台。结果:程序包未被删除,并且按照我的假设,保存操作成功。但是什么都没有尖叫。因此,这个神秘的删除器确实是问题所在,但显然既隐秘又宽容:(
编辑,五天后,解决方案:
在找到其他事情要做5天之后,我决定忍痛忍受,并按照肯·托马斯(Ken Thomases)的建议使用DTrace。它起作用了,向我展示了通过libsystem_kernel.dylib__unlink
的调用删除了主题文件包中的所有文件,而-[NSFileManager removeItemAtPath:error:]
又调用了该文件。
我不知道为什么这些函数的断点没有为这些调用而中断,除非在堆栈跟踪的底部可能有一个线索,提到了“ xpc”。此文件删除是否有可能由XPC帮助程序完成? DTrace是否还会探测正在探测的进程的辅助进程?那真是太神奇了。
以下是简短的DTrace会话记录:
Air2 jk$ sudo dtrace -n 'syscall::unlink*:entry,syscall::rmdir:entry,syscall::rename:entry { printf("time=%d arg=%s\n", timestamp/1000000000, copyinstr(arg0)); ustack(100); }' -p `pgrep MyApp`
Password:
dtrace: description 'syscall::unlink*:entry,syscall::rmdir:entry,syscall::rename:entry ' matched 4 probes
CPU ID FUNCTION:NAME
1 178 unlink:entry time=6562 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
(该笔录中的调用显然试图取消文件包的链接,由于该包不为空,我认为这将失败。随后是几个类似的调用,它们遍历包树,删除每个节点,最后是重复该调用以删除软件包,显然是成功的。)
答案 0 :(得分:1)
重命名操作将使源路径不再引用文件(看起来像源路径处的文件已被删除)。它也可以取消链接/删除目标路径上的文件,尽管它将替换为源路径上的文件。因此,它们将是rename()
,renameat()
,renamex_np()
和renameatx_np()
。
当然,rmdir()
可以删除目录,但前提是该目录为空。
显然,有一个隐藏的delete()
系统调用。它被描述为“使用Carbon语义从文件系统中删除名称”。框架可能正在使用它。