typedef enum {
A = 0,
B = 1,
C = 2
} my_enum;
/**
* @warning If random is NULL or blabla is invalid the behavior of this function is undefined.
*/
int foo(int *random, my_enum blabla)
{
//Prints: [DBG][<timestamp>][<thread_name>][foo:<line_no>]: Entering - Hello world!
LOG_DEBUG_ENTERING("%s", "Hello world!");
int ret_code = 0;
*random = 50;
ret_code = do_something(*random, blabla);
//Prints: [DBG][<timestamp>][<thread_name>][foo:<line_no>]: Exiting - <ret_code>
LOG_DEBUG_EXITING("%d", ret_code);
return ret_code;
}
我正在开发一个可移植的多线程应用程序,它可以在嵌入式和非嵌入式系统上运行,可以与至少支持 c99 的不同编译器一起使用。
我工作的一些平台没有调试器,因此拦截错误相当困难,在这些情况下唯一可以帮助我的工具是应用程序本身生成的日志消息。
因此,在此应用程序的所有功能中,我添加了
LOG_DEBUG_ENTERING()
和 LOG_DEBUG_EXITING()
宏,以便我可以跟踪应用程序的执行情况。
这些宏/函数的目的是确保一旦执行完毕消息就已经被打印出来,或者至多要打印的消息已经在某个地方(可能在另一个进程中)排队以便尽快打印,这就是实现并且取决于平台,在任何情况下,即使应用程序由于错误而崩溃,所有消息都会打印出来。
以上或多或少是我的应用程序中的一个真实示例。
如果以下列方式之一调用
foo()
将会导致应用程序崩溃:
int random;
foo(NULL, A); //It will crash when foo() tries to assign 100 to the address pointed to by random
foo(&random, 1000); //Crash because the bheaviour of do_something() is not defined if blabla is out of range
我可以轻松地从日志消息中找出应用程序在代码中崩溃的位置,但我担心编译器可能会更改宏调用顺序,例如这样:
int foo(int *random, my_enum blabla)
{
int ret_code = 0;
*random = 50;
ret_code = do_something(*random, blabla);
//Prints: [DBG][<timestamp>][<thread_name>][foo:<line_no>]: Entering - Hello world!
LOG_DEBUG_ENTERING("%s", "Hello world!");
//Prints: [DBG][<timestamp>][<thread_name>][foo:<line_no>]: Exiting - <ret_code>
LOG_DEBUG_EXITING("%d", ret_code);
return ret_code;
}
在这种情况下,应用程序将崩溃,而不会打印任何消息。
如何防止编译器/CPU改变
LOG_DEBUG_ENTERING()
的调用顺序?
foo()
和do_something()
的输入参数不是故意检查的,其想法是我们不保护我们的应用程序免受错误编码的影响,但我们仍然希望能够在必要时捕获这些错误。
使用可靠且理智的编译器。
任何符合标准的编译器都不允许以影响程序输出的方式重新排序执行。
此外,有许多编译器在一种或多种方面并不严格兼容,但对于他们来说,像这样破坏你的程序仍然要么是一个错误,要么是一种疯狂的设计。