用于跟踪钩子的自修改代码?

3

我正在寻找一种最低开销的方法,来将跟踪/日志钩子插入到一些非常性能敏感的驱动程序代码中。这些日志记录必须始终被编译,但大多数时候不执行(但执行速度非常快)。

没有比只有一个全局开/关变量,进行if(enabled){log()}更简单的方法了。然而,如果可能的话,我甚至想避免每次触发我的钩子时都要加载这个变量的成本。我想到,我可以潜在地使用自修改代码来解决这个问题--也就是说,无论我何时调用我的跟踪函数,当我想禁用钩子时,覆盖跳转指令为NOP,当我想启用它们时,替换跳转指令。

快速搜索并未出现任何先前的类似案例--有人做过吗?它可行吗,是否存在任何我没有预见到的重大障碍?

(Linux,x86_64)


1
要注意写/执行模式的独占性可能性。这会使编写自修改代码变得更困难... - dmckee --- ex-moderator kitten
4个回答

4

你编译后的驱动程序突然变大会有影响吗?

构建两个代码路径--一个带有日志记录,一个不带。使用全局函数指针跳转到性能敏感部分,根据需要覆盖它们。


4
是的,这种技术已经在Linux内核中实现,目的正是为了跟踪钩子。
请参考Jump Labels的LWN文章作为起点。
实际上并没有什么大的障碍,但还有一些小问题:多线程进程(在启用或禁用代码时,您必须停止所有其他线程);不连续的指令缓存(您需要确保在每个核心上都刷新指令缓存)。

谢谢,这正是我在寻找的。 - kdt

0
如果有一种方法可以声明一个寄存器为全局的,那么你就可以在从外部进入驱动程序的每个入口点处将寄存器加载为你的字的值,然后只需检查寄存器即可。当然,这样做会拒绝优化器使用该寄存器,这可能会带来一些不愉快的性能后果。

你不能使用 register 关键字声明全局变量,即使你可以这样做,失去该寄存器对其他所有内容来说都是一个可怕的、可怕的想法。 - Adam Rosenfield
@Adam Rosenfield - 实际上,有一些C编译器可以声明全局寄存器。特别是嵌入式的编译器。是的,对于其他所有东西都失去它将非常糟糕。我只是提出了一个有效的想法并指出了它的缺点。我不认为这应该被投票否决,但无论如何,我仍旧在这里留下我的答案。 - Omnifarious
这在嵌入式架构上是一个合理的方法,但不幸的是我需要在至少x86_64上操作,并且最好是可移植的。我也不明白为什么你被踩了... - kdt

0

我写的不是关于这是否可能,而是你是否获得了任何重要的东西。

一方面,您不希望每次出现日志记录可能性时都测试“启用日志记录”,另一方面需要测试“启用日志记录”,并使用yes或no情况下的代码覆盖代码。或者您的驱动程序“记住”上次它是no,因为这次请求的是no,所以不需要做任何事情吗?

与每次测试相比,所需的逻辑似乎并不是微不足道的。


你误解了。当使用重写钩子时,只有在打开跟踪(插入跟踪代码)或关闭跟踪(替换为空操作代码)时才会执行工作。整个重点在于当关闭跟踪时,跟踪点变成无操作。 - kdt
我的意思是,插入跟踪代码或替换为空操作代码所需的逻辑和代码可能会抵消执行无操作所节省的时间。 - Olof Forshell

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接