次の方法で共有


Linux のライブ リモート プロセス デバッグ

この記事では、Linux へのライブ WinDbg 接続を確立する方法について説明します。 Linux でのライブ リモート プロセス デバッグには、WinDbg バージョン 1.2402.24001.0 以降が必要です。

Linux では、WinDbg 接続をサポートするために GNU デバッガ - GDBServer が使用されます。 GDBServer の詳細については、「 https://en.wikipedia.org/wiki/Gdbserver」を参照してください。 リモート gdb デバッグのドキュメントを表示する 1 つの場所は次のとおりです。 https://sourceware.org/gdb/current/onlinedocs/gdb#Remote-Debugging

この例では Linux 用 Windows サブシステム (WSL) を使用していますが、他の Linux 実装も使用できます。

WinDbg の種類のリモート プロセス デバッグ

WinDbg でリモート デバッグを実行するには プロセス サーバー または KD 接続サーバー の 2 つの主な方法があります。 プロセス サーバーは、ユーザー モードのデバッグに使用されます。KD 接続サーバーは、カーネル モードのデバッグに使用されます。 これらの WinDbg 接続の種類に関する一般的な情報については、 プロセス サーバー (ユーザー モード)KD 接続サーバー (カーネル モード) を参照してください。

Linux ユーザー モード プロセスのデバッグを開始するには、2 つの方法があります。 特定のプロセスで gdbserver を起動することも、既存のプロセスを一覧表示してアタッチできる プロセス サーバー として gdbserver を起動することもできます。 これは、Windows 上の DbgSrv (dbgsrv.exe) プロセス サーバーとよく似ています。 詳細については プロセスサーバーをアクティブ化を参照してください。

ユーザー モードの Linux プロセスのデバッグ

特定のシングル ユーザー モード プロセスまたはマルチモードに接続して、すべてのプロセスを一覧に表示し、接続先のプロセスを選択することができます。 このトピックでは両方の方法について説明します。 どちらのメソッドも、次に説明する同じ接続文字列構文を共有します。

gdbserver 接続文字列の形式

gdbserver への接続に使用される形式は "protocol:arguments" で、引数は "argument=value" のコンマ区切りのリストです。 ユーザー モードの gdbserver 接続の場合、プロトコルは gdb で、引数のセットは次のようになります。

server=<address> - 必須: 接続先の gdbserver の IP アドレスを示します。

port=<port> - 必須: 接続先の gdbserver のポート番号を示します。

threadEvents=<true|false> - 省略可能: このバージョンの gdbserver のスレッド イベントが停止モードで正しく動作するかどうかを示します。

現在の gdbserver リリースでは、停止モードのサーバー (WinDbg が使用する) でスレッド イベントを有効にすると、gdbserver がクラッシュする問題があります。 この値が false (既定値) の場合、スレッドの開始イベントと停止イベントは合成されますが、スレッドの作成/破棄の実際の時間よりも大幅に遅く表示されることがあります。 この問題の修正プログラムが gdbserver で使用できる場合は、このオプションを使用して実際のイベントを有効にすることができます。

シングル ユーザー モード プロセスへの接続

このセクションでは、WinDbg を使用して、Linux でシングル ユーザー モード プロセスを識別して接続する方法について説明します。

WSL (Linux 用 Windows サブシステム)

ここの例では WSL (Linux 用 Windows サブシステム) を使用していますが、他の Linux 実装も使用できます。 WSL の設定と使用については、次を参照してください。

希望のプロセスを選択してください

コマンドを使用して Linux のプロセスを ps -A 一覧表示し、接続する実行中のプロセスを決定します。

user1@USER1:/mnt/c/Users/USER1$ ps -A
    PID TTY          TIME CMD
    458 pts/1    00:00:00 bash
    460 ?        00:00:00 rtkit-daemon
    470 ?        00:00:00 dbus-daemon
    482 ?        00:00:19 python3
   1076 ?        00:00:00 packagekitd
   1196 pts/0    00:00:00 ps

この例のチュートリアルでは、python3 に接続します。

ターゲット システムの IP アドレスを見つける

リモート Linux ターゲットに接続する場合は、外部 IP アドレスを特定するなどの ip route showコマンドを使用します。

user1@USER1:/mnt/c/Users/USER1$ ip route show
default via 192.168.1.1 dev enp3s0 proto dhcp metric 100
172.25.144.0/24 dev enp3s0 proto kernel scope link src 192.168.1.107 metric 100

