BitVisor
BitVisorDay 19

BitVisorのacpi_dsdt.cのデバッグ方法

BitVisorのcore/acpi_dsdt.cのデバッグ方法についてです。

背景

以下の記事にあるように、様々な回避策が実装されているということは、問題に遭遇したたびにデバッグをしてきたということなのです。今後もまた新たなパターンに遭遇しないとも限りませんので、デバッグ方法を知っておくと役に立ちます。

BitVisorのacpi_dsdt.cに実装されている各種回避策 - Qiita
https://qiita.com/hdk_2/items/de31413c67d8a708c63f

ACPI絡みで引っかかっているかどうかの判断

たいていは以下のようなところで判断できます。

  • 起動時の... oooの表示のところで非常に時間がかかる、止まる、あるいはgetbufなどのメッセージを出してpanicする
  • CONFIG_DISABLE_SLEEP=1にしていてもACPI S3が隠蔽されない・ゲストオペレーティングシステムからsuspend-to-RAMができてしまう

DSDT/SSDTの取得

調査のためには問題が起きているコンピューターの、DSDTおよびSSDTと呼ばれるACPIのテーブルを取得する必要があります。様々な方法がありますが、手っ取り早いのはGNU/Linux環境を (インストールされていなければUSBメモリーなりLive CDなりを利用して) 立ち上げて、acpidumpコマンドを使う方法です。このコマンドはACPICAに含まれており、例えばDebianではacpica-toolsパッケージに含まれます。(以前はacpidumpパッケージに含まれていました。) 標準出力にテキスト形式で出力されますので、リダイレクトしてファイルに保存します。

# acpidump > acpidump.txt

このファイルがあれば、後は他のコンピューターでも調査ができます。同じくACPICAに含まれるacpixtractコマンドにこのファイルを与えると、DSDTおよびSSDTのバイナリーが抽出されます。

$ acpixtract acpidump.txt 

Intel ACPI Component Architecture
ACPI Binary Table Extraction Utility version 20140926-32 [Oct  1 2014]
Copyright (c) 2000 - 2014 Intel Corporation

Acpi table [DSDT] - 56857 bytes written to dsdt.dat
Acpi table [SSDT] - 439 bytes written to ssdt1.dat
Acpi table [SSDT] - 2139 bytes written to ssdt2.dat
Acpi table [SSDT] - 2545 bytes written to ssdt3.dat
Acpi table [SSDT] - 601 bytes written to ssdt4.dat
Acpi table [SSDT] - 1183 bytes written to ssdt5.dat
Acpi table [SSDT] - 1267 bytes written to ssdt6.dat
Acpi table [SSDT] - 1714 bytes written to ssdt7.dat
Acpi table [SSDT] - 771 bytes written to ssdt8.dat
Acpi table [SSDT] - 281 bytes written to ssdt9.dat

ACPIにもっと興味があるなら、-aオプションをつければ他のテーブルもすべて抽出されます。

後は、やってもやらなくても良いのですが、同じくACPICAに含まれるiaslコマンドを用いて、"disassemble"できます。

$ iasl -d *.dat

複雑なDSDT/SSDTの観察がメインですが、他のテーブルもわかりやすく出してくれるので便利です。

acpi_dsdt.cの単体デバッグ

BitVisorのcore/acpi_dsdt.cは、最近、単体でデバッグできるようになりました。中を見ると、以下のようなコメントが入っているのがわかります。

/* For debugging:
   cc -DDEBUG_APP -DDEBUG_DUMP core/acpi_dsdt.c && ./a.out < dsdt.dat */

