トレースデバッグの活用

「printfを1行追加したら症状が消えた」という経験は、組み込み開発では珍しくありません。特に、printfの出力先がUARTなどであり、書式展開や送信待ちによって実行タイミングが変わる構成では、観測コード自体が不具合の再現条件を変えてしまうことがあります。

この記事では、実行の履歴をハードウェアの支援で取り出すトレースデバッグを取り上げます。何が取れるのか、どの経路で外へ出すのか、帯域が足りないときはどう逃がすのかを整理し、既存のログ設計と役割をどう分けるかまで扱います。

なお、最初に用語を整理すると、SWO(Serial Wire Output)はITMなどが生成したトレース情報を単一の信号線でチップ外へ出力するためのインターフェース、SWV(Serial Wire Viewer)はSWO経由で取得したITM/DWTなどのトレース情報をデバッガ上で表示・解析する機能、ITM(Instrumentation Trace Macrocell)はソフトウェアイベント出力、ETM(Embedded Trace Macrocell)は命令トレース(実行経路再構成)を生成するコンポーネントです。

用語の厳密な範囲はツールや資料によって異なるため、この記事では「SWOを出力経路」「SWVをPC側の表示・解析」として扱います。ITMが生成したイベントをSWOが外部へ出力し、SWVがそれを表示する関係です。ETMの出力には、一般にTPIUを介したトレースポートや、ETB/ETF/ETRなどのトレースバッファ・メモリ出力を使用します。利用できる経路は、MCU/SoCに実装されたCoreSight構成に依存します。

この記事では、(1)ITM/SWOで低侵襲ログ、(2)ETMで実行経路再構成、(3)帯域不足時のETB/メモリ退避、の3点を整理します。

  • 記事内では、Arm Cortex-MおよびCoreSightのトレース機能を主な例として説明します。利用可能な機能、信号端子、出力経路は、採用するMCU/SoCおよびデバッグツールによって異なります。

printfデバッグが行き詰まる場面

printfは便利ですが、時間に関わる不具合とは相性がよくありません。そこで、観測の手段が対象の挙動を変える構造を先に押さえます。

出力処理そのものが時間の流れを変えてしまう

計測用のコードを入れると、実行時間だけでなくキャッシュの使われ方まで変わり、観測前の挙動と一致しなくなることがあります。一方で、出荷前に計測コードを取り除くと、それ以前に取得した計測結果の妥当性を保証できなくなります。この現象は学術的にプローブ効果(probe effect)と呼ばれ、リアルタイムシステムのタイミング解析では正面から扱われている課題です。計測コードの有無でメモリ配置が変わり、キャッシュ挙動が変化することがあります。

侵襲を伴う観測をした時点で、見ているものは元のシステムとは別物になりうるという前提に立つ必要があります。タイミング起因の不具合を追うほど、この矛盾は深刻化していくのです。

高頻度の割り込みは記録が追いつかない

もう1つの壁が帯域です。出力の帯域が事象の発生頻度に追いつかなければ、記録を取りこぼすことがあります。トレースの仕組みが複数の情報源からのデータを同時に受けたとき、出力順を調停する構造を持っているのは、まさに発生と出力の速度差を吸収するためです。

ETMは通常、全命令の命令語そのものを逐次出力する方式ではありません。分岐、例外、同期情報など実行経路の再構成に必要な情報を圧縮して出力し、デコーダ側が実行ファイルの情報と組み合わせて実行経路を再構成します。実際に出力される情報は、ETMの種類および設定に依存します。

それでも、高頻度イベントと組み合わせると帯域が問題になりやすくなります。そのため、printfの文字列出力で高頻度の割り込みを逐一記録しようとする方法には、帯域の面で根本的な制約があるのです。

割り込みの頻度や優先度がなぜ観測を難しくするのかは、「割り込み処理とデバッグの基礎」の記事で扱った内容が前提になります。

トレースで取り出せる代表的な3種類の情報

トレースは一枚岩ではありません。取れる情報の種類ごとに、コストと使いどころが変わります。

命令トレース|実行経路を後から再構成する

命令トレース(ETM)は、一部のArm Cortex-MプロセッサおよびSoCに実装されるトレース機能です。ETMの搭載有無、利用可能な出力経路、記録可能なトレース種別は、採用するコアとMCU/SoCの実装に依存します。

前述のとおり、ETMが出力するのは全命令の命令語そのものではなく、実行経路の再構成に必要な情報です。受け側は、この情報を実行ファイルの情報と組み合わせて実行経路を再構成します。

命令の履歴に限定して設計された機能であり、デコーダ側がELFファイル(シンボル情報を含む実行バイナリ)を参照して実行経路を再構成する、という分業が成り立っています。トレースの欠落がなく、対応する実行イメージおよびデバッグ情報を用意できれば、クラッシュ直前の実行経路を記録に基づいて確認できます。

ITM(ソフトウェアトレース)|比較的小さい負荷で任意のイベントを記録する

ITMでは、Stimulus Portへの書き込みによってソフトウェアイベントをトレースとして出力できます。UART(汎用非同期送受信機)のような送信待ちや送信処理を回避できる点が利点です。

ただし、printf形式の文字列を使う場合は、書式展開や数値変換の処理負荷までは自動的になくなりません。低侵襲化を重視する場合は、文字列ではなくイベントIDと固定長の数値を記録する方式が有効です。

パケットにはタイムスタンプを付与でき、命令トレースと時系列を対応づけることで、「このイベントの前後で実行経路がどうなっていたか」を確認できる構成があります。printfの置き換え先として、まず検討すべき経路です。

データ観測とプログラムカウンタサンプリング|傾向を統計としてつかむ

最後は、全体の傾向を統計として捉える情報です。