このチュートリアルでは、同じ PC で実行されている WSL に接続し localhost の IP アドレスを使用します。

選択したプロセスに GDBServer をアタッチする

WSL Linux コンソールで、ポート 1234 で gdbserver を開始するために gdbserver localhost:1234 python3 と入力し、python3 プロセスに接続します。

USER1@USER1:/mnt/c/Users/USER1$ gdbserver localhost:1234 python3
Process python3 created; pid = 1211
Listening on port 1234

一部の Linux 環境では、コマンドを管理者として実行する必要がある場合があります 。たとえば、sudo - sudo gdbserver localhost:1234 python3を使用します。 デバッガー管理者のルート レベルのアクセスを有効にする場合は注意が必要であり、必要な場合にのみ使用してください。

WinDbg でプロセス サーバー接続を作成する

WinDbg を開き、[ファイル]、[リモート デバッガーへの接続] の順に選択し、接続のプロトコル文字列を入力します。 この例では、 gdb:server=localhost,port=1234を使用します。

接続文字列を示す WinDbg のデバッグ開始画面のスクリーン ショット。

[OK] ボタンをクリックすると、デバッガーが gdbserver に接続され、最初のプロセスの開始が中断されます。

最初のブレークポイントに到達したら、'g' を数回押すことができます。 モジュールの読み込みメッセージが表示されます (sxe スタイルの "モジュールの読み込み時に中断" イベントが正常に動作します)。

デバッグ シンボルがキャッシュに読み込まれるため、その時点に到達するまでに少し時間がかかる場合があることに注意してください。 シンボル サーバーまたはローカル検索パスを介してシンボルとバイナリを検索することに加えて、GDBServer 統合には、これらのファイルが symsrv またはローカルで見つからない場合にリモート ファイルシステムからプルする機能があります。 これは通常、symsrv またはローカル検索パスからシンボルを取得するよりもはるかに遅い操作ですが、適切なシンボルを検索することで全体的なエクスペリエンスが向上します。

スタックを一覧表示するには、k stacks コマンドを使用します。 Python3 モジュールが表示されるため、WinDbg を使用して Linux で python3 をデバッグしていることを確認できます。

0:000> k
 # Child-SP          RetAddr               Call Site
