簡體   English   中英

在C中跟蹤函數調用

[英]Tracing of function calls in C

我正在為用C編寫的自動化系統開發一些模塊,我需要使用硬件執行大量工作。 我沒有看到調試事物而不是跟蹤日志的簡單方法(如傳統方法)。 所以我正在尋找一個記錄函數調用的好習慣。 至少調用和返回值的順序。

它在應用程序中執行的方式非常簡單,並且實際上使用不相關的結構來污染代碼

int function (int param){
   if(trace_level & LOG_FCALLS){
      writelog("Entering function()");
   }

   /* something useful */

   if(trace_level & LOG_FCALLS){
      writelog("Exit from function()=%d", ret);
   }
}

我決定使用一個可以完成所有臟工作的宏。 現在看起來像這樣

#define LOG_E(fn) const char *__fname=fn;  printf("LOG: Entry to %s\n",__fname)
#define return(ret)     printf("LOG: Exit from %s()=%d\n",__fname,ret)

int testFunc(){
   LOG_E("testFunc");

   /*do useful things */

   return(ret);
}

我看到了這段代碼的問題

  1. 我正在覆蓋return語句,並且需要一直寫return(ret)而不是return ret 很容易忘記這個問題。

  2. 我在我的宏中定義字符串變量。 我知道在C99中存在__func__宏,但不幸的是,我的編譯器不支持這個宏或任何其他相關的宏。

  3. 如何記錄函數參數的值?

我很確定這不是一個新問題,我不是第一個面對它的人。 我也知道AOP的事情,但代碼檢測是我的系統不可接受的解決方案,我沒有發現任何可能與我的編譯器。

所以我正在尋找一個好主意如何以最優雅的方式實現跟蹤。

我的環境:傳統代碼,C,Watcom 10.x,實時操作系統

執行此操作的超級嚴謹,專業的方法是創建一個單獨的調試/測試項目,該項目完全獨立於生產代碼。 它是這樣的:

  • 確保在生產代碼上有備份/提交。
  • 在硬盤驅動器上制作生產代碼的硬拷貝。 這將成為您的測試項目。
  • 創建一個.txt日志文件,您可以在其中編寫要記錄的每個函數的完整簽名,例如:

     int function (int param) float function2 (void) ... 
  • 創建一個小的PC程序/腳本,將上述.txt文件作為輸入,然后在源代碼中搜索匹配的函數定義行。 然后,PC程序將根據原始代碼生成一個新的.c文件,在{和之前}之后,它將調試日志代碼插入所需的函數中。 你需要幾個小時的時間來完成這樣的計划。
  • 使用腳本創建的修改后的源代碼鏈接測試項目。

上面的方法是我自己在關鍵任務軟件上做的,你有安全標准(MISRA,代碼覆蓋等)的要求,說沒有允許在最終產品中執行的代碼。

此方法可確保生產代碼的完整性,並確保測試/調試代碼不會將任何意外錯誤添加到程序中。 它還使生成代碼中的編譯開關等雜亂無章。 並且您將不會在您的項目中保留任何舊的調試代碼而您忘記刪除(否則我總是在我的程序中忘記一些調試代碼片段)。

#if defined(DEBUG_BUILD)
#  define START_FUNCTION if(trace_level & LOG_FCALLS){writelog("+++ %s()", __func__)
   }
#  define END_FUNCTION if(trace_level & LOG_FCALLS){writelog("--- %s()", __func__)
#elif defined (TIMING_BUILD)
#  define START_FUNCTION  WRITE_TIMED_LOG("+++")
#  define END_FUNCTION WRITE_TIMED_LOG("---")
#else
#  define START_FUNCTION
#  define END_FUNCTION
#endif
int function (int param){
   START_FUNCTION;
   ...
   if(error_occurred) {
     END_FUNCTION;
     return errror_code;
   }
   ...
   END_FUNCTION;
   return 42;
}

這適用於MS Visual C.對於不同的數據類型(或無),您將需要不同版本的return宏。

#include <stdio.h>

#define TRACING

#ifdef TRACING
#define LOG_E printf("Func: %s\n", __FUNCTION__);
#define LOG_R printf("Exit: %s\n", __FUNCTION__);
#define LOG_I(ival) printf("Exit: %s %d\n", __FUNCTION__, ival);

#else
#define LOG_E
#define LOG_R
#define LOG_I(ival)
#endif

int main(void){
    int retval = 0;
    LOG_E
    printf("Hello world!\n");
    LOG_I(retval)
    return retval;

}

輸出:

Func: main
Hello world!
Exit: main 0

您可以自定義編譯器來處理它。 如果要使用GCC進行編譯,可以使用MELT (自定義gcc編譯器)。

也許您可以自定義openwatcom (或支付一些OpenWatcom專家來做到這一點)......

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM