woaidongmao

文章均收录自他人博客,但不喜标题前加-[转贴],因其丑陋,见谅!~
随笔 - 1469, 文章 - 0, 评论 - 661, 引用 - 0
数据加载中……

64位开发中去除64位平台的内存错误方法

   对新平台上应用程序的开发者来说,64位平台的稳定和可靠,是吸引他们的关键;而任何内存错误问题都会导致开发工作的失败,内存错误最棘手之处在于它是难以捉摸的,找出它们非常困难且要花费大量时间。内存错误不会在通常意义上的测试中暴露出来,正是因为它们潜在的有害性,所以在程序定型之前,去除所有的内存问题就显得非

 

常必要了。

  目前有一些强大的内存错误检测工具,它们可以在运行于双核心处理器的应用程序中,找出导致线程内存错误的原因;它可在传统测试技术找不出问题的地方,找出并修正那些难以捉摸、导致程序崩溃的"元凶"。错误检测工具可帮助你在发布程序之前,找出并修正那些C/C++内存错误,而在移植程序之前修正这些问题,可提高在新平台新架构上的程序质量,使移植过程更加流水线化,并且使老程序更加健壮可靠。

  为何移植如此之难?

  在向64位处理器或新硬件移植代码时产生的问题当中,大多数开发者是负有主要责任的。就此来说,代码在移植到新平台或新架构之上时,内存问题似乎也成倍增长了。

  在过渡到64位架构时最基本的问题,就是对各种不同的int和指针在比特位长度上假定。在从long转换到int时,不管是赋值还是显式转换,都存在着一定的隐含限制。前者可能产生一个编译器警告,而后者可能被无声地接受,就此导致了运行时的各种错误。另一个问题就是int常量并不总是与int同样大小,这是混淆有符号和无符号常量的问题,同时,适当地使用有关的后缀可以减少此类问题的发生。

  另一些问题的主要原因是各种指针类型的不匹配。举例来说,在多数64位架构上,指针类型不能再放入一个int中,而那些把指针值储存在int变量中的代码,此时当然就会出错了。

  这些问题通常会在移植过程中暴露出来,因为移植从本质上来说是一种变体测试。当你在移植代码时,实际上是在创建一种"同等变体"(对原始代码的小幅改动,不会影响到测试的结果),而通过这些"同等变体",可找出许多不常见的错误。在C/C++中,创建和运行"同等变体",可揭示出以下问题:

  1、缺少拷贝构造函数或错误的拷贝构造函数

  2、缺少或不正确的构造函数

  3、初始化代码的错误顺序

  4、指针操作的问题

  5、依赖未定义的行为,如求值的顺序

  在准备移植应用程序时,有以下几个相关步骤

  第1步、在移植之前,要保证原始代码中没有诸如内存崩溃、内存泄露等问题,找出指针类型和int错误的最有效的一个方法是,采用平衡变体测试,来达到运行时错误检测的目的。

  变体测试最先是为解决无法计量测试的准确性问题而产生的,大致如下:假定已有了一个完美的测试方案,它已经覆盖了所有的可能性,再假定已有一个完美的程序通过了这个测试,接下来修改代码(称之为变异),在测试方案中运行这个"变异"后的程序(称之为变体),将会有两个可能的情况:

  一是程序会受代码改变的影响,并且测试方案检测到了,在此假定测试方案是完美的,这意味着它可以检测一切改变。此时变体被称作"已死的变体"

  二是程序没受改变的影响,而测试方案也没有检测到这个变体。此时变体称作"同等变体"

  如果拿"已死变体"和已生成的变体作对比,就会发现这个比率要小于1,这个数字表示程序对代码改变有多敏感。事实上,完美的测试方案和完美的程序都不存在,这就说上面的两种情况可能会有一个发生。

  程序受影响的结果因个体而异,如果测试方案不适当,将无法检测到。"已经变体""生成变体"的比率小于1同时也揭示了测试方案有多精确。

  在实践中,往往无法区分测试方案不精确与同等变体之间的关系。由于缺乏其他的可能性,在此我们只好把"已死变体"对所有变体的比率,看成是测试方案的精确程度。

  例1test1.c)证实了以上的说法(此处所有的代码均在Linux下编译),test1.c用以下命令编译:

 