このように、DEBUG_APPを付けてコンパイルすると、単体で動くUnix用のプログラムになります。標準入力からDSDTまたはSSDTのバイナリーを読み取り、解釈ルーチンを実行します。先ほど取得したバイナリーが役に立ちます。最後にACPI S3などのスリープステートに関する情報を出力します。DEBUG_DUMPは、デバッグ用に解釈途中の状況を出力する機能を有効にします。

 1546[45]: AML_AMLCode AML_TermList2 AML_TermObj AML_Object AML_NameSpaceModifierObj AML_DefScope AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefDevice AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefCreateBitField AML_SourceBuff AML_TermArg AML_Type2Opcode AML_MethodInvocation AML_NameString2 AML_NameSeg AML_NameChar AML_LeadNameChar \_SB_.MEM_.MEMS
 1547[4d]: AML_AMLCode AML_TermList2 AML_TermObj AML_Object AML_NameSpaceModifierObj AML_DefScope AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefDevice AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefCreateBitField AML_SourceBuff AML_TermArg AML_Type2Opcode AML_MethodInvocation AML_NameString2 AML_NameSeg AML_NameChar AML_LeadNameChar \_SB_.MEM_.MEMS
 1548[53]: AML_AMLCode AML_TermList2 AML_TermObj AML_Object AML_NameSpaceModifierObj AML_DefScope AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefDevice AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefCreateBitField AML_SourceBuff AML_TermArg AML_Type2Opcode AML_MethodInvocation AML_NameString2 AML_NameSeg AML_NameChar AML_LeadNameChar \_SB_.MEM_.MEMS
 1549[0a]: AML_AMLCode AML_TermList2 AML_TermObj AML_Object AML_NameSpaceModifierObj AML_DefScope AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefDevice AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefCreateBitField AML_SourceBuff AML_TermArg AML_Type2Opcode AML_MethodInvocation AML_TermArgList AML_TermArg AML_DataObject AML_ComputationalData AML_ByteConst AML_BytePrefix \_SB_.MEM_.MEMS
 1549[0a]: AML_AMLCode AML_TermList2 AML_TermObj AML_Object AML_NameSpaceModifierObj AML_DefScope AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefDevice AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefCreateBitField AML_BitIndex AML_TermArg AML_DataObject AML_ComputationalData AML_ByteConst AML_BytePrefix \_SB_.MEM_.MEMS
 154a[78]: AML_AMLCode AML_TermList2 AML_TermObj AML_Object AML_NameSpaceModifierObj AML_DefScope AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefDevice AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefCreateBitField AML_BitIndex AML_TermArg AML_DataObject AML_ComputationalData AML_ByteConst AML_ByteData \_SB_.MEM_.MEMS
 154a[78]: AML_AMLCode AML_TermList2 AML_TermObj AML_Object AML_NameSpaceModifierObj AML_DefScope AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefDevice AML_TermList AML_TermObj AML_Object AML_NamedObj AML_DefCreateBitField AML_SourceBuff AML_TermArg AML_Type2Opcode AML_MethodInvocation AML_TermArgList AML_TermArg AML_DataObject AML_ComputationalData AML_ByteConst AML_ByteData \_SB_.MEM_.MEMS

こんな形でずらずらと出力されます。最初の16進数はバイナリー中のバイトオフセットです。バイナリーダンプと見比べる際に役立ちます。[]の中がその位置の1バイトを16進数で表したものです。:の後ろには、内部で解釈している、AMLの内容を表します。行末のパス名は、今見ている位置がACPIの木構造の名前空間のどの名前で表される部分かを示しています。

AMLの曖昧さと、メソッド引数の数を管理していないことにより、複数の解釈ができる場合があり、同じバイト位置に複数の候補が出力されることがあります。通常は解釈が進むにつれて収束しますが、時に発散することがあり、それは時間がかかるとか止まるとかいった問題につながります。この方法で見てみると、同じバイトオフセットに数千といった候補がずらずらと出力されるようになります。

後は、きちんと動くようにプログラムをなおすだけ、というのが一番の問題ですが、デバッガーも使用できますので、何度もBitVisorの起動を繰り返すよりは遥かに簡単にデバッグできます。