00 00007fff`ffffce10 00007fff`f786d515     libc_so!_select+0xbd
01 00007fff`ffffce80 00005555`55601ce8     readline_cpython_310_x86_64_linux_gnu!PyInit_readline+0xac5
02 00007fff`ffffcf60 00005555`556f06a1     python3!PyOS_Readline+0x109
03 00007fff`ffffcfa0 00005555`556eee7e     python3!PyFrame_LocalsToFast+0x62a1
04 00007fff`ffffd000 00005555`556edcf0     python3!PyFrame_LocalsToFast+0x4a7e
05 00007fff`ffffdb80 00005555`557a18e9     python3!PyFrame_LocalsToFast+0x38f0
06 00007fff`ffffdc00 00005555`557a1470     python3!PyCodec_LookupError+0xb09
07 00007fff`ffffdc50 00005555`557b89dc     python3!PyCodec_LookupError+0x690
08 00007fff`ffffdc70 00005555`5560b42f     python3!PyUnicode_Tailmatch+0xc6c
09 00007fff`ffffdcb0 00005555`5560b012     python3!PyRun_InteractiveLoopObject+0x4e0
0a 00007fff`ffffdd50 00005555`557b7678     python3!PyRun_InteractiveLoopObject+0xc3
0b 00007fff`ffffdda0 00005555`555f55c8     python3!PyRun_AnyFileObject+0x68
0c 00007fff`ffffddd0 00005555`555ea6e8     python3!PyRun_AnyFileExFlags+0x4f
0d 00007fff`ffffde00 00005555`55780cad     python3!Py_str_to_int+0x2342a
0e 00007fff`ffffdef0 00007fff`f7c7cd90     python3!Py_BytesMain+0x2d
0f 00007fff`ffffdf20 00007fff`f7c7ce40     libc_so!_libc_init_first+0x90
10 00007fff`ffffdfc0 00005555`55780ba5     libc_so!_libc_start_main+0x80
11 00007fff`ffffe010 ffffffff`ffffffff     python3!start+0x25
12 00007fff`ffffe018 00000000`00000000     0xffffffff`ffffffff

この時点で、リモート プロセス サーバー経由でリモート Windows デバッガーにアタッチされた WinDbg を使用して実行できるほぼすべての操作を実行できる必要があります。 ステップ、ソース レベルのデバッグ、ブレークポイントの設定、ローカルの検査などを行うことができます。

デバッグが完了したら、CTRL + D キーを押して WSL の gbdserver ウィンドウを終了します。

プロセスサーバーへの接続

ユーザー モードの GDBServer を使用して 1 つのプロセスに接続するだけでなく、プロセス サーバーとして 1 つを設定し、システム上の既存のプロセスを一覧表示してアタッチすることもできます。 これを行うには、gdbserver が "--multi" コマンド ライン引数で開始されます- gdbserver --multi localhost:1234

user1@USER1:/mnt/c/Users/USER1$ sudo gdbserver --multi localhost:1234
Listening on port 1234

プロセス サーバーに接続するには、WinDbg で [ファイル] または [プロセス サーバーに接続] を選択し、上記の単一プロセス gdbserver の例と同じプロトコル文字列を入力します。

gdb:server=localhost,port=1234

[OK] ボタンをクリックすると、プロセス サーバーとして gdbserver に接続されます。 dbgsrv と同様に、新しいプロセスを生成することも、既存のプロセスを一覧表示してアタッチすることもできます。

この例では、「プロセスにアタッチ」オプションを使用します。

20 位のプロセスが一覧されているプロセスの接続を示す WinDbg の [デバッグの開始] 画面のスクリーン ショット。

Windows プロセス (PID、ユーザー、コマンド ラインを含む) で表示されるのと同じものの多くが表示されることに注意してください。 [プロセスにアタッチ] ダイアログの一部の列は Linux には関係なく、データは含まれません。

セッションの終了

CTRL + D キーを押して WSL の gbdserver ウィンドウを終了し、[WinDbg でのデバッグの停止] を選択します。 セッションを終了するには、場合によってはデバッガーを終了する必要があります。

プロセス サーバーへの再接続

WinDbg は、gdbserver がプロセスにアタッチされているかどうかによって、"プロセス サーバー" と "単一ターゲット" を認識します。 何らかのプロセスにアタッチし、フリーズしたままにし、デバッガーを閉じて、プロセス サーバーに再接続しようとすると、プロセス サーバーとして認識されない可能性があります。 このような場合は、ターゲット gdbserver を再起動し、デバッガーを再接続します。

Linux WinDbg の機能

デバッガーの機能の多くは、コア ダンプ (スタック ウォーク、シンボル、型情報、ローカル変数、逆アセンブルなど) のデバッグで期待どおりに動作しますが、デバッグ ツールチェーン全体がまだ ELF、DWARF、および Windows セマンティクスとの結果の違いを認識していない点に注意することが重要です。 デバッガーの一部のコマンドでは、現在、予期しない出力が発生する可能性があります。 たとえば、 lm ELF モジュールの正しくない情報は引き続き表示されます。PE ヘッダーは想定どおりに手動で解析されます。

EXDI 経由の Linux カーネル モード

Windows デバッガーでは、EXDI を使用したカーネル デバッグがサポートされています。 これにより、さまざまなハードウェアとオペレーティング システムをデバッグできます。 EXDI 接続の構成およびトラブルシューティングのセットアップに関する一般的な情報については 「EXDI デバッガー トランスポートの構成」を参照してください。

EXDI を使用して QEMU カーネル モード デバッグを設定する方法については、「 EXDI を使用した QEMU カーネル モード デバッグの設定」を参照してください。

Linux のシンボルとソース

このセクションでは、Linux シンボルの基本的な使用方法と可用性について説明します。 詳細については、「 Linux のシンボルとソース 」および「 ソース コード拡張アクセス」を参照してください。

DebugInfoD シンボル サーバー

WinDbg バージョン 1.2104 以降、ソース パス コマンド (.srcpath、.lsrcpath (ソース パスの設定)) は DebugInfoD* タグを介した DebugInfoD サーバー からのファイルの取得をサポートします。

DebugInfoD* タグは 1 つ以上の DebugInfoD サーバーを指すことができ、各サーバー URL は https://domain.com でフォーマットされ、 *で区切られます。 サーバーはソース パスにリストされているのと同じ順序で検索され、最初に一致した URL からファイルが取得されます。 詳細については、「 ソース コードの拡張アクセス」を参照してください。

たとえば .sympath (シンボル パスの設定) コマンドを使用して、次のように DebugInfoD パスを設定できます。

.sympath+ DebugInfoD*https://debuginfod.elfutils.org

シンボル パスの設定に関する一般的な情報については、「 シンボルの使用」を参照してください。

読み込まれているシンボルに関する情報を表示するには、次を使用します !sym noisy。 詳細については !symを参照してください。

また、その成果物の種類の返しをサポートする DebugInfoD サーバーからのソースの自動ダウンロードもサポートされています。 基本的には次のことができます。

.srcpath+ DebugInfoD*https://debuginfod.elfutils.org

DWARF シンボルと Linux シンボル ユーティリティの操作の詳細については、Linux シンボル !sourcemap!diesymソース を参照してください

C++ アプリのチュートリアル

  1. テキスト エディター (nano や vi など) を使用して C++ ファイルを作成します。 次に例を示します。

nano DisplayGreeting.cpp

  1. テキスト エディターで、C++ プログラムを記述します。 デバッグする必要があるあいさつを表示する簡単なプログラムを次に示します。
#include <array>
#include <cwchar>
#include <cstdio>
#include <iostream>
using namespace std;

void GetCppConGreeting(wchar_t* buffer, size_t size)
{
    wchar_t const* const message = L"HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL DEBUGGING!";
    wcsncpy(buffer, message, size);
}

int main()
{
    std::array<wchar_t, 50> greeting{};
    GetCppConGreeting(greeting.data(), greeting.size());

    cin.get();
    wprintf(L"%ls\n", greeting.data());

    return 0;
}
  1. nanoエディタを保存 (CTRL-O) し、終了 (CTRL-X) します。

  2. g++ を使用して C++ ファイルをコンパイルします。 o オプションを使用して出力ファイル名を指定し、-g オプションを指定するとシンボル ファイルが生成されます。

g++ DisplayGreeting.cpp -g -o DisplayGreeting

  1. コードにエラーがない場合、g++ コマンドは DisplayGreeting という名前の実行可能ファイルをディレクトリに作成します。

  2. 次のコマンドを使用してプログラムを実行できます。

./DisplayGreeting

  1. リターン キーを押すと、アプリにメッセージが表示されます。 出力を見ると、Greeting が切り捨てられ、代わりに "????" が表示されているようです。

HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YO????

DisplayGreeting のデバッグ

  1. コードを実行する準備ができたら、gdbserver を使用してアプリを開始できます。

gdbserver localhost:1234 DisplayGreeting

  1. WinDbg を開き、[ファイル]、[リモート デバッガーへの接続] の順に選択し、接続のプロトコル文字列を入力します。 この例では、 gdb:server=localhost,port=1234を使用します。

  2. 接続されると、出力はポート 1234 でリッスンしており、リモート デバッグ接続が確立されていることを示す必要があります。

Bob@Bob6:/mnt/c/Users/bob$ gdbserver localhost:1234 DisplayGreeting
Process /mnt/c/Users/bob/DisplayGreeting created; pid = 725
Listening on port 1234
Remote debugging from host 127.0.0.1, port 47700

既にメンションしたように、一部の Linux 環境では、コマンドを管理者として実行する必要があります。通常は sudo を使用します。 デバッガー管理者のルート レベルのアクセスを有効にする場合は注意が必要であり、必要な場合にのみ使用してください。

デバッガー セッションにソース パスとシンボル パスを追加する

ブレークポイントを設定し、ソース コードと変数を表示するには、シンボルとソース パスを設定します。 シンボル パスの設定に関する一般的な情報については、「 シンボルの使用」を参照してください。

デバッガー セッションにシンボル パスを追加するために使用 .sympath します。 この例では、Bob という名前のユーザーに対して、WSL Linux Ubuntu のこの場所でコードが実行されています。

\\wsl$\Ubuntu\mnt\c\Users\Bob\

WSL では、このディレクトリは次の Windows OS の場所にマップされます。 C:\Users\Bob\

そのため、これら 2 つのコマンドが使用されます。

.sympath C:\Users\Bob\

.srcpath C:\Users\Bob\

WSLファイル システムの詳細については、「 WSLのファイルのアクセス許可」を参照してください。

  1. 追加の Linux OS シンボルを利用するには、次のように .sympath の場所を使用して DebugInfoD シンボルを追加します。

.sympath+ DebugInfoD*https://debuginfod.elfutils.org

  1. また、その成果物の種類の返しをサポートする DebugInfoD サーバーからのソースの自動ダウンロードもサポートされています。 これを利用するには、.srcpath を使用して elfutils サーバーを追加します。

.srcpath+ DebugInfoD*https://debuginfod.elfutils.org

ブレークポイントの設定

DisplayGreeting アプリのメインにブレークポイントを設定します。

0:000> bp DisplayGreeting!main
0:000> bl
     0 e Disable Clear  00005555`55555225  [/mnt/c/Users/bob/DisplayGreeting.cpp @ 14]     0001 (0001)  0:**** DisplayGreeting!main