cc -o test1 test1.c.

main(argc, argv) /* line 1 */
int argc; /* line 2 */
char *argv[]; /* line 3 */
{ /* line 4 */
int c=0; /* line 5 */
/* line 6 */
if(atoi(argv[1]) < 3){ /* line 7 */
printf("Got less than 3\n"); /* line 8 */
if(atoi(argv[2]) > 5) /* line 9 */
c = 2; /* line 10 */
} /* line 11 */
else /* line 12 */
printf("Got more than 3\n"); /* line 13 */
exit(0); /* line 14 */
} /* line 15 */


  例1:程序test1.c

  这个简单的程序读取输入的参数,并打印出相关的信息。现在假定用一个测试方案来测试此程序:

Test Case 1:
input 2 4
output Got less than 3
Test Case 2:
input 4 4
output Got more than 3
Test Case 3:
input 4 6
output Got more than 3
Test Case 4:
input 2 6
output Got less than 3
Test Case 5:
input 4
output Got more than 3


  这个测试方案在业界是有一定代表性的,它进行正则测试,表示它将测试对所有正确的输入,程序是否有正确的输出,而忽视非法的输入。程序test1完全通过测试,但它也许隐藏着严重的错误。

  现在,对程序进行"变体",用以下简单的改变:

Mutant 1: change line 9 to the form
if(atoi(argv[2]) <= 5)
Mutant 2: change line 7 to the form
if(atoi(argv[1]) >= 3)
Mutant 3: change line 5 to the form
int c=3;


  如果在测试方案中运行此修改后的程序,Mutants 13完全通过测试,而Mutant 2则无法通过。

    Mutants 13没有改变程序的输出,所以是同等变体,而测试方案没有检测

 

到它们。Mutant 2不是同等变体,故Test Cases 1-4将会检测到程序的错误输出,而Test Case 5在不同的电脑上可能会有不同的表现。以上表明,程序的错误输出,可看作是程序可能会崩溃的一个信号。

  我们统计一下,共创建了三个变体,而只被发现了一个,这说明表示测试方案的质量为1/3,正如你看到的,1/3有点低,之所以低是因为产生了两个同等变体。这个数字应当作是测试不足的一个警告,实际上,测试方案应检测到程序中的

 

两个严重错误。

  再回到Mutant 2,在Test Case 5中运行它,如果程序崩溃了,那这个变体测试不但计量到了测试方案的质量,还检测到了严重的错误,这就是变体测试发现错误的方法。

main(argc, argv) /* line 1 */
int argc; /* line 2 */
char *argv[]; /* line 3 */
{ /* line 4 */
int c=0; /* line 5 */
int a, b; /* line 6 */
/* line 7 */
a = atoi(argv[1]); /* line 8 */
b = atoi(argv[2]); /* line 9 */
if(a < 3){ /* line 10 */
printf("Got less than 3\n"); /* line 12 */
if(b > 5) /* line 13 */
c = 2; /* line 14 */
} /* line 15 */
else /* line 16 */
printf("Got more than 3\n"); /* line 17 */
exit(0); /* line 18 */
} /* line 19 */


  例2:同等变体

  在例2中的同等变体(Mutant 4),它和前一个变体的不同之处在于,Mutant 4是同等变体,这意味着它在构建时的目的,就是要使修改后的程序如同原始程序一样运行。如果在测试方案中运行Mutant 4,那么Test Case 5大概会失败--程序将崩溃。此处表明,通过创建一个同行变体,实际上是增强了测试方案的检测力度,由此得出的结论是,有以下两种方法,可提高测试方案的精确性:

  ·在测试方案中增加测试数量

  ·在测试方案中运行同等变体

  这两点是非常重要的,尤其是第二点,因为它证明了变体可提高测试的有效性。在这些例子中,是由手工创建了每一个变体,并且对每一个程序都作了单独的修改,这个步骤费时又费力,但是自动生成同等变体是有可能的,正如例3所演示的,这个程序没有输入,只有一个输出,原则上来说,它只需要一次测试:

int doublew(x)
int x;
{ return x*2; }

int triple( y)
int y;
{ return y*3; }

