在嵌入式软件中使用断言的一点想法

嵌入式软件中的断言应该分成三个级别,而不是仅有启用和关闭两种。1、启用断言并打印可读信息;2、启用断言并打印代码地址、3、关闭断言。


原文:http://blog.csdn.net/zoomdy/article/details/46289867
mingdu.zheng at gmail dot com


矛盾

断言可以帮助开发人员在软件开发过程中比较容易地发现软件缺陷。典型的做法是在开发过程中启用断言,而在最终发布时关闭断言。

对于嵌入式软件而言,可能在软件开发的后期就不得不关闭断言,因为嵌入式系统的内存资源是非常有限的,而启用断言会占用相当可观的ROM资源,当嵌入式软件开发进行到后期时,所有的功能代码集成以后这些功能代码基本上用光了ROM资源,已经没有ROM资源可以启用断言(除非在开发时使用的是比最终发布的硬件具有更多ROM资源的硬件)。

还有人建议永远都不要关闭断言,即使是发布的版本也不要关闭断言。对于C/C++这样的仅有极少运行时检查特性的语言而言,断言还是一种简单有效的运行时检查机制。另外,少量的软件缺陷可能仍然残留在系统中,断言仍然可以检测出这些残留的缺陷并报告。发布时关闭断言,当发现问题时重新启用断言进行检查,这样的做法对于时间相关的代码而言可能是不可重现的,因为启用断言后程序的执行时间是与关闭断言时不相同的。这是建议总是保留断言的另一个原因,为了执行时间上的一致性。

一方面,嵌入式系统的ROM资源是有限的,增加ROM资源意味着增加成本;另一方面,断言应该尽可能地保留以便能检测错误。嵌入式系统开发过程总是充斥着矛盾。

解决办法

解决这个矛盾的办法是减少断言所占用的ROM资源量。断言宏通常会打印一些可读信息,例如产生断言的文件名和行号以及断言条件等,这些可读信息可以非常快速地帮助开发人员定位发生断言的位置。这些可读信息基本上是字符串常量,占用大量ROM资源的正是这些字符串常量。如果用代码地址来代替这些可读字符串,那么就可以节省大量的ROM资源,代价是开发人员必须通过一些工具(例如addr2line)来定位产生问题的代码,而不是直观地看到问题所在之处。

由此可见,断言应该分成三个级别,而不是仅有启用和关闭两种。

  1. 启用断言并打印可读信息,可读信息可以帮助开发人员快速定位问题,因此有条件打印可读信息的情况下还是应该打印可读信息,这可以提高开发效率的。
  2. 启动断言并打印代码地址,在发布版本中以及ROM资源十分紧张的情况下使用这种方式,一方面可以继续检测错误,另一方面尽可能节省资源。
  3. 关闭断言。

调试专用断言

在调试阶段,希望断言失败时可以立即进入调试模式,源代码调试器将当前代码停留在断言失败处。为了达到这个目的,可以在断言实现中加入断点指令,一旦断言失败就会执行到断点指令,继而进入调试模式。

断言定义示例

//#define ZMD_ASSERT_EN_FILE_LINE
//#define ZMD_ASSERT_EN_PC
#define ZMD_ASSERT_EN_BKPT
//#define ZMD_ASSERT_EN

#if defined(ZMD_ASSERT_EN_FILE_LINE)
   #define ZMD_ASSERT(c) \
   if(!(c)) { \
       zmd_diag_print_file_line("ASSERT", __FILE__, __LINE__, #c); \
       for(;;) {} \
   }
#elif defined(ZMD_ASSERT_EN_PC)

   __attribute__( ( always_inline ) ) static inline uint32_t __get_PC(void)
   {
       uint32_t result;
       asm volatile ("MOV %0, pc" : "=r" (result) );
       return(result);
   }

   #define ZMD_ASSERT(c) \
       if(!(c)) { \
             zmd_diag_print_pc(__get_PC()); \
             for(;;) {} \
       }
#elif defined(ZMD_ASSERT_EN_BKPT)
   #define ZMD_ASSERT_BKPT()  __asm volatile ("bkpt 0")

   #define ZMD_ASSERT(c) \
   	    if(!(c)) { \
   		    ZMD_ASSERT_BKPT(); \
   		    for(;;) {} \
   	    }

#elif defined(ZMD_ASSERT_EN)
   #define ZMD_ASSERT(c) \
       if(!(c)) { \
           for(;;) {} \
       }
#else
#define ZMD_ASSERT(c)
#endif
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值