llvm函数包装器用于计时

时间:2012-06-11 17:52:14

标签: llvm clang

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

P.S。我的目标语言是C

2 个答案:

答案 0 :(得分: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

在这种情况下,代码复杂性会略高,并且可能会导致更高的编译和运行时开销。

答案 1 :(得分:0)

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

如果您使用Linux平台,最简单的方法就是使用精彩的ltrace实用程序。您提供C程序作为ltrace的参数。 “-T”选项将输出已用的通话时间。如果需要呼叫时间摘要,请使用“-c”选项。您可以使用“-e”和“--library”选项控制输出量。其他平台有一些类似的工具(如dtrace),但它们不太容易使用。

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

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

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

你必须非常小心,不要在原型之前加入#define等。

使用您最喜欢的计时器功能及其相应的数据类型(双倍,长,无论如何)。我喜欢< chrono>在C ++ 11中我自己。