main() {
int i = 2;
printf("Got %d \n", doublew(i++)+ triple(i++));
}


  例3:自动生成变体

Test Case 1:
input none
output 12


  有意思的是,这个程序因编译器的差异,而分别给出答案1312(注:译者在Visual C++ 2005中,得出的结果是10)。假设你要编写一个这样的程序,还要能在两个不同的平台上运行,如果不同平台上的编译器有所差异,此时你会察觉到这个程序的不同,疑问由此而生:"是哪错了?"这有可能就是导致问题产生的原因。

  试想你在例4中创建了一个同等变体,此时这个程序的结果不依赖于编译器,实际上应是13,这也是在预料之中的。但一旦运行变体测试,就会发现错误了。

int doublew(x)
int x;
{ return x*2; }

int triple( y)
int y;
{ return y*3; }

main() {
int i = 2;
int a, b;

a = doublew(i++);
b = triple(i++);
printf("Got %d \n", a+b);
}


  例4:一个变体

  在变体测试中,最让人惊奇的是,它能找出正常看来是不可能检测到的错误,通常,这些错误隐藏得很深,直到程序崩溃时,才可能发现,但对此,程序员经常不能理解。同等变体是找出错误的机会,而不是其他。但普遍来说,程序员期望同等变体能得出与原程序一样的结果,但如果总是这样的话,那同等变体是没有任何作用了。

  第2步:当清除最致命的错误之后,要把那些可能会出错的代码在移植之前,用静态分析工具再确认一遍。在静态分析时,有两个主要的工作要做:

  ·找出并修正那些移植到新平台之后可能会出错的代码

  ·找出并修正那些可能不能很好地被移植的代码

    首先,要用业界推荐的C/C++编码标准来检查那些可能在新平台上出错的代码,以确认其编码结构没有问题。通过确认代码符合编码标准,可防止不必要的错误发生,还能减少在新平台上的调试工作量,并降低在最终产品中出现bug的机率。

  以下是一些可用的编码标准:

  不要返回对一个局部对象或对在函数内用"new"初始化的指针

 

的引用。对一个局部对象返回一个引用,可能会导致堆栈崩溃;而返回一个对在函数内用"new"初始化的指针的引用,可能会引起内存泄漏。

  不要转换一个常量到非常量。这可能会导致数值被改变,从而破坏数据的完整性。这也会降低代码的可读性,因为你不能再假定常量不被改变。

  如果某个类有虚拟成员函数,它最好也带有一个虚拟析构函数。这能在继承类中防止内在泄漏。带有任何虚拟成员函数的类,通常被用作基类,此时它应有一个虚拟析构函数,以保证继承类通过一个指向基类的指针来引用时,相应的析构函数会被调用。

  公共成员函数必须为成员数据返回常量句柄。当把一个非常量的句柄提供给成员数据时,此时调用者可在成员函数之外修改成员数据,这就破坏了类的封装性。

  不要把指向一个类的指针,转换成指向另一个类的指针,除非它们之间有继承关系。这种无效的

 

