(2023/9/13)
[連載13] 例外が発生した場合ー2
前回は、例外が発生した場合のデバッガの振る舞いについて記載しましたが、すべて書ききれたわけではありません。
今回は、もう一つの機能についてご紹介します。
今でこそ滅多に見ませんが、昔のWindowsはよくクラッシュしましたねー。。。(あ、もう知らない人も多い?)
いきなり、画面が真っ青になり、よくわからないログがバババーッと表示されましたよね。
エラーの番号とか出たりして、番号をよく調べたものです。
Linuxでもシステムクラッシュに遭遇したことありませんか?
筆者は数年前、飛行機で遭遇しました。といっても怖い話ではありません。
機内の暇つぶしにゲームをやっていると、突然シートモニタがクラッシュしました。
で、バババーッとログが表示された後、しばらくして勝手に再起動し、上部に例のペンギンさんが鎮座したまま、画面が固まりました。(悲)
こんなところで君に会うとは思わなかったよ。。。
この、バババーッと表示されるアレは、「クラッシュダンプ」と言われるものです。
表示されている内容は、設計者が見ると、発生した例外の内容や場所がわかるようになっていて、現象の原因を解析するのに役立ちます。
今回の焦点はこの、クラッシュダンプです。
前回のおさらいを少し。
SOLID-IDEでのデバッグ中に予期しない例外が発生した場合、SOLID-IDEが要因等表示してくれました。
そして開発が進み、ある程度例外が発生しても耐えうるように例外ハンドラを自作できれば、デバッガを接続していない場合でも何らかの対処ができることをご紹介しました。
前回はUART経由でContext情報を表示しただけでしたが、本来はシステム仕様によって適切に処理すべきところです。
しかし、さらに、予期しない例外が発生することがあり得るのが、組み込み開発の恐ろしさです。
万が一そうなった場合、対処ができるのか?
ここは、システム設計屋さんの腕の見せ所の一つではないでしょうか。
SOLID-OSでは、予期しない(例外ハンドラにより対処されていない)例外発生時、
・スナップショット(クラッシュダンプ)を指定のメモリに保存
・保存した内容をデバッガで吸い上げて内容表示
ができます。
こちらで紹介されている機能です。
http://solid.kmckk.com/doc/skit/current/user_guide/crashdump_debug.html
見ていきましょう!
予期しない例外が発生した場合、その時点のスナップショットを指定の場所に保存しておく機能について、ご紹介します。
先程のURLにある前半部分と、その前準備です。
http://solid.kmckk.com/doc/skit/current/user_guide/crashdump_debug.html
前回同様、Cortex-A9搭載ボード×PARTNER Jet2の組み合わせで動かしてみます。
SOLIDでのスナップショット機能は、OSの実装レイヤを作成することで機能するようになっています。
今回は、この実装レイヤを、スナップショットをメモリに保存する、という方法でご紹介します。
この方法は、評価ボードを使用している時など、デバッグ初期のシステムで有効な手段です。
SOLIDを製品に適応するような場合は、この実装レイヤを、例えばSDカードなど何らかのストレージに保存するように作成することで、JTAGデバッガなどがない時でもスナップショットを取得できるようにする事ができます。
まず、保存先をどこにするのかを決めます。
書き込みが可能な場所であり、どこからも使われていない場所である必要があります。
今回も引き続き、前回のデータアボート発生プログラムを使用します。
では、使われていないところを見てみましょう。
こういう場合はMMUの登録状態を見れば良いです。
という事で、ソリューションエクスプローラにある、memory_map.smmファイルをダブルクリックして、[メモリマップデザイナ]ウインドウを立ち上げてみましょう。
※[メモリマップデザイナ]ウインドウについては、過去記事でご紹介しています。
https://note.com/yn_2022/n/n40b4e94e7a01#34de5483-0322-4a4e-960a-bd70b75e4139
RAMとして割り当てられている領域の最終物理アドレスは0x4bffffffです。
物理アドレス0x4c000000から0x5cffffffまで空いていることになるので、この領域を「保存先のアドレス」として使いましょう。
先程開けた0x4c000000~をメモリマップデザイナに登録し(正しくは、[メモリマップデザイナ]ウインドウ経由でMMUに登録し)、使えるようにします。
仮想アドレスは0x20000000あたりが空いているようです。なのでそうしましょう。
[メモリマップデザイナ]ウインドウの左上にある[Add]ボタンを押して、メモリコンフィグレーションを追加します。
新規行を、以下のように変更します。
これで保存先メモリの準備が整いました。
次に、int SOLID_SNAPSHOT_Create(uint32_t attr, SOLID_CPU_CONTEXT *pContext)により、スナップショットを作成します。
今回は、自作例外ハンドラから、例外発生時のコンテキストの状態をスナップショットすることが目的なので、自作例外ハンドラ内に記述します。
第2引数には、例外ハンドラに渡されている pContextを指定します。
static int my_dabort_handler(void* param, SOLID_CPU_CONTEXT* pContext)
{
int snapshot_result;
/* データアボート時にスナップショットを生成 */
snapshot_result = SOLID_SNAPSHOT_Create(SOLID_SNAPSHOT_ATTR_DEBUG, pContext);
スナップショットとして保存する内容は、以下のURLに記載されています。
http://solid.kmckk.com/doc/skit/current/os/cs/snapshot.html#solid-snapshot-attr
今回はお試しとして、SOLID_SNAPSHOT_ATTR_DEBUG属性を選択しました。
この属性を選択すると、SOLID_CORE, SOLID_HEAP, OSSTACKも保存されるので、後から例外発生時の状況をデバッガで再現することができます(!)
ちなみに、SOLID_SNAPSHOT_AddUserArea(SOLID_ADDRESS addr, size_t size)を使用すれば、スナップショットに保存したい領域を指定することもできます。
「このバッファがどうなっていたか知りたいからスナップショットしてほしい!」
という使い方など、可能です。
以下3つの要実装関数があります。
int IMPL_SNAPSHOT_Open():
スナップショット機能の最初に呼び出される
int IMPL_SNAPSHOT_Write(const void *pData, size_t size):
スナップショットの内容が通知される
int IMPL_SNAPSHOT_Close():
スナップショット機能の最後に呼び出される
必要であれば実装することができます。
今回は、スナップショットのデータ内容を、先ほど保存領域として設定した場所に書き込んでいくことにします。
ですので、IMPL_SNAPSHOT_Open()関数は不要なので、空の関数として実装しておきます。
IMPL_SNAPSHOT_Write()関数には、先ほど保存領域として設定した場所に書きこむコードを実装します。
IMPL_SNAPSHOT_Close()には、保存した領域の最終アドレスをシリアルターミナルに出力するようにしてみます。
int IMPL_SNAPSHOT_Open(){
return SOLID_ERR_OK;
}
SOLID_ADDRESS write_addr = 0x20000000;
int IMPL_SNAPSHOT_Write(const void *pData, size_t size){
if ((write_addr + size) > 0x24000000) return SOLID_ERR_NOMEM;
memcpy((void *)write_addr, pData, size);
write_addr += size;
return SOLID_ERR_OK;
}
int IMPL_SNAPSHOT_Close(){
syslog(LOG_INFO, "SanpShot from 0x20000000 to 0x%x", (write_addr-1));
return SOLID_ERR_OK;
}
では、データアボート例外を発生させ、例外ベクタ関数の最後でブレークしてみましょう。
保存先RAMの中身を、下部に表示してみました。
データが保存されていますね。
シリアルターミナルを見てみます。
スナップショット保存領域が表示されました。
この内容を、ファイルシステムに変換してPCからバイナリエディタで見ることによって、何が発生したのかの解析の手がかりにできます。
さらに、今回はRAM保存にしましたが、システムにSDカードがあるのであればそちらにファイルシステムとして保存すれば簡単に持ち運びできたりもしますね。
では、RAMに保存した内容を見てみましょう。
先程のURLにある後半部分です。
http://solid.kmckk.com/doc/skit/current/user_guide/crashdump_debug.html
なお、今回は、スナップショットデータを(リセットで消えてしまう)RAMに保存しています。
このため、RAMデータが消えないうちにデータをリードしてファイル化をしたいので、PARTNER-JetのIDEを使用してプログラム実行を行います。
また「プログラムを実行する」という。先ほどと同じことを行いますが、お付き合いください。。。
RAMではなく、あらかじめSDカードにファイルとして保存するなど、消えない領域に保存すれば、以降のようにPARTNER-Jetを使う必要はありません。IMPL_SNAPSHOT_Write()関数を工夫すればそのようにできますね。
ファイルさえできれば、4.3章の「スナップショット内容を表示」で、スナップショット内容を見てみる事ができます。
ICE本体であるPARTNER-Jetを使います。
このため、PARTNER-Lancharを起動します。
上記の部分にある、[PERTNER Launcher]をクリックします。
すると、PERTNERデバッガが起動されます。
プログラムを実行します。
①プログラム実行準備をします。
まず、プログラムをダウンロードできるように。MMU等の初期化を行う必要があります。
⇒プログラムを一旦走らせ、適当に強制ブレークすればOKです。
プログラムをダウンロードします。
⇒[ファイル]⇒[ロード]メニューから、outファイルを選択すればOKです。
②ブレークポイントを設定します。
③実行すると、一旦Root_task()先頭でブレークしますが、再実行すればブレークポイントで停止します。
シリアルターミナルログには、再程と同じく以下のように表示されています。
ダンプ保存エリアは0x20000000 - 0x21105f40となりました。
PARTNER-JetのWRコマンドにより、バイナリをファイルに吸い上げて保存することが可能です。
保存するファイルの拡張子は、.sdmpがお勧めです。
SOLID-IDEか、クラッシュダンプだな、認識してくれる拡張子だからです。
コマンドは以下です。
WR ファイル名,アドレスS,アドレスE[,/アクセスポート]
保存できたスナップショットファイル (.sdmp) はこれです。
保存ができました!
先程保存したスナップショットファイル (.sdmp) 、SOLID-IDEで表示してみましょう。
スナップショットファイル (.sdmp) を、SOLID-IDEにをドラッグ&ドロップするだけでOKです。
スナップショット内容が表示されました!
ここで、CFGディレクトリとシンボルファイルのパスを入力し、「デバッグの開始」を押してみます。
すると、いろいろ情報が表示されました!
これらすべて、スナップショットファイル (.sdmp) に保存されている情報です。
例外が発生した場所が一目瞭然ですね!
前回に引き続き、例外が発生した場合のハンドリングについて見てきました。
今回の焦点であるクラッシュダンプは、テスト・デバッグが終わり運用段階になった際の、フェールセーフの一つです。
ここをしっかりと設計しておかないと、いざという時に途方にくれてしまいかねません。
SOLID-OSの機能はいろいろありますが、特に今回の機能は、組み込みシステムのフェールセーフ設計における一つの方法を提案してくれているように感じました。
その他にも、まだまだSOLIDには便利な機能があります。
次回も、どんどんご紹介していく予定です。