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

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

从调用栈可知,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

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

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

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

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

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

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

PATH-on-my-machine

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

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

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

手动设置 PATH

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

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

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

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

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

设置界面如下图:

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

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

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

总结

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

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

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

BianChengNan wechat
扫描左侧二维码关注公众号,扫描右侧二维码加我个人微信:)
0%