我们如何确定计算机编程的下层组件(如编译器,汇编器,机器指令等)是否完美无缺?


57

由于我们越来越依赖于计算,包括日常生活中非常重要的任务,所以我只是想知道如何测试这些重要组件。

从技术上讲,如何测试编译器和汇编器?(我想这与停止问题有关!)


36
您可能想通过“肯·汤普森·哈克”开始研究,请参阅“信任信任的思考
Bryan Oakley

7
这是一个有正确性证明的编译器示例:compcert.inria.fr/doc/index.html
Giorgio

8
大多数编译器/链接器/汇编器在很多不同的情况下都通过大量使用它们而受到最深入的测试。对于发现错误,除了拥有数百万个用户使用您的编译器之外,别无其他。
巴特·范·恩根·申瑙

3
并将操作系统添加到列表中。
Erik Eidt 2015年

Answers:


104

您不确定,但是您只是假设它们是,直到发现它们不是。这些年来,编译器和硬件中存在许多错误。

测试这些方法(例如编译器)的方式是:非常狭窄,严格地定义它们,仔细编写它们,然后使用庞大的测试套件进行测试以验证其正确性。加上编译器的广泛用户基础,将检测并报告更多错误。相比之下,牙医预约计划应用程序具有更少的用户,仍然能够检测缺陷的用户更少。

SQLite包含约73k行代码,而其测试套件包含约91378k行代码,是SQLite本身的1250倍以上。我希望编译器和其他核心工具具有相似的比率。当今的处理器基本上是使用软件来设计的,使用的是诸如Verilog或VHDL之类的硬件描述语言,并且这些处理器还具有在其上运行的软件测试以及专用的IO引脚,以便在制造时进行自测。

归根结底,这是一个概率游戏,重复而广泛的测试使您可以将缺陷的概率降低到可接受的低水平,就像其他软件项目一样。


7
我经常想知道与OP相同的问题,但是关于DBMS的问题。您给出了一个很好的示例,该示例在SQLite的上下文中给出了答案。谢谢!
布兰登2015年

7
+1,但以某种方式我怀疑“编译器和其他核心工具的比率相似”。
Mehrdad 2015年

5
请注意,(1)SQLite实际上具有两个测试套件,两者之间具有非平凡的冗余性;(2)尽管如此,SQLite中仍然存在错误。
Matthieu M.

7
我一直认为SQLite是可用于一般用途的最“经过广泛测试”的软件之一(就测试代码行/操作代码行而言),甚至比许多编译器更是如此。如果没有别的,功能齐全的编译器就是一个巨大的软件,而且我无法想象它具有一千倍的测试代码。(据报道,GCC的行数高达1450万行。似乎编译器集合本身的LOC仅为14k LOC,或者它们旁边有140亿行的测试代码库似乎不太可能!:
David Z

2
@DavidZ:这肯定是我所知道的唯一一个使用广泛的OOM测试的项目(它们使用故障注入器进行测试,并在第1次分配失败,然后在第2次分配失败时重播它们,直到整个测试为止被执行)。
Matthieu M.

46

用外行的话来说:

  1. 你不能。
  2. 编译器和解释器与任何其他(专业)软件一样,都经过单元测试。
  3. 成功的测试并不意味着程序没有错误,仅意味着未检测到错误。
  4. 长期以来,使用该编译器的广泛用户群可以很好地表明其几乎没有错误,因为用户通常会测试设计人员未想到的用例。
  5. 开源也是一个很好的指标。“有了足够多的关注,所有错误都是很浅的……鉴于有足够多的Beta测试人员和联合开发人员,几乎每个问题都将得到快速表征,并且解决方案对于某人来说是显而易见的。” 。一个封闭源代码的编译器可能会在特定的时间出现错误,或者生成的代码不能达到最佳的机器代码,而其背后的公司可能只是不公开它们的存在,而使其在产品路线图中的优先级非常低。

底线:

我想说的是OOP(O ld,O pen和P opular)。我只是编了首字母缩写。


19
+1为了发明另一个TLA(三个字母的缩写)-世界上还没有足够的这些。
s1lv3r 2015年

34
而且,OOP在计算机编程中还没有意义。因此,KTT(对您表示敬意)!
皮埃尔·阿洛

15
皮埃尔的评论是个玩笑@Dannnno。
扬尼斯2015年

19
另外,它可以P opular,ø LD和Ø笔。;)实际上,这就是我按照重要性排序的方式。
jpmc26 2015年

23
@ jpmc26我会选择实用,旧,开放和流行。至于首字母缩略词...
StupidOne

24

一直都是乌龟。

没有什么可以确定的。您别无选择,只能选择置信度。

您可以将其视为堆栈:数学>物理>硬件>固件>操作系统>汇编器/编译器/等

在每个级别上,您都可以执行一些测试来提高您的置信度。这些测试中的一些具有形式证明的质量,其中一些是基于观察的,大多数是两者的结合。

