从本月初开始,我加入了某机站开发团队。尽管我在通讯行业工作快10年了,但这是头一次工作于机站项目,所需学习的东西自然很多,还是那句话 — 有挑战才会有进步。在此我想与读者分享过去三周我就软件的可查错性的一点新体会。
现从事的项目是真正的嵌入式系统,机站上的MAC和PHY程序是运行在DSP之上的。加入团队伊始,同事告诉我,团队在碰到Crash这样的问题时,显得很是被动。经过我俩几番的交谈,我得知当DSP出现crash时,工程师无法轻松地获得当时的调用栈(call stack)。当时我想,“哇,尽然会出现DSP发生crash时无法以日志的形式输出调用栈的问题。看看我能否在这方面做点贡献”。于是呼,加入团队之初我就开始阅读所用DSP的相关资料。
除了学习,还得从事一些具体的工作。我被分配从事机站程序优化方面的工作,以使机站在满足用户一定数据速率的情况下能支持更多的用户。其中,我选择了去除项目所不需要的某特性(feature)的代码。当我将该特性从代码中移除后,交给同事做Sanity Test,结果发现程序会出现crash。
由于我正好关注DSP在crash时的可查错性问题,于是我借这一机会将前人留下的文档好好地读了一遍,加上同事的帮助,很快实践了一下现有的查错手段,且最终找到了引起crash的根源。
结合实践与DSP资料的阅读,我发现要在现有DSP上改善出现crash时的可查错性问题还真不是一件易事,其中最大的瓶颈在于DSP本身。在大多象x86、ARM这样的处理器上,C函数之间的调用会通过栈帧的形式形成调用栈,栈帧是由编译器生成的代码创建和使之消亡的。也正是因为调用栈的存在,使得当程序出现crash时,通过在中断(或异常)服务程序中回朔调用栈的方式获得函数调用关系以便我们找查错。然而,在DSP上,只有当以-g选项编译调试版本的程序时,编译器才生成代码为每一个函数创建栈帧,这样做的目的是为了更方便的调试。一旦不使用-g选项,DSP并不一定为每一个函数形成栈帧。
读者或许会想,为什么不在出现crash时用调试版本的程序帮助查错呢?其一,调试版本的程序在性能上达不到产品的要求,甚至可能使得产品的功能根本就不能正常工作(查错无从下手)。其二,调试版本的程序由于没有优化过,所以所需占用的文件存储空间更大。在我们的项目中,据说调试版本的程序文件因为闪存空间有限根本无法放入机站中。最后,使用调试版本的程序有时无法复现问题。
在软件开发期间出现上述可查错性问题在嵌入式软件开发领域是一种很常见的现象,在技术上无法很好地解决可查错问题时,我们应如何应对?单元测试或许是一条不错的出路!前面提到的crash问题,其实如果我做单元测试的话可以很轻松地发现问题,因为问题的根源是没有对变量指针进行初始化。
说到单元测试,或许会让不少人头痛 — 太浪费时间!这种观点多少有些主观,因为单元测试能为我们节省用于查错的时间,且帮助我们规避查错压力从而更从容地开展开发活动。单元测试的实施首先不是成本问题,而是意识和习惯问题!
幸运的是,现有的项目部署了单元测试。熟悉已有的单元测试环境是下一件我想去做的事。
原文转自:http://www.kuqin.com/testing/20120726/323318.html