我编写了一个小型代码覆盖实用程序来记录在 x86 可执行文件中命中了哪些基本块。它在没有源代码或目标调试符号的情况下运行,并且只丢失了它监视的基本块。
但是,它正在成为我的应用程序的瓶颈,它涉及单个可执行映像的重复覆盖快照。
当我试图加快速度时,它已经经历了几个阶段。我开始只是在每个基本块的开头放置一个 INT3,作为调试器附加,并记录命中。然后我尝试通过将计数器修补到任何大于 5 字节的块(JMP REL32 的大小)来提高性能。我在进程内存空间中写了一个小存根('mov [blah], 1 / jmp backToTheBasicBlockWeCameFrom'),并为其打补丁。这大大加快了速度,因为没有例外,也没有调试器中断,但我想加快速度。
我正在考虑以下其中一项:
1) 使用我修补的计数器预先检测目标二进制文件(目前我在运行时执行此操作)。我可以在 PE 中创建一个新部分,将我的计数器放入其中,修补我需要的所有钩子,然后在每次执行后使用我的调试器从同一部分中读取数据。这将使我获得一些速度(根据我的估计大约为 16%),但我仍然需要在较小的块中拥有那些讨厌的 INT3,这确实会削弱性能。
2) 检测二进制文件以包含其自己的 UnhandledExceptionFilter 并结合上述处理其自己的 int3。这意味着在每个 int3 上都没有从调试对象到我的覆盖工具的进程切换,但是仍然会引发断点异常和随后的内核转换——我认为这实际上不会给我带来太多的性能是对的吗?
3)尝试使用英特尔的硬件分支分析指令做一些聪明的事情。这听起来非常棒,但我不清楚我将如何去做——在 Windows 用户模式应用程序中甚至可能吗?如果它相当简单,我可能会写一个内核模式驱动程序,但我不是内核编码器(我涉足一点)并且可能会让我自己很头疼。还有其他项目使用这种方法吗?我看到 Linux 内核有它来监控内核本身,这让我认为监控特定的用户模式应用程序会很困难。
4) 使用现成的应用程序。它需要在没有任何源代码或调试符号的情况下工作,可以编写脚本(所以我可以分批运行),最好是免费的(我很吝啬)。但是,付费工具并没有被排除在外(如果我可以在工具上花费更少并提高性能足以避免购买新硬件,那将是很好的理由)。
5) 别的东西。我在 Windows XP 上的 VMWare 中运行,在相当旧的硬件(Pentium 4-ish)上运行 - 有什么我错过的,或者我应该阅读的任何线索吗?我可以将我的 JMP REL32 减少到小于 5 个字节(并且在不需要 int3 的情况下捕获更小的块)吗?
谢谢。