※ 2015年2月に執筆したものを転載
gdbの続きです。
前回、「最終成果物に対してのユニットテスト」として、gdbを使用しました。
ただし、テスト対象が状態機、制御器だったため効能はいまひとつ。
gdbのcall命令は純粋に関数呼び出しを再現しているだけなので、関数内部の静的変数へのアクセスもRAMに反映されます。
関数を呼び出すだけで、毎回内部状態がリセットされるタイプだと困り者だったのですが、状態が保持/更新できるのであれば、状態機、制御器のテストができないわけではない。
ということになります。
要は、連続的にデータを放り込んで、結果が正しいことが確認できれば良いのですが、それをユニットテストとしてやるにはちょっと面倒臭いです。
一応、スクリプトを記載できるので、データさえ用意できれば、そのデータを入力として渡せばOKとなります。
前回まで散々scilabでシミュレーションしてきているので、そのデータを流用してしまおうと思っています。
とりあえず以下な感じ↓
scilab/xcosにはcsv_writeなるブロックが存在しており、こいつに信号を入力すれば勝手に目的にブツが手に入ります。
・・・
のはずだったのですが、なんかcsv_writeブロックが見当たりません・・・。
マニュアルを参照したところ、
「csv_writeブロックはversion5.4.0で廃止になりました」
・・・
・・・
・・・
まじか。代替ブロックも無いのか。
うーん、結構便利なブロックだと思うのだけど、なんで廃止になったのだろう?
しょうがないので、代わりに
toWorkspaceというブロックを使用して、scilabのWorkspace変数に全サンプルデータを保持させます。
Workspace変数からcsvWriteというscilab関数を使用して、改めてcsv化します。
以下のようなファイルができます。
0,0 0,14138.66667 0,33213.32676 0,54876.71513 0,79060.49358 0,103547.1938 0,128070.1948 #・・・合計で1000行くらい
controllerという名前の関数を呼び出して、第3、第4引数から最終指令値、DutyCutを返してもらうので数値の前に”call controller(“、数値の後に”,(long)$command,(int)$dutycut)”をつけてしまえばcallコマンドの出来上がりとなります。
call controller(0,0 ,(long*)$command,(int*)$dutycut) call controller(0,14138.66667 ,(long*)$command,(int*)$dutycut) call controller(0,33213.32676 ,(long*)$command,(int*)$dutycut) call controller(0,54876.71513 ,(long*)$command,(int*)$dutycut) call controller(0,79060.49358 ,(long*)$command,(int*)$dutycut) call controller(0,103547.1938 ,(long*)$command,(int*)$dutycut) call controller(0,128070.1948 ,(long*)$command,(int*)$dutycut) #・・・合計で1000行くらい
その結果が以下
$1003 = {command = 0, dutycut = 0} $1004 = {command = 0, duty_cut = 0} $1005 = {command = -23952, dutycut = 0} $1006 = {command = -39512, dutycut = 0} $1007 = {command = -40960, dutycut = 0} $1008 = {command = -40960, dutycut = 0} $1009 = {command = -40960, dutycut = 0} $1010 = {command = -40960, dutycut = 0} #・・・合計で1000行くらい
パッと見ではさっぱりわかりません。
動いてるっぽいってことだけはわかりますが。
とりあえず、数値だけ引っこ抜いてExcelでグラフにしてしまいましょう。
ついでに、scilabのシミュレーション結果のグラフを一緒に並べます。
これは一致と見てよいでしょう。
まぁ元コードが一緒なので、コンパイラがバグってさえなければ当たり前の結果ではあるのですが、
この当たり前さえ信用できない世知辛い世の中なのですよ。
今回のコンパイラはarm-gccを使用しました。
gccじゃなきゃ使用できないから実業務ではあんまり使える場面はなさそうですね。
って思いきや、そうでもないのです。
gdbが取り込めるのは別にgccでコンパイルしたもの限定では無いのです。
ELF/DWARFというオブジェクトフォーマット仕様があるのですが、これに準じていればOKです。
ルネサスやらGreenHillsやらIARやらWindRiverやらいろいろコンパイラメーカはありますが、
皆ELF/DWARF形式のオブジェクトは生成可能です。
デフォルトでは独自の形式の場合はありますが、設計でELF/DWARF形式に切り替えられます。
よって、そのままgdbに読ませてしまえば、今回使用した方法そのままで動作します。
さらに言ってしまうと、ELF/DWARFはソースコードファイル名や各ソースライン情報を保持しているので、ソースファイルのありかをgdbに教えてしまえば、コードデバッグもできてしまいます。
純正環境と比べるとかなり見劣りしてしまうので、
・純正環境が不足している(物的にもライセンス的にも)
・純正環境を準備するのがめんどい
・自動でなんかやりたい
ってのが無ければ純正環境使った方が良いです。
とはいえ、gdbも割と優秀なフロントエンドがあるので、デバッグの目的によっては純正環境より使いやすい場合もあるんですけどね。
DDDとかはその中でもちょっと変体的かなぁ・・・。
gdbスクリプトでsfr定義用のコンビニ変数を定義してしまえば、純正環境とそう変わらない操作性は得られてしまう感じもありますが、トレースバッファなどの特殊なモジュールが装備されてたりするとやはり純正環境に分がありそうです。
(実はトレースバッファ内を直接参照&インストラクションdumpすれば近いことがgdbでもできちゃいますが、結構手間はかかることは事実かな)
ここらへんは目的に合わせて上手に使い分けるとこなんでしょうね。
コメント