根据参数值进行检测

Instrumenting upon argument value

在我正在进行的项目中,我们同意仅使用函数 returning 错误代码(没有例外)来处理错误。

为了不 "pollute" 我的代码带有调试消息,我正在研究一个基于检测的解决方案(受 this post 启发)。

我没有使用整数作为错误代码,而是将其封装在 class 中,如下所示:

// File rc.h
class rc
{
public:
    rc(int) __attribute__((no_instrument_function));
    rc& operator=(rc);
private:
    int value;
};

然后将这些运算符和检测函数定义为:

// File rc.cc
#include <stdio.h>
#include <time.h>
#include "rc.h"

static FILE *fp_trace;
static int isError;    

extern "C"
{

  void __attribute__ ((constructor)) trace_begin (void)
    {
      fp_trace = fopen("trace.out", "w");
      isError = 0;
    }

  void __attribute__ ((destructor)) trace_end (void)
    {
      if(fp_trace != NULL) {
        fclose(fp_trace);
      }
    }

  void __cyg_profile_func_enter (void *func,  void *caller) __attribute__((no_instrument_function));
  void __cyg_profile_func_exit (void *func, void *caller) __attribute__((no_instrument_function));

  void __cyg_profile_func_enter (void *func,  void *caller)
  {
  }

  void __cyg_profile_func_exit (void *func, void *caller)
  {
    if ((fp_trace != NULL) && (isError == 1)) {
      fprintf(fp_trace, "x %p %p %lu\n", func, caller, time(NULL));
      isError = 0;
    }
  }
}

rc::rc(int valueIn) :
  value(valueIn)
{
}

rc& rc::operator=(rc rcIn)
{
  value = rcIn.value;
  if (value != 0)
  {
      isError = 1;
      fprintf(fp_trace, "%d\n", value);
  }
  return *this;
}

但是,为了不打印太多东西,我不想记录 returns return 代码 0 的函数调用,因此 isError 标志。

这可以与以下示例一起使用:

#include <cstdlib>
#include <ctime>
#include "rc.h"

rc bar(void)
{
  rc result(std::rand() % 3);
  return result;
}

int main(void)
{
  std::srand (std::time(NULL));
  rc returnCode(0);
  for (int i=0; i<10; ++i)
  {
    returnCode = bar();
  }
  return 0;
}

编译并 运行 与

g++ -finstrument-functions -g -c -o rc.o rc.cc 
g++ -g -c -o test.o test.cc 
g++ test.o rc.o -o a.out 
./a.out

使用给定 in the previously mentioned post 的脚本读取输出将导致如下结果:

Error code 2
rc::operator=(rc) at 2016-07-03T18:32:09+0200, called from main (test.cc:32)

这几乎是我所希望的。但是与我只是在 rc foo(void) 中添加一个测试以测试输出是否为非零然后记录错误的解决方案相比,这只会增加开销(加上由于检查引起的开销)发生错误(希望不要太频繁),我将在每次调用时增加由于检测而产生的开销(加上 rc 包装器可能产生的开销,但我可以接受)…

有没有我无法想到的解决方案,它不会在参数为零的情况下检测 operator=?由于 rc::value 仅在 运行 时间已知,因此我认为带有参数值的模板不会专门用于 value=0 并且不会检测这种情况。会吗?

有一个额外的限制:检测函数是赋值运算符很重要,因为我想知道调用者,因此我不能添加一些额外的代理级别。


edit 关于最后一点,我考虑过使赋值运算符内联(而不是检测)并在 return 的情况下调用检测函数代码不为零,但无法正常工作,因为 finstrument-function doc page 指出:

This instrumentation is also done for functions expanded inline in other functions. The profiling calls will indicate where, conceptually, the inline function is entered and exited.

这样它就不会指向真正的调用者。

解决方案不是依赖检测,而是依赖检测使用的 __builtin_return_address(unsigned int) 函数。

您可以使用此函数获取调用者的 return 指针:

rc& rc::operator=(rc rcIn)
{
  value = rcIn.value;
  if (value != 0)
  {
      fprintf(fp_trace, "%d %p\n", value, __builtin_return_address(1));
  }
  return *this;
}

这样你就不需要依赖工具来获取调用方地址,并且会比使用 int 增加很少的开销。