#概要
ArduinoのIDEにはデバッグ機能がない。
機能がないのは開発元のポリシーかもしれないが、どつぼに嵌った時などは俗にいうprintf攻撃だけでは力不足を感じる。
デバッグ機能付きのIDEもあるには有るが、趣味でそこまでやる踏ん切りはなかなかつかない。
そこでprintf+α程度でデバッグ出来るものを考えた。
#問題点の把握
printf攻撃には次の弱点がある。
(1)プログラムが走行している場所が判らないと仕掛けられない。
場所が判らない場合、幅広く仕掛ける必要がある。
(2)たくさん仕掛けると表示がウザくなる。
確認完了部位のprint処理は削除が必要。
(3)一時停止ができない
特定の信号などを確認する場合に必要
(4)データの書き換えができない。
書き換えられればデバッグが進む
#必要な機能
問題点の把握より、次の機能が必要と考える。
(1)トレース機能
プログラムが何処を走っているのかを確認する。
(2)ブレーク機能
プログラムを一時停止させる。
(3)データ書き換え機能
プログラム動作中にデータを書き換えて、処理の流れを変える。
#実現方法
プログラム中にブレークポイント文言を追加し、ここを通過時にデバッグ機能に分岐させる。
文言(trace(xx))をマクロ展開してデバックプログラムを呼び出す。
プログラムの書き換えが必要となるので、printf攻撃の手間とどっこいどっこいという気もしないではない。
#デバッグ記載例
下記はBlinkプログラムにデバッグ文言を追加したものである。
次の二つの文言をデバッグするプログラムに追加する事により、デバッグコマンドが使用可能となる。
(1)trace_cmdloop(引数)
デバック用コマンドを入力する為の文言。
loop内への記載が必要。
引数が'0'の場合、プログラムは停止させずにデバッグコマンドの処理を行う。
setup内で使用する場合、引数を'1'とする事により、プログラムを一時停止させた状態でブレーク等の設定を行う。
(2)trace(引数)
この文言を挿入すると、ブレーク等が可能となる。
引数はブレーク位置の識別の為に必要だが重複しても構わない。
「uno_Blink.ino」被デバックプログラム
void setup() {
// initialize digital pin LED_BUILTIN as an output.
pinMode(LED_BUILTIN, OUTPUT);
}
int tct = 100;
void loop() {
trace_cmdloop(0);
trace(1);
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
delay(tct); // wait for a second
trace(2);
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
delay(tct); // wait for a second
trace(3) {tct = trace_data; Serial.print(tct); }
}
#操作コマンド説明
全てのコマンドはコマンド+改行
(1)トレース
tnn(nnは1-31の数値)入力で、プログラム通過毎に[nn]が表示される。
t単独だと全てのトレースを表示。tの再入力で停止。
(2)ブレーク
bnn(nnは1-31の数値)入力で、プログラム通過時にbreak inを表示し停止。
b単独だと最初のブレーク位置で停止。
(3)再実行
g入力でプログラム再開
(4)ステップ実行
次のブレークポイントでプログラム停止。
(5)クリア
cnn(nnは1-31の数値)入力で、nnのトレース/ブレークを解除
c単独だと全てクリア。
(6)追加プログラム実行
enn(nnは1-31の数値)入力で、nnの右側の文言を実行。
(7)データ書き換え
wnn dddd(nnは1-31の数値、ddddはintの数値)入力で、変数のデータを書き換える。
※実際にはtrace_dataを設定して、ブレーク時に右辺を実行しているだけであり、書き換えのタイミングに注意が必要。
<Arduino MicroDebuger>
txx trace
bxx break
exx exec
cxx clear
wxx yyy write
g go
s step
l list bp
t2
[2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2][2]c
b1
Break at 1
c
g
Go
s
Next
Break at 1
s
Next
Break at 2
w3 100
100
#デバッグ処理プログラム
「uno_debug.ino」
被デバッグプログラムと同一フォルダに置く事で、各コマンドの実行が可能となる。
デバッグを無効にする場合は、#define trace(xx) if(false)とする
#define trace(xx) if(trace_break(xx))
//#define trace(xx) if(false)
#define trace_MAX 32
uint8_t trace_setting[trace_MAX] = {0};
char trace_cmdbf[16];
int trace_data;
uint8_t trace_brkall=0, trace_cmdbfp = 0;
void trace_cmdloop(uint8_t dmd){
uint8_t i;
if(trace_setting[0]==0){ // 初期化
Serial.begin(115200); delay(100); Serial.println("");
Serial.println("<Arduino MicroDebuger>");
Serial.println(" txx trace");
Serial.println(" bxx break");
Serial.println(" exx exec");
Serial.println(" cxx clear");
Serial.println(" wxx yyy write");
Serial.println(" g go");
Serial.println(" s step");
Serial.println(" l list bp");
for( i=0; i<trace_MAX; i++) trace_setting[i] = 0; trace_setting[0] = 0xff;
} else{ // ユーザ入力確認
do{
if(Serial.available()) dmd = trace_cmdset(dmd);
} while(dmd);
}
}
uint8_t trace_cmdset(uint8_t dmd){
uint8_t i, kd, cmd, bno;
int ndt;
while(Serial.available()) {
kd = Serial.read();
if (kd>=0x20) trace_cmdbf[trace_cmdbfp++] = kd;
else {
if(kd==0x08 && trace_cmdbfp!=0) trace_cmdbfp--;
if(kd==0x0d){
trace_cmdbf[trace_cmdbfp] = 0;
Serial.println(trace_cmdbf);
cmd = trace_cmdbf[0]; bno = atoi(&trace_cmdbf[1]);
if(cmd=='t') {// Trace
if(bno) trace_setting[bno] |= 0x01;
else trace_brkall ^=0x01;
}
if(cmd=='b') {// Break
if(bno) trace_setting[bno] |= 0x02;
else trace_brkall ^=0x02;
}
if(cmd=='e') {// Exec
if(bno) trace_setting[bno] |= 0x04;
else trace_brkall ^=0x01;
}
if(cmd=='l') {// Trace
Serial.print("Set [ ");
for( i=1; i<trace_MAX; i++)
if( trace_setting[i] != 0) {Serial.print(i);Serial.print(":");Serial.print(trace_setting[i]);Serial.print(" ");}
Serial.println(" ]");
}
if(cmd=='c') {// Clear
if(bno)trace_setting[bno] = 0x00;
else {
for( i=1; i<trace_MAX; i++) trace_setting[i] = 0;
trace_brkall = 0;
}
}
if(cmd=='w') { // Data
i = 0; while(trace_cmdbf[i++]!= ' ') ; // ' 'を探す
trace_data = atoi(&trace_cmdbf[i]); // 設定値
trace_setting[bno] |= 0x08; // Set oneshot
}
if(cmd=='g') { // Go
trace_brkall = 0;
dmd = 0; Serial.println("Go ");
}
if(cmd=='s') { // Step
trace_brkall = 0x02;
dmd = 0; Serial.print("Next");
}
trace_cmdbfp = 0;
}
}
}
return dmd;
}
uint8_t trace_tcnt = 0;
int trace_break(int dno){
uint8_t bkf;
bkf = trace_brkall | trace_setting[dno];
if(bkf==0) return 0; // 何もしない
if((bkf & 0x01)){ // Trace
Serial.print("[");Serial.print(dno);Serial.print("]");
if(trace_tcnt++ >32) {Serial.println(""); trace_tcnt = 0;}
}
if((bkf & 0x02)){ // Break
Serial.print("\nBreak at ");Serial.println(dno);
trace_cmdloop(1);
}
trace_setting[dno] &= 0x07;
return (bkf & 0x0c);
}
その他
上記記載のハード、ソフトは無保証であり、各自の責任においてご利用願います。