【问题标题】:Self-modifying code for trace hooks?跟踪挂钩的自修改代码?
【发布时间】:2010-12-29 01:05:13
【问题描述】:

我正在寻找将跟踪/日志记录挂钩插入到一些对性能非常敏感的驱动程序代码中开销最小的方法。必须始终编译这些日志记录内容,但大多数时候什么都不做(但什么也不做非常快)。

没有什么比拥有一个全局开/关词更简单的了,比如if(enabled){log()}。但是,如果可能的话,我什至想避免每次我碰到一个钩子时加载那个词的成本。我突然想到,我可能会为此使用自修改代码——即,在我调用跟踪函数的任何地方,当我想禁用钩子时,我用 NOP 覆盖跳转,并在我想要的时候替换跳转启用它们。

一个快速的谷歌并没有找到任何关于这方面的现有技术——有人做过吗?是否可行,是否有任何我没有预见到的主要绊脚石?

(Linux,x86_64)

【问题讨论】:

  • 注意写/执行模式排他性的可能性。它会使编写自修改代码变得更加困难......

标签: c linux self-modifying


【解决方案1】:

是的,该技术已在 Linux 内核中实现,目的完全相同(跟踪挂钩)。

请参阅the LWN article on Jump Labels 了解起点。

实际上并没有任何主要的绊脚石,而是一些次要的障碍:多线程进程(在启用或禁用代码时,您必须停止所有其他线程);不连贯的指令缓存(您需要确保在每个核心上刷新 I-cache)。

【讨论】:

  • 谢谢,这正是我想要的。
【解决方案2】:

如果您编译的驱动程序突然变大两倍,这有关系吗?

构建两个代码路径——一个有日志记录,一个没有。使用全局函数指针跳转到对性能敏感的部分,并酌情覆盖它们。

【讨论】:

    【解决方案3】:

    如果有办法以某种方式声明一个全局寄存器,您可以在每个入口点从外部将您的单词值加载到您的驱动程序中,然后检查该寄存器。当然,那么您将拒绝优化器使用该寄存器,这可能会产生一些令人不快的性能后果。

    【讨论】:

    • 你不能用 register 关键字声明一个全局变量,即使你可以,失去这个寄存器来做其他事情也是一个可怕的想法。
    • @Adam Rosenfield - 实际上有一些方法可以用一些 C 编译器声明寄存器全局变量。特别是嵌入式的。是的,因为其他一切而失去它会非常糟糕。我只是抛出了一个有效的想法并指出了它的缺点。我认为这不应该被否决,但无论如何。无论如何,我都会在这里留下我的答案。
    • 这在嵌入式架构上是一种合理的方法,但不幸的是,我至少需要在 x86_64 上运行,并且最好是可移植的。我也不明白你为什么被否决...
    【解决方案4】:

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

    一方面,您不想在每次出现日志记录可能性时都测试“启用日志记录”,另一方面需要测试“启用日志记录”并用是或否代码覆盖代码.或者你的司机是否“记得”上次不是,因为这次请求没有,所以不需要做任何事情?

    与每次测试相比,必要的逻辑似乎并不简单。

    【讨论】:

    • 你误会了。当使用重写的钩子时,唯一的工作是在跟踪打开(插入跟踪代码)或关闭(替换为无操作代码)时完成。关键是,当跟踪关闭时,跟踪点变为无操作。
    • 我的意思是,插入跟踪代码或用无操作代码替换所需的逻辑和代码很可能会抵消执行无操作所获得的时间。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-02-05
    • 1970-01-01
    • 1970-01-01
    • 2011-01-04
    • 2011-08-28
    相关资源
    最近更新 更多