Go コマンドまたはメニュー オプションを使用して、コードの実行を再開します。

ソースコードの読み込み

.reloadコマンドを使用して、シンボルを再読み込みします。

コマンドを lm 使用して、DisplayGreeting アプリが実行されていることを確認します。

0:000> lm
start             end                 module name
00005555`55554000 00005555`55558140   DisplayGreeting T (service symbols: DWARF Private Symbols)        c:\users\bob\DisplayGreeting
00007fff`f7a54000 00007fff`f7a732e8   libgcc_s_so   (deferred)             
00007fff`f7a74000 00007fff`f7b5a108   libm_so    (deferred)             
00007fff`f7b5b000 00007fff`f7d82e50   libc_so  T (service symbols: DWARF Private Symbols)        C:\ProgramData\Dbg\sym\_.debug\elf-buildid-sym-a43bfc8428df6623cd498c9c0caeb91aec9be4f9\_.debug
00007fff`f7d83000 00007fff`f7fae8c0   libstdc___so   (deferred)             
00007fff`f7fc1000 00007fff`f7fc1000   linux_vdso_so   (deferred)             
00007fff`f7fc3000 00007fff`f7ffe2d8   ld_linux_x86_64_so T (service symbols: DWARF Private Symbols)        C:\ProgramData\Dbg\sym\_.debug\elf-buildid-sym-9718d3757f00d2366056830aae09698dbd35e32c\_.debug