棘手的部分是在其中的一些测试中弄清递归,因为我们现在使用程序来进行证明和观察性分析,而现在手工很难做到这一点。

最终,尽管答案是您尝试了所有可以想到的东西。静态分析,模糊测试,模拟,使用有目的地选择的极端输入或随机输入运行,运行/映射每个控制路径,形式证明等。基本上,测试的目标应始终是尽一切可能证明您的产品(例如理论/芯片/程序)无法正常工作。如果您付出了真正的努力却仍然失败,则可以提高对产品正确性的置信度。

测试充其量是一个半决定性的过程,这意味着只要有一个错误,您最终就会发现它,但是您永远无法确定自己已经找到了所有错误。即使使用了经过正式验证的软件,您仍然依赖于物理,用于进行形式证明的工具,并且您证明的事情对于程序执行(通常是主观上)“预期”的事情是必要且充分的。更不用说您使用的所有其他组件都没有形式证明。


17

对于新开发者来说,这是一个“危险”的问题,因为他们将开始指责他们的工具而不是他们的代码(在那里,这样做,看到太多人这样做了)。尽管编译器,运行时环境,OS等中存在错误,但是开发人员应该切合实际,并记住这一点,直到有证据和单元测试表明存在其他错误为止,该错误才存在于代码中

在使用C,C ++和Java进行编程的25年以上的过程中,我发现:

  • 由于编译器错误而导致的两个错误(gcc和SunOS C)
  • 由于Java JVM问题(通常与内存消耗/垃圾收集相关)而导致的错误每年大约一两次。
  • 大约每月一两个库中的一个错误,该错误通常通过使用库的最新版本或还原到库的先前版本来修复

所有其他错误均与错误直接相关,或更常见的是,对库的工作原理缺乏了解。有时似乎是错误的原因是由于不兼容,例如Java类结构的更改如何破坏了某些AOP库。


我很好奇-哪种语言几年?早在C ++正确标准化之前的EGCS日子里,编译器bug的发现并不难……
Charles Duffy 2015年

3
编译器,cpu或语言越模糊,就越容易在编译器中发现错误(在其他人之前),因此在GCC C中找到2很不错:)
Surt 2015年

1
碰巧的是,我只是浪费了大约一个月的时间,以为我所遇到的问题出在我的gdb脚本中,或者是我对所检查内容的理解。最终,我变得可疑,简化了测试案例,并在库(libkvm)中发现了设计缺陷,这使内核调试器无法从核心转储中访问某些地址。即YMMV-当我发现上游的代码中有一个新错误时,尤其是我正在使用而不是在开发中的错误时,我最高兴。
艾莉·史蒂芬斯

当然,它不是编译器错误,甚至不是更常用的库之一。实话实说,我根本没有发现任何频率的错误。
艾莉·史蒂芬斯

@ArlieStephens那里有一个教训:简化测试用例是您在发现问题失败时应尽早采取的措施。不管问题是您的问题还是其他代码的问题,都将帮助您缩小范围。通常,如果问题出在其他代码中,则将导致“证据和单元测试表明”。
jpmc26 2016年

8

我认为这里有趣的一点是,绝大多数商业软件(甚至是开源软件)许可证都明确规定您不能信任该软件。

本软件按“原样”提供,不提供任何形式的明示或暗示担保,包括但不限于对适销性,特定目的的适用性和非侵权性的担保。

来自Microsoft Word许可协议

。除有限保修和适用法律允许的最大范围外,Microsoft及其供应商按原样和所有故障提供软件和支持服务(如果有),并在此否认所有其他担保和条件,无论是明示,暗示或法定的,包括但不限于对适销性,对特定目的的适用性,可靠性或可用性,响应的准确性或完整性,结果,工人的努力,所有与软件有关的病毒,缺乏疏忽,以及通过软件提供或未能提供支持或其他服务,信息,软件和相关内容,或由于使用软件而引起的其他情况。

从本质上讲,几乎在您使用的每个软件中,许可证中的这一句话都告诉您,您不能信任该软件,更不用说使用的编译器了。

软件就像一个科学理论,它被认为可以按照指定的方式工作,直到不起作用为止。


+1表示非常许可指出没有软件是完美的。
图兰斯·科尔多瓦

3
我很高兴地注意到IBM的ViaVoice for Mac中有这种做法的偏差。他们实际上不是说“如果不行,那就太糟糕”,而是说“保证软件能够按规定执行”。
WGroleau 2015年

1
保修措辞中特定措辞的通俗翻译是:“这可能是一个软件,或者可能是一个片段。它可能会起作用。它可能不会起作用。即使它起作用了,也可能无法起作用。做你想做的事哦,顺便说一句,我们可能从别人那里偷了一些东西。太可惜了,我们已经有了你的钱,并用它雇用了很多律师。 -nyah-nyah-nyaaah-naah!“。:-)
Bob Jarvis

