32/70 assert调试机制


layout: "post"
title: "32/70 assert调试机制"
date: "2017-03-31 09:25"


程序的调试排错往往会占用整个工作的绝大多数时间,很多高级语言都拥有一整套的调试方法。C 语言虽然比较底层,但在调试程序时,也可以使用我之前推荐的 GDB等调试工具,但很多高手绝对不会仅依赖于调试工具,大多数人还是喜欢在开发时用 assert 来排查错误,而开发结束后,更有一些人会使用更加高效完整的日志库等来监控可能出现的 bug。

C 标准库

C 标准库中自带了 assert.h 这个文件,可以说前人在使用时已经将排错考虑细致了,我们加入这个功能后即可以直接使用 assert 进行调试了,函数声明:void assert(int expression); expression 这可以是一个变量或任何 C 表达式。如果 expression 为 TRUE,assert() 不执行任何动作。如果 expression 为 FALSE,assert() 会在标准错误 stderr 上显示错误消息,并中止程序执行。实例如下:

#include 
#include 

int main(void)
{
   int a;

   printf("请输入一个整数值: ");
   scanf("%d", &a);
   assert(a >= 10);
   printf("输入的整数是: %d\n", a);
}

请输入一个整数值: 2
a: a.c:22: main: Assertion `a >= 10' failed.
[1]    9632 abort (core dumped)  ./a

网上查了下,没能找到 assert 标准库的函数原型,于是自己写了个简易版本的 assert :

#define assert(expr) ((void)((expr) || (_assert(__FILE__,__LINE__),0)))

void _assert(const char *file, int line)
{
    printf("%s:%d", file, line);
    exit(0);
}

MCU 自带库 assert 实现

这边我们以国内广泛使用的 stm32 v3.5.0 官方库为例,使用STM32库函数的时候,你会发现带参数的库函数前面都有assert_param语句。

assert_param语句是用于程序开发的时候,调试用的检测语句。默认是不开启的,你可以无视它的存在。但是,当你在调试程序的时候,可以打开这个检测机制,调试完了再关闭。具体代码如下:

/* Exported macro ------------------------------------------------------------*/
#ifdef  USE_FULL_ASSERT

/**
  * @brief  The assert_param macro is used for function's parameters check.
  * @param  expr: If expr is false, it calls assert_failed function which reports 
  *         the name of the source file and the source line number of the call 
  *         that failed. If expr is true, it returns no value.
  * @retval None
  */
  #define assert_param(expr) ((expr) ? (void)0 : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
  void assert_failed(uint8_t* file, uint32_t line);
#else
  #define assert_param(expr) ((void)0)
#endif /* USE_FULL_ASSERT */

如果我们想要使用 assert_param 就需要定义加入 #define USE_FULL_ASSERT 语句,另外我们可以发现 assert 判定失败时会调用 assert_failed() 函数,但值得注意的是, ST 的固件库中没有预留这个函数的原型,因此我们需要手动创建这个 assert_failed 函数,以下提供一个可供参考的代码:

void assert_failed(uint8_t* file, uint32_t line)
{
    /* 
        注意:编译器 Build Output 栏是不会报错的,这边使用的是 MCU 数据如何输出,
        需要根据具体情况分析,一般 MCU 都会把结果输出到串口。 
     */
    printf("Wrong parameters value: file %s on line %d\r\n", file, line); 
    while(1);
}

另外 assert_failed()/_assert 函数基本上是所有判定失败后都需要调用的函数,具体要根据你平台提供的库而定,有些 MCU 平台是预留了 assert_failed 这种函数的,你不需要重新定义,只需要略加补充方法即可。例如 EFM32 提供的 assert 如下:

#if defined(DEBUG_EFM) || defined(DEBUG_EFM_USER)

/* Due to footprint considerations, we only pass file name and line number, */
/* not the assert expression (nor function name (C99)) */
void assertEFM(const char *file, int line);
#define EFM_ASSERT(expr)    ((expr) ? ((void)0) : assertEFM(__FILE__, __LINE__))
#else
#define EFM_ASSERT(expr)    ((void)(expr))

void assertEFM(const char *file, int line)
{
  (void)file;  /* Unused parameter */
  (void)line;  /* Unused parameter */

  /* 预留用来定制你需要的操作 */
  while (true)
  {
  }
}

小结

assert 调用基本上大同小异,错误时直接终止程序或者死循环,同时能够提供代码出错位置即可。开发时,不适用 assert 也是可以满足绝大多数需求的,但这时你往往需要自己写语句让错误输出出来,assert 仅是提供了一个更加便捷的操作方式而已。

参考链接:
http://www.runoob.com/cprogramming/c-macro-assert.html
http://www.rationmcu.com/stm32/1508.html


写作时间:21:38-22:17

你可能感兴趣的:(32/70 assert调试机制)