转换将导致不受控的指针、数据崩溃等问题,或者其他错误。

  不要从一个构造函数中直接访问一个全局变量。C++语言的定义之中,没有规定在不同的代码单元中定义的静态对象初始化的顺序。因此,在从一个构造函数中访问一个全局变量时,这个变量可能还没有初始化。

  当找到并修正有错误的代码之后,从那些在当前平台上运行良好的代码中再继续找,因为它们可能不能被很好地移植。以下是一些对大多数64位移植项目都适用的规则:

  尽量使用标准类型。比如说,使用size_t而不是int。如果想要一个无符号的64int,那么请使用uint64_t。这个习惯不但有助于找出和防止代码中的bug,还能在将来向128位处理器移植程序时,帮上大忙。

  检查现有代码中long数据类型的用法。如果变量、域、参数中数值的变化范围,只在2Gig-1-2Gig4Gig0之间,那么最好分别使用int32_tuint32_t

  检查所有的"窄向"赋值。应该避免这种情况出现,因为把一个long赋值给一个int,在64位数值上会导致截断。

  找出"窄向"转换。应只在表达式中使用窄向转换,而不是在操作数中。

  找出那些把long*转换成int*,或把int*转换成long*的地方。在32位环境下,这也许是可交替的,但在64位中不行,并检查所有的不匹配指针赋值。

  找出那些在乘法符号的两端,没有long操作数的表达式。要使int型表达式将产生64位结果,至少其中的一个操作数是longunsigned long

  找出long型值用int初始化的地方。应避免这种类型的初始化,因为甚至在64位类型的表达式中,int常量也可能只是代表一个32位类型。

  找出那些对int进行移位操作,又把结果赋给long的地方。如果结果是64位值,最好使用64位乘法。

  找出那些64位表达式中的int常量。在64位表达式中应使用64位值。

  找出把指针转换成int的地方。涉及指针与int互转换的代码,应仔细检查。

  检查内联汇编语句。因为它不可能被很好地移植。

  第3步:重复一遍运行时错误检测,以确认所有的修改都没有引入新的运行时错误。

  第4步:此时,你可选择进行更多的测试步骤,以保证在移植之前,所有的代码都完全正确。这个额外的步骤是单元测试,单元测试是在每一个软件单元完成之后进行的传统测试,它在开发阶段的后期,也是有益的。因为在单元级别,很容易设计出每个函数的输入,它将有助于更快地找出那些在应用级别测试中无法发现的错误。

  找出64位处理器上的问题

  也许64位处理器本身就有问题,如果是这样的话,下面的步骤应该有用: 第1步:在64位处理器上重新编译应用程序。在编译中如果有问题,应考虑是不是因编译器的不同而产生的。

  第2步:一旦重新编译代码,应进行代码检查,以确保新代码都遵循适当的编码标准。在这一点上,任何人都不希望每一次修改都带来一个错误,此时解决好过在程序运行时才发现。

  第3步:链接并生成应用程序。

  第4步:应试着运行程序。如果在64位处理器上,运行程序时发现了问题,应使用单元测试方法一个函数一个函数地去找,这样能确定哪些代码没有正确地被移植;修正这些问题直到程序可以运行。

  第5步:重复运行时错误检测。

  一旦程序可以运行,一定要重复一遍运行时错误检测,因为移植过程很可能导致新的问题产生,例如新的内存崩溃或程序工作方式有所不同。如果运行时错误检测发现了错误,那么此时赶快修正它。

  结论

  遵循此文中提及的方法,可在程序发布之前,找到并修正C/C++内存错误,并可以节省下数周的调试时间,使用户免受"灾难"之苦。

 

posted on 2009-09-08 00:35 肥仔 阅读(1952) 评论(6)  编辑 收藏 引用 所属分类: Windows开发

评论

# re: 64位开发中去除64位平台的内存错误方法  回复  更多评论   

Lots of specialists claim that <a href="http://lowest-rate-loans.com/topics/mortgage-loans">mortgage loans</a> aid people to live their own way, just because they can feel free to buy needed things. Moreover, various banks give credit loan for all people.
2010-06-30 07:34 | JuanitaGoodwin34

# re: 64位开发中去除64位平台的内存错误方法  回复  更多评论   

Lots of college students become embarrassed because of academic papers composing. However, clever people order coursework online and have advantages.
2010-07-06 21:09 | buy coursework online

# re: 64位开发中去除64位平台的内存错误方法  回复  更多评论   

Your knowledge related to this good topic is supreme! Thence scholars not have to accomplish the dissertation writing or essay thesis by their own efforts, they should get your support.
2010-07-09 00:51 | dissertation writing service

# re: 64位开发中去除64位平台的内存错误方法  回复  更多评论   

Students have to remember that the perfect essay writing service can help to have good grades, accomplishing the great quality sample essay. So, it's your own choice to buy custom essays or to spend hours for writing!
2010-07-15 06:29 | custom term paper

# re: 64位开发中去除64位平台的内存错误方法  回复  更多评论   

There is no automatic ways of research papers. Professional writers who are employed at the writing services work very hard to write the best papers!
2011-09-30 04:18 | essay service

# re: 64位开发中去除64位平台的内存错误方法  回复  更多评论   

Do you have papers completing difficulties? Doubt no more and buy essays, just because it's the best way out.
2011-10-23 02:27 | essay writing

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理