How to code "savely"?

ysyx · 03-12 · 80 人浏览
How to code "savely"?

This blog will introduce a BAD PRACTICE, which everyone should avoid.

这是一个你可能已经习以为常但可能会导致严重后果的行为--面对大量的变量,不仔细思考各个变量对应的含义是什么,而是直接模仿别的语句的用法直接对照地写。

它可能有七八成的概率你的代码可以正常工作,但是这并不是一个好的习惯,一旦产生错误你可能需要付出极大代价。

tl;dr

先上图。

这是我写的一条RISC-V32 addi指令的解析语句,当时写这句的时候好像是快到饭点了,想着快点写完,遂仿照下面那行的格式写,但其实这里的src1已经被计算好是寄存器rs1的值了,所以如此执行,我可能会访问某个编号为uint32_t的寄存器(显然,这很荒谬,因为寄存器成本高,数量肯定有限,访问一个编号为0x12345678的寄存器是不可能的)

为什么我第一天没发现呢?因为当时执行的指令是00 00 04 13rs1的编号为0,rs1存的值也刚好为0,所以没有发生错误。。。。。

Appendix

引用了(PA)的讲义,值得品读。

调试工具与原理

在实现监视点的过程中, 你很有可能会碰到段错误. 如果你因此而感觉到无助, 你应该好好阅读这一小节的内容.

我们来简单梳理一下段错误发生的原因. 首先, 机器永远是对的. 如果程序出了错, 先怀疑自己的代码有bug. 比如由于你的疏忽, 你编写了if (p = NULL)这样的代码. 但执行到这行代码的时候, 也只是p被赋值成NULL, 程序还会往下执行. 然而等到将来对p进行了解引用的时候, 才会触发段错误, 程序彻底崩溃.

我们可以从上面的这个例子中抽象出一些软件工程相关的概念:

  • Fault: 实现错误的代码, 例如if (p = NULL)
  • Error: 程序执行时不符合预期的状态, 例如p被错误地赋值成NULL
  • Failure: 能直接观测到的错误, 例如程序触发了段错误

调试其实就是从观测到的failure一步一步回溯寻找fault的过程, 找到了fault之后, 我们就很快知道应该如何修改错误的代码了. 但从上面的例子也可以看出, 调试之所以不容易, 恰恰是因为:

  • fault不一定马上触发error
  • 触发了error也不一定马上转变成可观测的failure
  • error会像滚雪球一般越积越多, 当我们观测到failure的时候, 其实已经距离fault非常遥远了

理解了这些原因之后, 我们就可以制定相应的策略了:

  • 尽可能把fault转变成error. 这其实就是测试做的事情, 所以我们在上一节中加入了表达式生成器的内容, 来帮助大家进行测试, 后面的实验内容也会提供丰富的测试用例. 但并不是有了测试用例就能把所有fault都转变成error了, 因为这取决于测试的覆盖度. 要设计出一套全覆盖的测试并不是一件简单的事情, 越是复杂的系统, 全覆盖的测试就越难设计. 但是, 如何提高测试的覆盖度, 是学术界一直以来都在关注的问题.
ysyx
Theme Jasmine by Kent Liao