コマンドが表示グリーティング コードへのアクセスをトリガーすると、WinDbg に表示されます。

19 行目の wprint にブレークポイントが設定された WinDbg の DisplayGreeting.cpp コードのスクリーン ショット

スタックをリストするには、「k」コマンドを使用します。

0:000> k
 # Child-SP          RetAddr               Call Site
00 00007fff`ffffde00 00007fff`f7b84d90     DisplayGreeting!main+0x1f [/mnt/c/Users/BOB/DisplayGreeting.cpp @ 15] 
01 00007fff`ffffdef0 00007fff`f7b84e40     libc_so!__libc_start_call_main+0x80 [./csu/../sysdeps/x86/libc-start.c @ 58] 
02 00007fff`ffffdf90 00005555`55555125     libc_so!__libc_start_main_impl+0x80 [./csu/../sysdeps/nptl/libc_start_call_main.h @ 379] 
03 00007fff`ffffdfe0 ffffffff`ffffffff     DisplayGreeting!start+0x25
04 00007fff`ffffdfe8 00000000`00000000     0xffffffff`ffffffff```

dx コマンドを使用して、ローカル変数 greeting を表示します。 サイズは 50 であることに注意してください。

0:000> dx greeting
greeting                 : { size=50 } [Type: std::array<wchar_t, 50>]
    [<Raw View>]     [Type: std::array<wchar_t, 50>]

コードを見て、50 があいさつメッセージに十分なサイズではない可能性があることに注意してください。

wchar_t const* const message = L"HELLO FROM THE WINDBG TEAM. GOOD LUCK IN ALL OF YOUR TIME TRAVEL

これを確認するには、あいさつのローカル変数を展開し、あいさつが切り捨てられていることを確認します。

gdbserver 接続のトラブルシューティング

gdbserver --debug コンソールに追加情報を表示して、接続状態に関する詳細情報を収集するオプションを使用します。 たとえば、プロセス サーバーを起動するには、次のコマンドを使用します。

gdbserver --debug --multi localhost:1234

関連項目

Linux のシンボルとソース

ソース コードの拡張アクセス

ELFUTILS debuginfod

最適なリレーショナル モデルデバッグ メソッドの選択