DWTの比較器を利用すると特定アドレスへのアクセスを監視でき、対応する実装では、条件に一致したデータアクセスをトレースイベントとして出力できます。また、一定間隔でプログラムカウンタをサンプリングし、実行時間がどの関数に費やされているかの分布を見る方法もあります。さらに、実行サイクル数を測定するためのカウンタも用意されています。

ただし、比較器数、監視可能なアクセス種別、データトレースおよびプログラムカウンタサンプリングの対応状況は、対象MCU/SoCの実装に依存します。

いずれも「全部を記録しない」ことで帯域を節約する設計であり、ボトルネックの見当をつける段階ではこの統計的な観測が効率的です。

出力経路の選択|単一信号線による出力と複数信号線を用いるトレースポート

取れる情報が決まっても、外へ出す経路が足りなければ意味がありません。ここが実務の分岐点になります。

単一信号線による出力に流せる情報と流せない情報

SWOによるトレース出力の経路を示す図。チップ内部のソフトウェアトレースとデータ観測は、単一信号線のSWOから出力し、ホストPCで解析できる。一方、命令トレースはSWOでは出力できず、別の経路が必要になることを示している。

Arm Cortex-Mシリーズなど、一部のプロセッサアーキテクチャに備わる単一信号線のトレース出力(SWO)は、端子1本で使える手軽な経路です。ただし、SWOは端子数が少なく帯域も低い出力方式のため、出力できるトレース種別に制約があります(詳細はMCU/SoCのCoreSight構成に依存します)。

一般的なCortex-M向けの構成では、主にITMおよびDWT由来のトレースを単一信号線で出力します。一方、ETMの命令トレースは、TPIUを介した外部トレースポート、ETB/ETFなどのオンチップバッファ、またはETRを介したシステムメモリなど、別のトレース経路を用いる構成が一般的です。

つまり、printfをそのまま置き換えられる経路と、実行経路を丸ごと追える経路は別物です。端子1本のままイベント記録と統計観測まではできても、命令レベルの履歴が必要な場合は別の経路を用意する必要があります。ここを混同すると、機材をそろえたのに目的の情報が取れないという手戻りにつながりかねません。

帯域が足りないときはオンチップのバッファへ逃がす

外部への出力が追いつかない場合の逃がし方も用意されています。トレースデータを外へ出さず、チップ内のバッファ(例:ETB)へいったん蓄えてからデバッガでゆっくり回収する構成と、システムのメモリへ書き出す構成(例:ETR)です。

ETBなどのオンチップバッファは容量が限られるため、長時間の全量記録には適しません。一方、循環バッファとして動作させ、異常検出をトリガに記録を停止することで、異常発生直前の履歴を残す用途に適します。保持できる時間は、バッファ容量、トレースの種類、発生頻度、圧縮率によって変わります。

あわせて、記録する対象を条件で絞り込んでから流す運用にすれば、限られた帯域と容量を本当に見たい事象へ集中して割り当てられます。

端子を確保できるかは基板設計の段階で決まる

複数本の信号線を使うパラレルトレースポートは帯域の面で有利ですが、その分の端子を占有します。量産基板ではデバッグ用の端子をどこまで残すかが設計の論点になりやすく、いざ不具合が出てから「トレース用の端子がない」と気付いても容易には対応できません。

デバッグやトレース用のコネクタについては、業界団体の推奨仕様が公開されています。JTAGは、IEEE 1149.1で規定されるテストアクセスおよび境界走査の標準であり、多くのデバイスではデバッグ接続にも活用されます。IEEE 1149.7は、Compact JTAG(cJTAG)として知られる拡張規格で、デバッグおよびテスト接続に必要な信号線数を抑える用途で利用されます。

デバッグ用接続の基本については「JTAG/ICEの役割とデバッグ技術」の記事で扱ったとおりですが、どこまでの観測手段を量産基板に残すかは、ソフトウェア担当者が基板設計の初期段階で声を上げるべき論点です。端子を確保しておくと、市場不具合の解析まで含めて選択肢が残ります。

トレースデバッグを日常の開発に組み込む

道具をそろえても、使う前提が決まっていないと現場では機能しません。トレースデバッグを実機のどの段階で使うかは、「エミュレータ/シミュレータ/実機の使い分け」の記事で整理した使い分けと地続きです。運用の設計まで含めて完成します。

ログ設計との役割分担をあらかじめ決める

トレースデバッグが使えるようになっても、ソフトウェアで実装するログが不要になるわけではありません。ログは出荷後も含めた事象の追跡のための手段、トレースデバッグは開発時の時間軸を含む問題の詳細な解析のための手段、という使い分けが基本になります。

非同期化やリングバッファの設計といったログ側の論点は、「組み込みシステムのログ設計と性能の最適化」の記事で扱っているとおりです。

また、計測用コードをどう扱うかは個人の裁量に任せず、コーディング規約として明文化しておくべきです。情報処理推進機構(IPA)の「【改訂版】組込みソフトウェア開発向け コーディング作法ガイド[C言語版]ESCR Ver.3.0」でも、デバッグを考慮したコーディングは規約作成の方針の1つに位置づけられています。

計測条件を残して同じ観測を再現できるようにする

最後に残るのは、観測の再現性です。出力経路の設定、絞り込みの条件、クロックの分周比といった計測条件が記録されていなければ、別の担当者が同じ観測を再現できず、解析結果の検証もできません。

実務では、出力経路やフィルタ条件などの計測設定もプロジェクトの資産として構成管理に含め、いつでも同じ条件で測定し直せる状態を保つことが、トレースデバッグを一過性の道具にしないための運用になります。

printfの限界を超える観測手段を手にした今、次に問われるのは、どこまで観測できる状態で製品を出荷するかという設計判断ではないでしょうか。

組み込みソフトの世界 トップへ戻る