2
@BobJarvis:我最喜欢在某些开源软件(例如nmap IIRC)上使用的保修声明是:“如果损坏,您可以保留这两部分”。
彼得·科德斯

该声明在开源软件和许多免费的封闭源软件中无处不在。大多数商业付费软件许可证中都没有显示它。
jwg '16

2

作为数学语言*的编译器作家,根据我的经验,从理论上我可以说你不能。而且某些错误会给出错误的结果,例如(从我的耻辱列表中)6/3*2从正确的位置进行计算6/(3*2)并输出1而不会崩溃或产生无意义的编译错误。

但是恕我直言,许多编译器没有其他软件那样多的错误,因为:

  • 编写单元测试很容易。每个语句都是一个单元,您可以编写简单的测试:test_unit("2+(-2)*(-2+1)*3+1",9);
  • 程序是语句的组合,对于任何要输出正确结果的程序,每个单独的语句必须给出正确的结果(大多数情况下)。因此,在程序给出正确结果的同时,几乎不可能出现任何错误。
  • 随着编写程序的大小和数量的增加,捕获错误的可能性也大大增加。

对于汇编程序,机器说明等,以上内容也适用;另一方面,芯片设计和生产中的验证和确认流程要严格得多,因为这是一项巨大的业务:电子设计自动化

在生产之前,应该对每个CPU进行严格的测试,因为每个bug的成本将近两百万美元:芯片生产中存在大量的非经常性生产成本。因此,公司在投入生产之前会花很多钱并为他们的设计编写很多仿真代码,尽管这不能提供100%的保证-例如:Pentium FDIV bug。

简而言之,不太可能在编译器,机器代码等中出现严重的错误。

我谦虚的数学语言 *


英特尔通过运行随机指令序列并与软件模型进行比较来测试其CPU的性能,其中包括: tweakers.net/reviews/740/4/…。这就是为什么您经常看到真正晦涩的勘误发布的原因,这是因为在一种非常规模式下某些不太可能的指令组合。
彼得·科德斯

0

完美无瑕?他们不是。我最近安装了一些“更新”,由于各种基础工作方式的无法解释的变化,我的ASP.NET站点又几个月后(以及代码的几个重新编程部分)再次正常工作。

但是,它们经过许多非常聪明的注重细节的人员的测试,然后使用,他们往往会注意到并报告和修复大多数问题。Stack Exchange是一个很好的例子(并改进了),所有使用这些工具的人们如何至少在实际使用范围内,如何帮助测试和分析这些惊人的复杂和低级工具的工作方式。

但是完美无缺。尽管您还可以看到Stack Exchange上的人员对性能细节以及标准合规性和怪异之处有了深刻的了解,但始终存在缺陷和不完善之处,尤其是当不同的人对缺陷的看法不同时。


-1

为了表明底层系统是完美无缺的,您要么

a)需要证明它们是完美的

  1. 数学证明
  2. 对于微不足道的程序只有现实可行

b)进行详尽的测试

  1. 仅适用于普通程序和某些简单程序
  2. 一旦计时元素进入测试,就不可能进行详尽的测试,因为可以无限期地划分时间。
  3. 除了琐碎的程序外,可能的执行选项还会成倍爆炸。

在软件测试中,穷举测试仅用于某些简单功能的单元测试。

示例:您要测试某个字段的8个字符的utf-8输入,您可以选择将输入剪切为utf-8最大长度6的8倍(以字节为单位),这样实际上就有8 * 6 = 48个字节无限的可能性。

您现在可以认为您只需要测试8个字符中每个字符的1,112,064个有效代码点,即。1,112,064 ^ 8(例如10 ^ 48)测试(这已经不太可能了),但是实际上您必须测试48个字节或256 ^ 48中的每个字节的每个值,这大约是10 ^ 120,这与国际象棋的复杂度相同与宇宙中大约10 ^ 80的原子总数相比。

取而代之的是,您可以按增加的使用顺序使用,并且每个测试都应涵盖所有先前的测试:

a)测试好样品和坏样品。

b)代码覆盖率,即 尝试测试每一行代码,这对于大多数代码而言相对简单。现在您想知道您无法测试的代码的最后1%是什么...错误,无效代码,硬件异常等。

c)路径覆盖,测试所有组合中所有分支的所有结果。现在您知道了当您的职能包含10个以上条件时测试部门为什么讨厌您。您还想知道为什么无法测试最后1%的内容...有些分支依赖于先前的分支。

d)数据测试,测试一些具有边界值,常见问题值和幻数的样本,零,-1、1,最小+/- 1,最大+/- 1、42,rnd值。如果这不能为您提供路径覆盖,则说明您尚未捕获分析中的所有值。

如果您已经这样做,则应该为ISTQB基础考试做好准备。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.