1

我想添加一个函数包装器来记录某些函数的进入和退出时间。LLVM 似乎是实现这一目标的好工具。但是,我一直无法找到有关如何编写函数包装器的教程。有什么建议么?

ps 我的目标语言是 C

4

2 回答 2

0

我喜欢这样做,并根据情况使用多种方法。

如果您在 Linux 平台上,最简单的方法是使用出色的 ltrace 实用程序。您提供您正在计时的 C 程序作为 ltrace 的参数。“-T”选项将输出经过的调用时间。如果您想要调用时间的摘要,请使用“-c”选项。您可以使用“-e”和“--library”选项控制输出量。其他平台有一些类似的工具(如 dtrace),但它们并不那么容易使用。

另一种略显老套的方法是使用宏来重新定义函数名。这具有宏的所有潜在缺陷,但可以在小型程序的受控环境中很好地工作。C 预处理器不会递归地扩展宏,因此您可以在调用点从包装宏内部调用实际函数。这避免了在函数体中每个潜在返回之前放置“停止计时”代码的困难。

#define foo(a,b,c) ({long t0 = now(); int retval = foo(a,b,c); long elapsed = now() - t0; retval;})

注意表达式中非标准代码块的使用。这避免了用于计时和 retval 的临时名称的冲突。此外,通过将 retval 作为语句列表中的最后一个表达式,此代码将为嵌入在赋值或其他表达式上下文中的函数调用计时(您需要将“retval”的类型更改为适合您的函数的任何内容)。

您必须非常小心,不要在原型等之前包含#define。

使用您最喜欢的计时器函数及其适当的数据类型(double、long long 等)。我自己喜欢 C++11 中的 <chrono>。

于 2012-10-14T16:32:11.957 回答
0

func_start假设在进入每个函数和返回时都需要调用func_return,最简单的方法是执行以下操作:

for each function F
  insert a call to func_start(F) before the first instruction in the entry block
  for each block B in function F
    get the terminator instruction T
    if T is a return instruction
      insert a call to func_return(F) before T

总而言之,包括您的样板代码,您FunctionPass将不得不为此编写大约 40 行代码。

如果你真的想使用包装方法,你必须这样做:

for each function F
  clone function F (call it G)
  delete all instructions in F
  insert a call to func_start(F) in F
  insert a call to G in F (forwarding the arguments), put the return value in R
  insert a call to func_return(F) in F
  insert a return instruction returning R in F

这种情况下的代码复杂度会稍高一些,并且您可能会产生更高的编译和运行时开销。

于 2012-06-13T17:32:13.543 回答