根据参数值进行检验

时间:2016-07-03 17:02:54

标签: c++ instrumentation

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

为了不污染"我的代码与调试消息我正在研究基于仪器的解决方案(灵感来自this post)。

我没有使用整数作为错误代码,而是将其封装在一个类中:

// 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;
}

但是,为了不打印太多东西,我不想记录返回返回代码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,并且不会检测该案例是否有效。会吗?

还有一个额外的约束:由于我想知道调用者,所以检测函数是赋值运算符很重要,因此我无法添加一些额外的代理级别。

关于最后一点的

编辑,我想过将赋值运算符内联(而不是检测)并在返回代码不为零的情况下调用一个检测函数但这不能像finstrument-function doc page那样说明:

  

此工具也适用于在其他功能中内联扩展的功能。分析调用将指示在概念上输入和退出内联函数的位置。

因此它并没有指向真正的来电者。

1 个答案:

答案 0 :(得分:0)

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

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

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就会增加很少的开销。