let's make dependency walker fast again

let's make dependency walker fast again

缘起

最近,经常需要调查 dll 加载失败的问题。前一阵子刚分享了一篇,感兴趣的小伙伴而可以点击 这里。相信,有 windows 开发经验的小伙伴儿一定听过 Dependency Walker 这款工具,它可是查看模块依赖关系的神兵利器。但是在我的机器上,Dependency Walker 运行的特别慢,经常无响应!慢了这么久,是时候提提速了。

重现问题

很容易就可以重现问题,随便拖动一个 dll 文件(或者 exe 文件)到 Dependency Walker 中,等大概 5 秒钟,就会出现大名鼎鼎的无响应界面,需要等待很久才能等到分析结果。
hang-dependency-walker

探查敌情

在大动干戈之前,先用 process explorer 粗略查看 Dependency walker 中各个线程的运行情况。

view-hang-dependency-walker
view-hang-dependency-walker

看来,只有一个线程在工作(其它两个线程是系统工作线程),也是界面线程。如果界面线程都用来处理其它工作,长时间没有处理界面消息的话,界面就会无响应。

从调用栈可知,Dependency Walker 在运行过程中会不断的调用 SearchPathFindFirstFile。从这两个函数的名字可以猜测是在搜索文件。

如果有超多文件需要搜素,那么整个过程慢是合理的。只从 process explorer 中并不能知道 Dependency Waler 在搜索哪些文件,在哪些地方搜索。

小贴士:使用 process explorer 的线程功能,可以很方便的快速定位一些问题,如有必要,再上调试器进行分析。

process monitor

观察进程文件操作记录对于 process monitor 来说简直是小菜一碟。因为预计时间会比较长,而且只需要关心 Dependency Walker 相关的事件,所以在开始前,先做好过滤。

拖动 process monitor 上的靶子图标到 Dependency Walker 上,表示只关心当前这个 Dependency Walker 实例相关的事件。勾选 Drop filtered Event 表示丢弃过滤的事件,因为会过滤掉大部分无关的事件,生成的记录文件会比较小,可以记录更多有用的事件。

filter-in-procmon
filter-in-procmon

Dependency Walker 完成分析后,停止记录,查看文件相关事件。 dll 相关的事件一共有 247688 条。

好奇我是怎么知道的?请看下图:

filtered-dll-file-event
filtered-dll-file-event

观察一个比较典型的文件( api-ms-win-core-rtlsupport-l1-1-0.dll)的相关记录。该文件相关的事件数量是 908 条。

filtered-api-ms-win-core-rtlsupport-l1-1-0-dll-file-event
filtered-api-ms-win-core-rtlsupport-l1-1-0-dll-file-event

该文件相关的部分事件截图如下:

api-ms-win-core-rtlsupport-l1-1-0-dll-events-detail
api-ms-win-core-rtlsupport-l1-1-0-dll-events-detail

在上图中,可以很明显的看到 Result 列大部分结果不是 Success。这个在意料之中,因为正常情况下这个 dll 应该只在特定路径下才有。从 Path 列中的记录可知,搜索路径有很多个。

深入观察

如果仔细观察,可以发现搜索路径非常像典型的 Dll 搜索路径。Dll 搜索路径的官方参考资料链接为 https://docs.microsoft.com/zh-cn/windows/win32/dlls/dynamic-link-library-search-order?redirectedfrom=MSDN。

为了方便大家查看,我截取了相关部分,如下图:

dll-search-directories
dll-search-directories

我机器上的 PATH 环境变量如下图:

PATH-on-my-machine
PATH-on-my-machine

如此看来,Dependency Walker 正是按照典型的 Dll 搜索路径在查找啊(默认情况,可以修改)!

通过上面的简单分析,可以初步判定:除了把应该写在工作线程的代码写在 UI 线程有点问题,其它地方没有明显问题,确实有很多工作需要处理。

只能从减少搜索路径的方向着手了。如果可以少搜索一些地方,那么工作量会显著下降,速度相应的会提高上来了。我第一个想到的办法就是改变 PATH 环境变量的值。

手动设置 PATH

最简单的办法是,设置 PATH 环境变量的值为空,这样就可以避免搜索 PATH 环境变量指定的相关路径了。如果调整全局的 PATH 环境变量会影响所有程序,不可取。但是可以通过脚本启动 Dependency Walker ,在启动 Dependency Walker 之前,设置 PATH 为空,这样只会影响 Dependency Walker。保存下面的批处理脚本为 start_depends.bat,双击运行即可。

set PATH=""
"C:\My\tools\DEPENDS\x64\Depends.exe"

通过以上脚本启动的 Dependency Walker 眼中的 PATH 是空。

如果再次拖动相同的文件到 Dependency Walker 中,基本上是秒出结果。如下图:

dependency-walker-with-empty-path
dependency-walker-with-empty-path

在修改 PATH 环境变量之前,大概需要 2 分钟左右才能看到结果,而现在只需要用 1 秒左右,是不是不止快了 100 倍呢?对于一些依赖更加复杂的程序,效率提升的会更加明显!

小贴士:只有确定不会从 PATH 中加载对应的 DLL,才可以设置 PATH 为空。既然可以设置 PATH 为空,就可以设置 PATH 为任意值。

但是,这样的操作终究有些不自然。难道,Dependency Walker 本身就不支持设置搜索路径吗?

Dependency Walker 设置

简单的浏览了一遍 Dependency Walker 的菜单,发现可以通过 Options -> Configure Module Search Order... 来设置模块搜索顺序及搜索路径。

configure-module-search-order-in-dependency-walker
configure-module-search-order-in-dependency-walker

设置界面如下图:

set-module-search-order-in-dependency-walker
set-module-search-order-in-dependency-walker

可以根据自己的需要进行设置。

小贴士:The system's ’PATH‘ environment variable directories 指的是系统 PATH 环境变量对应的路径,排除它,效果与把 PATH 设置成空一样。

总结

  • Dependency Walker 可以自定义搜索目录,合理设置搜索目录,会大大提高搜索速度!

    默认情况下,Dependency Walker 会搜索 PATH 指定的路径,所以也可以通过修改 PATH 环境变量的值达到相同的效果。

  • 如果长时间(大概 5 秒钟)没有处理界面消息的话,界面就会无响应。

posted @ 2020-11-03 09:49  BCN  阅读(174)  评论(0编辑  收藏  举报