2016-06-15 7 views
2

私はprintfを使ってUARTにログを作成する組み込みシステムで作業しています。埋め込みCでprintfを使ったマルチレベルデバッグ

必要なデバッグの種類を設定できるデバッグソースファイルを作成したいと考えています。私はラップするために、このマクロを定義し、この定数から始まって、システムログ システムのデバッグのための

  • DEBUG_LOG_1
  • システム高度なデバッグのための
  • DEBUG_LOG_2
  • ため

    • DEBUG_LOG_0:

      私はこの定数を定義し標準のprintf:

      /* Define for debugging level 0 - System Logs */ 
      #ifdef DEBUG_LEVEL_0 
      #define edi_Print_L0(...) printf(__VA_ARGS__) 
      #endif 
      
      #ifndef DEBUG_LEVEL_0 
      #define edi_Print_L0(...) printf(...) 
      #endif 
      
      /* Define for debugging level 1 - Debug */ 
      #ifdef DEBUG_LEVEL_1 
      #define edi_Print_L0(...) printf(__VA_ARGS__) 
      #define edi_Print_L1(...) printf(__VA_ARGS__) 
      #endif 
      
      #ifndef DEBUG_LEVEL_1 
      #define edi_Print_L0(...) printf(...) 
      #define edi_Print_L1(...) printf(...) 
      #endif 
      
      /* Define for debugging level 2 - Advanced Debug */ 
      #ifdef DEBUG_LEVEL_2 
      #define edi_Print_L0(...) printf(__VA_ARGS__) 
      #define edi_Print_L1(...) printf(__VA_ARGS__) 
      #define edi_Print_L2(...) printf(__VA_ARGS__) 
      #endif 
      
      #ifndef DEBUG_LEVEL_2 
      #define edi_Print_L0(...) printf(...) 
      #define edi_Print_L1(...) printf(...) 
      #define edi_Print_L2(...) printf(...) 
      #endif 
      

      次に、選択したレベルのデバッグを有効にするために、ヘッダーファイルからデバッグ定数をインポートします。

      マクロ定義についてのご意見はありますか?スコープを達成するためのスマートな方法はありますか?

      ありがとうございます!

    +1

    一般的に、私はすべてのstdioを忘れるために助言し、あなたをロールアウトだろう独自の単純なデバッグルーチン。 stdioに付属するオーバーヘッドは、通常、組み込みシステムのすべての空きメモリとCPUパフォーマンスを低下させます。さらに、存在しないタイプの安全性を持つ危険なライブラリです。 – Lundin

    +0

    'printf'は実際にデバッグしていません。 @ Lundinが書いたものに加えて:デバッガを使ってデバッグする。ただし、UARTは処理されると、最終的にテキストが失われたり、ブロックされたりします。割り込みハンドラから 'printf'する方法については話しません。 – Olaf

    答えて

    2

    ログレベル/ソースをコード全体に渡してから、個々のレベルやソースを1か所で単に無効/有効にすることは理にかなっています。

    I.e.あなたのコードでは、あなたが使用します。

    Log(Log_Comm, LevelDebug, "Some minor stuff"); 
    Log(Log_Comm, LevelWarn, "Something strange"); 
    Log(Log_Comm, LevelError, "Something seriously wrong"); 
    Log(Log_System, LevelDebug, "Some minor stuff"); 
    Log(Log_System, LevelWarn, "Something strange"); 
    Log(Log_System, LevelError, "Something seriously wrong"); 
    

    そして、あなたは、単に持っている:

    // log levels 
    #define LevelDebug 0x01 
    #define LevelInfo 0x02 
    #define LevelWarn 0x04 
    #define LevelError 0x08 
    #define LevelAll 0x0F 
    
    // enabled levels for individual log sources 
    #define Log_Comm (LevelWarn | LevelError) 
    #define Log_System (LevelAll) 
    
    #define Log(source, level, message) do { \ 
        if (source & level) { \ 
         sendToPort(message); \ 
        } \ 
    } while (0) 
    

    (編集)

    コメントで@Cliffordで指摘したように、またあるかもしれませんすべてのソース定義を通過することなく、特定のレベルをグローバルに無効にする必要があります。これはadditinalマスクを指定することによって行うことができます。

    // if LevelDebug is omitted from this mask, 
    // debug message will not be logged regardless 
    // of individual source settings 
    #define Global_Level_Mask (LevelWarn | LevelError) 
    
    #define Log(source, level, message) do { \ 
        if (source & level & Global_Level_Mask) { \ 
         sendToPort(message); \ 
        } \ 
    } while (0) 
    

    1つの追加の懸念は、これはあなたのコードの周りに生成されます「到達不能コード」警告の数かもしれません。

    // visual studio will show a warning 
    // C4127: "conditional expression is constant" 
    // when compiling with all warnings enabled (-w4) 
    
    // these pragmas will disable the warning around if's 
    #define Log(source, level, message) do { \ 
    __pragma(warning(push)) \ 
    __pragma(warning(disable:4127)) \ 
        if (source & level & Global_Level_Mask) { \ 
    __pragma(warning(pop)) \ 
         sendToPort(message); \ 
        } \ 
    } while (0) 
    

    ない、それは少し醜い見えますが、私見それを:私は、Visual Studioで、たとえば、それがif文の周りのプラグマを追加することで解決することができ、他のコンパイラでは、この問題を解決する方法がわからないけど、簡単な使用とより多くの制御を可能にします。

    もちろん

    (あなたが1つの少ないパラメータを持っているので、おそらく単純)、何が各ログレベルに別々のマクロを持っていることからあなたを停止していない、すなわち:

    #define LogDebug(source, message) Log(source, LevelDebug, message) 
    #define LogInfo(source, message) Log(source, LevelInfo, message) 
    #define LogWarn(source, message) Log(source, LevelWarn, message) 
    
    // usage example: 
    LogDebug(Log_Comm, "Some minor stuff"); 
    LogWarn(Log_System, "Something strange"); 
    
    +0

    私はこの考えが好きですが、開発が必要かもしれません。どのようにすべてのデバッグを完全に除外しますか?可能なすべての 'source'マクロを明示的に定義して' if(0&0x01')などのように展開して、 "常にfalse"や "到達不能なコード"の警告を生成する必要があります。すべてのデバッグコードをソースから完全に除外する手段が必要です。空であるかヌルである 'Log'の定義です。 – Clifford

    +0

    @Clifford:はい、これは単なるアイデアでしたが、あなたは警告について正しいです。レベル別のログを除外するために、あるレベルを通過するだけのif条件(if(source&level&GlobalEnabledLevels))に追加の "global enable"フラグを単純に入れることができます。警告に関しては、プラグマなしでこれに対処するきちんとした方法があるかどうかはわかりません。プラグマはマクロ内ではあまりよくサポートされていないことがあります。 Visual Studioの例(MicrosoftのC99コンパイラ)を使用してコードを更新します。 – Groo

    +1

    @Groo:遅く返事を申し訳ありませんが、私は非常に忙しかった!私はあなたの提案に従って、私のデバッグインターフェイスは非常にうまく動作します! – Federico

    0
    1. コンパイラは分かりませんが、printf(...)はコンパイラを生成します エラーです。
    2. 同じ名前の関数が複数定義されているため、ヘッダーをコンパイルできません。

      #ifdef DEBUG_LEVEL_0 
      #define edi_Print_L0(format, ...) printf(format, ##__VA_ARGS__) 
      #else 
      #define edi_Print_L0(...) do{}while(0) 
      #endif 
      

      をしかし、あなたのヘッダーを再考した方がよい:

    には、単にあなたができるヒントを提供します。あなたは関係なく、定義されたどのレベルのすべてのマクロを定義する必要があります

    #define MH_HGREEN "\033[1m\033[32m" 
    #define MH_END  "\033[m" 
    
    +0

    なぜDEBUG_LEVEL_0が定義されていないときに "do {} while(0)"を使用しますか? 「edi_Print_L0(...)printf(...)」を使用しますか? – Federico

    +0

    私はあなたのコンパイラがどちらか分かりませんが、私はそれがコンパイルされないことを99%確信しています。 BTW 'printf(...)'と 'printf(__ VA_ARGS __)'の違いはどう思いますか? – LPs

    +0

    @Federico:はい、あなたのコードはコンパイルされますか?マクロが何もしないようにしたいのであれば、全く内容を持たなくてはなりません(または "no-loop"が提案されているか、単に '(void)0')。引数を指定しないでprintfを呼び出すのはおそらくエラーで、 'printf(" ")'があっても、printfは完全に不必要に呼び出されます。 http://stackoverflow.com/questions/1306611/how-do-i-implement-no-op-macro-or-template-in-cを参照してください。 – Clifford

    2

    、あなたはそのマクロを呼び出すそれ以外の任意の場所:例えば、

    色をbできる
    #define STRCAT(a, b, c, d, e) a b c d e 
    #define DEBUG(level, message, color, ...) if (level < DEBUG_LEVEL) fprintf(stdout, STRCAT(color, "%s: %s - ", message, MH_END, "\n"), __FILE__, __func__, ##__VA_ARGS__) 
    

    :私がするために使用しましたが定義されていない場合、エラーが発生します。非アクティブにしたいマクロは、単に空文として定義することができます。

    たとえば、DEBUG_LEVEL_0が定義されていないがDEBUG_LEVEL_1の場合は、edi_Print_L0()の2つの異なる定義があるなど、再定義が動作しない場合もあります。同様にDEBUG_LEVEL_0DEBUG_LEVEL_1が両方とも定義されている場合、依然として複数の定義があります。あなたは、定義を作成する必要があり相互に排他的、または複数のレベルのマクロ定義は、唯一の最高レベルがアクティブであることを確認存在している場合:

    :私には、また、デバッグマクロのより有用な定義を示唆している

    #if defined DEBUG_LEVEL_2 
        #define edi_Print_L0(...) printf(__VA_ARGS__) 
        #define edi_Print_L1(...) printf(__VA_ARGS__) 
        #define edi_Print_L2(...) printf(__VA_ARGS__) 
    #elif defined DEBUG_LEVEL_1 
        #define edi_Print_L0(...) printf(__VA_ARGS__) 
        #define edi_Print_L1(...) printf(__VA_ARGS__) 
        #define edi_Print_L2(...) 
    #elif defined DEBUG_LEVEL_0 
        #define edi_Print_L0(...) printf(__VA_ARGS__) 
        #define edi_Print_L1(...) 
        #define edi_Print_L2(...) 
    #else 
        #define edi_Print_L0(...) 
        #define edi_Print_L1(...) 
        #define edi_Print_L2(...) 
    #endif 
    

    例えばそのようにし

    #define edi_Print_L0(format, ...) printf("\nL0:%s::%s(%d) " format, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) 
    #define edi_Print_L1(format, ...) printf("\nL1:%s::%s(%d) " format, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) 
    #define edi_Print_L2(format, ...) printf("\nL2:%s::%s(%d) " format, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) 
    

    、ライン:

    edi_Print_L2("counter=%d", counter) ; 
    

    機能でファイルmain.cの言うライン24でmain()counterは、レベル0または1で何もしないが、レベル2の意志の出力でます例えば25に等しい時:

    L2:main.c::main(24) counter=25 
    

    だから、あなたはそれはあなたがコード内の正確な場所で必要とデバッグ出力およびデバッグレベルを取得で発行された。

    (より容易に維持)より良い解決策は、数値を持つ単一のマクロDEBUG_LEVELを持つことです。

    #if !defined DEBUG_LEVEL 
        #define DEBUG_LEVEL = -1 
    #endif 
    
    #if DEBUG_LEVEL >= 2 
        #define edi_Print_L2(format, ...) printf("\nL2:%s::%s(%d) " format, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) 
    #else 
        #define edi_Print_L2(...) 
    #endif 
    
    #if DEBUG_LEVEL >= 1 
        #define edi_Print_L1(format, ...) printf("\nL1:%s::%s(%d) " format, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) 
    #else 
        #define edi_Print_L1(...) 
    #endif 
    
    #if DEBUG_LEVEL >= 0 
        #define edi_Print_L0(format, ...) printf("\nL0:%s::%s(%d) " format, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__) 
    #else 
        #define edi_Print_L0(...) 
    #endif 
    

    このソリューションでは、各マクロの一つだけ定義することができますので、メンテナンスが非常に簡単かつ少ない誤差であります易しい。

    +0

    '' \ nD2:%s ::%s(%d)のような連結を使用すると、 '' format'は文字列リテラルであるときにうまく動作しますが、 '' format''が変数であるときはうまく動作しません。おそらく 'printf()'を代わりに2回呼び出すでしょうか? – chux

    +0

    @chux:そうですが、フォーマット文字列に変数を使用すると、フォーマット文字列エクスプロイト(またはフォーマット指定子を含む文字列からの偶発的なエラー)の可能性があるため、いずれの場合でも悪い習慣です。この実装は、デバッグコードにバグを導入することを積極的に防ぎます - これは常に有用です!たとえば 'printf(str)'は、 'str'が' '100%correct" 'を指していれば期待通りに動作しません。代わりに 'printf("%s "、str)'を使うべきです。 – Clifford

    +0

    私は "フォーマット文字列の変数は悪い習慣です"とは反対ですが、UVは便利な答えです。 – chux

    関連する問題