タスク/割込みトレース機能の自作について

動的テストツールを拝見して(記事)、トレース機能を自作した時のことを思い出したので、少し紹介しておきましょう。

リアルタイムOS(RTOS)の話です。

 

デバッグにおいて、一番厄介なのが「どこで何が起こったのか分からない」というケースですね。

そんな事態に陥らない様に、ログやデバッグメッセージを出力する「仕込み」に気を配るわけですが、いつもうまくいくとは限りません。

ある程度目星が付けられる場合はまだしも、まったく見当がつかない、というときは困りものです。

何度も現象を再現させ(再現率が低いときは、再現しやすい方法を模索し)、少しずつ発生個所を絞り込んでいく。
気が遠くなりますよね。

大変な労力をかけて絞り込んでも、原因究明に至らない、ということもしばしば。

そんな時、もう一つの方法がトレースという方法です。

ここで言う「トレース」は、実機動作においてプログラムのどこをどんな順番に実行し、どこまで実行されたか、どこで実行が止まったか(暴走したか)、を記録する手段の事です。

関数や機能単位の入り口と出口に記録処理を仕込むくらいであれば、難しいことではありませんが、
タスク(スレッド)の切り替え(ディスパッチ)や、割込みへの遷移/復帰を記録するには、一歩踏み込んだ手法が必要になります。

JTAGデバッガなどにトレース機能が付いていて、ターゲットのOSに対応している場合は、素直にそれを使えばよいでしょう。

しかし、それが無い場合、自分で何とかするしかありません。

OSのマニュアルを見てみてください。

タスク ディスパッチ をHOOKするサービスコールがあると思います。
API関係とデバッグ関係が分かれている場合は、デバッグ関係のページに属していると思われます。

これは、タスクに遷移したときと、復帰したときに呼ばれる関数です。

その使い方はOSによって異なりますが、例えば、「事前に遷移したときに呼んでもらう関数を指定しておく」という実装が一般的でしょう。

割込みの場合はhookではなくEnter(通常処理から割込み処理に入った)とLeave(割込み処理を抜けて通常処理に戻る)といった名前で用意されていると思います。

これが無い場合は、あきらめるか、OSの改造にチャレンジするか、ですが、言うまでもなく大変です。

もう一つ、大きな課題があります。

CPUやOSが停止してしまう様なバグの場合、記録処理も止まってしまうので、実際に問題の発生した箇所での情報を記録/入手する手段がありません。
記録に残るのは、問題発生個所のはるか手前までの情報のみです。

これを何とかするためには、記録処理において、記録媒体への書き込みを完了させる必要があります。

従って、記録媒体は、書き込みに時間のかかるモノであってはならないのです。
「トレースを開始したら実機動作が鈍くなる」などというものは使い物になりませんので。

つまり、HDDやFLASH-ROM等、書き込みに時間がかかるデバイスでは無理です。
SRAMやFRAMなど、直接書き込みができて、電源再起動やリセットしても記録が残ってるデバイスがお勧めです。

もう一つの方法は、記録処理において、その情報を外部へ吐き出すことです。
正確には、「吐き出し終わる」、あるいは「送出ハードに渡し終わる」ことです。

シリアルを用いる場合は、送出FIFOに入れ終わるところまで、です。
シリアルはスループットが遅いのでLAN(Ethernet、TCP/IP)としたいところですが、APIやシステムコールに渡したところから実際に吐き出されるところまで、多くのソフトウェア処理が介在するため、その間にCPUやOSが止められては、間に合いません。

先に紹介した動的テストツールDT10(記事)の場合、吐き出し手段が色々用意されていて、GPIOでも可能、というところがすごいですね。
これによって、対応可能なターゲット層が格段に広がると思います。
多くの場合、シリアルなどは既に使用されていて、空いていないと思いますので。

GPIOであれば、使っていないLEDをやめて、こちらに使う、といった方法も考えてよいと思います。
(まあ、たまに空きGPIOはおろかLEDも1つ2つしかついていない、という酷いボードもありますが…)

今度新しい開発環境を使うときには、あらかじめDT10の利用を提案したいですね。
デバッグ段階になってから、死ぬ思いしてトレース機能を実装する…などと言うアホなことはもうしたくありません。