
WinDbgでメモリダンプと停止したライブプロセスをPrologを使って分析します。
永続的な SWI-Prolog ワーカーを備えたネイティブ x64 および ARM64 用 WinDbg 拡張機能です。ユーザーモードダンプまたは停止したネイティブ x64 または ARM64 プロセスから、スレッド、スタックフレーム、シンボル、ロード済みモジュール、およびメモリを公開します。独自の Prolog ファイルをロードし、デバッガーのコマンドウィンドウからクエリを実行できます。
バージョン: 0.2.0(実験的、ARM64 ネイティブ対応)。
デバッガーのアーキテクチャに合わせてビルドされた DLL を使用してください。デバッガー、DLL、およびネイティブターゲットは一致している必要があります。x64/AMD64 同士、または ARM64 同士のいずれかです。クロスアーキテクチャのスコープコンテキスト、WOW64、ARM32、および ARM64EC ターゲットは、このリリースの対象外です。ネイティブ x64 および ARM64 ビルドは、99 件のポータブルチェックすべてと CDB のダンプ/ライブスモークテストに合格しました。実行内容、ログ、および残りの手動チェックについては docs/VALIDATION.md を参照してください。
ソースリポジトリにはコンパイル済みバイナリは含まれていません。以下の Windows 向け手順でビルドして、dist/x64/ または dist/arm64/ を生成してください。リリースアーカイブは PrologWinDbg-v0.2.0-win-x64.zip および PrologWinDbg-v0.2.0-win-arm64.zip という名前で、それぞれ選択したバイナリを dist/ に配置します。以下の例では、このリリースアーカイブのレイアウトを使用しています。ソースからビルドする場合は、パス内の dist/arm64 または dist/x64 を置き換えてください。
SWI-Prolog の Windows コンソール版をインストールします。 ネイティブ ARM64 インストーラーは 開発版ダウンロード から入手できます。 CI マトリックスでは、両方のアーキテクチャでネイティブ SWI-Prolog 10.1.18-1 を使用しています。SWI は 別プロセスとして実行され、DLL にリンクされたりバンドルされたりすることはありません。
dist/prolog.dll を dist/prolog および dist/examples ディレクトリと一緒に保持してください。
一致する x64 または ARM64 の WinDbg でネイティブのユーザーモードダンプを開くか、そのアーキテクチャのネイティブプロセスにアタッチしてブレークします。再現可能なダンプを作成するには、PowerShell で以下を実行します:
.\dist\prologdbg_target.exe --dump C:\Temp\prologdbg-sample.dmp
WinDbg で、これらのパスを実際のプロジェクトディレクトリに置き換えてください。引用符で囲んだパスには スラッシュを使用してください。デバッガーはバックスラッシュをエスケープとして扱います。
.load C:/PrologWinDbg/dist/prolog.dll
!prolog.runtime "C:/Program Files/swipl/bin/swipl.exe"
!prolog.info
!prolog.consult "C:/PrologWinDbg/dist/examples/patterns.pl"
!prolog.query wd:current_snapshot(S), wd:architecture(S,Architecture).
!prolog.query wd:current_snapshot(S), wd:thread_id(S,T,WindowsTid).
!prolog.query wd:current_snapshot(S), wd:stack_frame(S,T,Depth,IP).
!prolog.query wd:current_snapshot(S), patterns:stack_contains(S,T,Module,Function).
拡張機能は、明示的なランタイムパス、PROLOGDBG_SWIPL 環境変数、または標準の Program Files\swipl\bin\swipl.exe インストールを使用します。
初回必要時にワーカーを起動します。すべての通信はローカルパイプを使用します。
提供されたターゲットについて、既知の8バイトマーカーを検査します:
!prolog.query wd:current_snapshot(S), wd:lookup(S,'prologdbg_target!g_magic',ok(A)), patterns:inspect_magic(S,A,Verdict).
期待される判定結果: matched(prologdb, Address)。マーカーバイトは
PROLOGDB を綴ります。ターゲットはこれらの名前をエクスポートするため、この例では
PDB は不要です。プライベート PDB は他の関数やアプリケーションに役立ちます。
WinDbg で prologdbg_target.exe --live を起動します。初期の
ローダーブレークポイントを通過して続行します。その後、2 つのワーカー
スレッドが開始された後にターゲットは CapturePoint でブレークします。ダンプの場合と同じクエリを使用します。
あるいは、prologdbg_target.exe --wait を実行し、出力された PID にアタッチして
ブレークインします。このモードは、途中で停止しない場合、10 分後に終了します。
実行を制御するには、通常の WinDbg の bp、g、p、t コマンドを使用します。各
!prolog.query は新しいスナップショットを収集します。登録済みの Prolog ブレークポイント
アクションと自動継続は、今後のバージョンで計画されています。
| コマンド | 目的 |
|---|---|
!prolog.help | コマンドのヘルプを表示 |
!prolog.runtime "absolute path to swipl.exe" | ランタイムを選択し、既存のワーカーを停止 |
!prolog.info | ワーカーを起動し、そのバージョンを報告 |
!prolog.consult "absolute path to rules.pl" | Prolog ソースをロードまたは再ロード |
!prolog.query Goal. | 証拠を収集し、解を列挙 |
!prolog.queryfile "absolute path to goal.txt" | ファイルから単一の UTF-8 ゴールを読み取り、クエリを実行 |
!prolog.eval Goal. | デバッガの証拠なしで Prolog を評価 |
!prolog.limits | 現在の制限を表示 |
!prolog.limits 5000 100 128 10000000 | ミリ秒、回答数、スレッドあたりのフレーム数、推論制限を設定 |
!prolog.reset | ワーカーを停止; ロード済みのルールとカスタムランタイム状態は失われる |
単純なゴールはコマンドの直後に渡します。標準の Prolog のシングル
クォートはアトムを区切ります。末尾のピリオドは省略できます。1 コマンドにつき 1 つのゴールを使用し、
サブゴールはカンマで結合します。Prolog の論理和 (;) の場合は、WinDbg のコマンド区切り文字から保護するために、
ゴール全体をダブルクォートで囲みます:
!prolog.eval "(X=a;X=b), writeln(X)."
複雑な引用、Unicode テキスト、または複数行のゴールについては、ゴールを UTF-8 のテキストファイルに記述し、!prolog.queryfile "C:/rules/goal.txt" を使用してください。その内容は WinDbg のコマンドテキスト解析をバイパスして、直接 Prolog に渡されます。ファイルサイズの上限は 1 MiB です。再利用可能なロジックについては、consult した .pl ファイル内で述語を定義してください。
ターゲットを指定しない例:
!prolog.eval member(X,[1,2,3]), Y is X*X.
回答は bindings([Name=Value, ...], ResidualGoals) として出力されます。2 番目の
リストは残余制約を保持します。最終ステータスは、探索の枯渇、失敗、回答数の上限、推論の上限、タイムアウト、例外を区別します。
回答数の上限に達したことは、それ以上の解が存在することを主張するものではありません。
ファイルを参照すると、通常の Prolog のロードと初期化が実行されます。ロードエラーは 報告されますが、Prolog のロードはトランザクションではありません。エラーが発生する前に一部の節が ロードされている可能性があります。ファイルを修正して再ロードするか、ワーカーをリセットしてください。
:- module(my_rules, [in_function/4]).
:- use_module('C:/PrologWinDbg/dist/prolog/wd.pl').
in_function(S, T, Module, Function) :-
wd:stack_frame(S, T, _, IP),
wd:symbol(S, IP, Module, Function, _).
次に:
!prolog.consult "C:/rules/my_rules.pl"
!prolog.query wd:current_snapshot(S), my_rules:in_function(S,T,myapp,'ProcessRequest').
述語モードとエビデンスのセマンティクスについては docs/API.md を参照してください。初期ルールは、スタックマッチング、スレッド間で共有される関数、明示的なスタックエビデンス、読み取り不能なアドレス、および結論が出ない結果を伴うメモリシグネチャ診断を提供します。
デフォルトのクエリ予算は5秒、100件の回答、スレッドあたり128フレーム、1000万回の推論です。スナップショット収集には、同じサイズの独自の時間予算が割り当てられます。初期シンボルの読み込みは遅くなる場合があります。シンボルキャッシュをウォームアップするか、!prolog.limits で最初の値を引き上げてください。
メモリ要求は1回あたり最大64 KiB、クエリあたり合計16 MiBです。スナップショットのキャプチャは、4,096スレッド、4,096個のロード済みモジュール、8 MiBのファクトテキストに制限されます。ワーカーは512 MiBのPrologスタック制限と768 MiBのWindowsプロセスメモリ制限を使用します。ネイティブブリッジは、出力、RPC数、IPC待機時間も制限します。
コマンドを中断するには、WinDbgのBreakアクション / Ctrl+Breakを使用してください。ワーカーの待機中またはスナップショット項目の走査中、拡張機能は GetInterrupt をチェックします。中断されたIPCはワーカーをリセットします。その後、ルールを再度確認してください。通常のProlog時間/推論制限はワーカーを保持します。!prolog.reset コマンドは、現在のコマンドが戻った後に使用できます。これは同時実行のキャンセルコマンドではありません。
個々のDbgEng呼び出し、特にシンボルの読み込みとリモート読み取りは同期的です。拡張機能は呼び出し間の経過時間と中断をチェックしますが、DbgEng自体がブロックされている間はハードデッドラインを保証できません。
手動中断チェック:
!prolog.limits 600000 100 128 1000000000
!prolog.eval sleep(300).
Ctrl+Break を押します。プロンプトが戻ることを確認してから、!prolog.info と
!prolog.eval X is 2+2. を実行します。その後、通常の制限に戻します。
Visual Studio 2022(C++ によるデスクトップ開発)、CMake、および
Debugging Tools for Windows を含む Windows SDK をインストールします。ARM64 の場合は、Visual
Studio C++ ARM64 ビルドツールをインストールします。すべてのポータブルテストを実行するには、
SWI-Prolog と Python 3 が必要です。swipl.exe と python.exe を PATH に追加します。
Visual Studio Developer PowerShell から:
.\scripts\build-windows.ps1 -Architecture ARM64
.\scripts\smoke-windows.ps1 -Architecture ARM64
x64 の場合は -Architecture x64 を使用します。省略すると、エミュレートされた PowerShell プロセスからであっても、ネイティブの Windows ホストアーキテクチャが選択されます。ビルドディレクトリとパッケージは別々です(build-windows-arm64、dist/arm64、および x64 の対応物)。x64 ホストは ARM64 をクロスコンパイルできますが、ネイティブ C++ テストはスキップされます。ARM64 Windows 上でスモークテストと CTest を実行してください。
デバッガ SDK が別の場所にある場合は、-DebuggingTools を渡します。これには sdk\inc\dbgeng.h(または inc\dbgeng.h)が含まれている必要があります。コンパイラはその Windows SDK インクルードパスも使用できます。CDB または SWI-Prolog が別の場所にある場合は、-Cdb と -Swipl を渡します。スモークスクリプトは Debuggers/arm64/cdb.exe または Debuggers/x64/cdb.exe を選択し、起動前に CDB、DLL、およびテストターゲットの PE アーキテクチャを検証します。展開済みのリリースアーカイブをテストするには、-Dist .\dist を渡します。MSVC ビルドは C++ ランタイムを静的にリンクし、テストターゲットの PDB を含めます。
スモークスクリプトは、新しい一時ディレクトリに独自のターゲットプロセス、フルダンプ、デバッガコマンドファイル、およびログを作成します。ダンプセッションとライブセッションの両方、スレッド/スコープの復元、メモリ証拠、タイムアウトからの復旧、ワーカーのリセット、および DLL のアンロード/再ロードをテストします。ログとダンプを保持し、そのディレクトリを出力します。既存の無関係なプロセスにはアタッチしません。
cmake -S . -B build
cmake --build build
ctest --test-dir build --output-on-failure
オプションの MinGW クロスビルドでは、cmake/mingw-x64.cmake と、DEBUGGING_TOOLS_ROOT を通じて別途提供される最新の Microsoft DbgEng ヘッダーを使用します。MinGW に同梱されているヘッダーは、ここで使用されているワイド文字インターフェースには古すぎます。小さな dbgeng_compat.hpp ラッパーは、Microsoft のヘッダーやインターフェースレイアウトを変更することなく、欠落している SAL 注釈を処理します。
cmake -S . -B build-cross -G Ninja \
-DCMAKE_TOOLCHAIN_FILE=cmake/mingw-x64.cmake \
-DDEBUGGING_TOOLS_ROOT=/path/to/debugger-sdk
cmake --build build-cross
cmake --install build-cross --prefix dist