プログラミング素人のはてなブログ

プログラミングも電気回路も専門外の技術屋の末端が勉強したことや作品をアウトプットするブログ。コードに間違いなど見つけられたら、気軽にコメントください。 C#、Python3、ラズパイなど。

デバッグプリントの小技

はじめに

デバッグプリント(Debug print)とは、ソフトウェアにおいて、動作を確認するためにシリアルなどに文字列を出力するものです。
ログを出力したりするためにも使います。
これらはArduinoでのSerial.print()C言語でのprintf()などをもって実現されます。

デバッグプリントの問題点

しかしながら、これらの出力はCPUにとって負荷が高かったり、シリアルの出力が多いとこれが渋滞し、本来の動作が遅延するということが発生する場合があります。
また、本番環境でこういったものが出力されると、内容によってはノウハウが流出したり、クラックの手がかりを与えたりすることにもつながります。
一方で、どのコードをどの順番で通ったかを知るためには重要な手掛かりであり、デバッグには欠かせない機能でもあります。

デバッグプリントを簡単に切り替える方法

ここではArduinoでの例を示します。
まず、基本のSerial.print()は以下のように使います。

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  Serial.println("なにか文字列や変数");
  delay(1000);
}

この場合loop()のなかで、デバッグ中でも本番運用中でも常に同じように出力されます。

これをこのようにします。

#define DEBUG_PRINT

void debug_print(String st) 
{
#if DEBUG_PRINT
  Serial.println(st);
#endif
}

void setup()
{
  Serial.begin(9600);
}

void loop()
{
  debug_print("なにか文字列や変数");
  delay(1000);
}

このようにすると、本番運用時には#define DEBUG_PRINTコメントアウトするだけで、シリアルプリントを削減することができます。
コードが複雑になったり量が増えるなどはありますが、通常はコンパイルで最適化されるので、本番運用時にこれによって大きく遅くなったりサイズが大きくなったりはしないのではないかと思います。

また、ログレベルを指定する引数を加えて

enum
{
  TRACE,
  DEBUG,
  INFO,
  WARN,
  ERROR,
  FATAL
} log_level;

int CURRENT_LOG_LEVEL = INFO; // 現在の出力レベル設定(INFO以上のみ出力など)

void debug_print(String st, int level) 
{ 
#ifdef DEBUG_PRINT
  if (level >= CURRENT_LOG_LEVEL) 
  { 
    Serial.println(st); 
  } 
#endif 
}

void loop()
{
  debug_print("なにか文字列や変数", ERROR);
}

のようにすると、TRACEDEBUGINFOWARNERRORFATALの段階に応じてどこまで出力させるかを選択することもできます。

まとめ

デバッグプリントの小技を紹介しました。
高級な言語(Pythonなど)では組込み関数としてこのような機能がありますが、組込み開発ではC言語でハードコードしたりもします(ある程度スニペット化されてはいます)。
defineマクロで1行で書かれていたりすると、初見では読めなかったりします。