共用方式為


Linux 即時遠程進程偵錯

本文說明如何建立與Linux的即時 WinDbg 連線。 Linux 上的即時遠端進程偵錯需要 WinDbg 1.2402.24001.0 版或更新版本。

GNU 調試程式 - GDBServer 用於 Linux 上,以支援 WinDbg 連線。 如需 GDBServer 的詳細資訊,請參閱 https://en.wikipedia.org/wiki/Gdbserver。 您可以在您可以在這裡檢視遠端 gdb 偵錯檔的其中一個位置 - https://sourceware.org/gdb/current/onlinedocs/gdb#Remote-Debugging

這裡的範例會使用 Windows 子系統 Linux 版 (WSL),但也可以使用其他 Linux 實作。

WinDbg 類型的遠端進程偵錯

使用 WinDbg 執行遠端偵錯有兩個主要方法 - 行程伺服器KD 連線伺服器。 進程伺服器用於使用者模式偵錯;KD 連線伺服器用於內核模式偵錯。 如需這些 WinDbg 連線類型的一般資訊,請參閱進程伺服器(使用者模式)KD 連線 ion 伺服器(核心模式)。

有兩種方式可以開始偵錯 Linux 使用者模式進程。 您可以在特定進程上啟動 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 (Windows 子系統 Linux 版)

這裡的範例使用 WSL (Windows 子系統 Linux 版),但可以使用其他 Linux 實作。 如需設定和使用 WSL 的相關信息,請參閱:

選取所需的程式

使用 ps -A 命令列出 Linux 中的進程,以判斷要連線的執行中進程。

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 route show命令來判斷外部 IP 位址。

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

在本逐步解說中,我們將連線到在相同計算機上執行的WSL,並使用localhostIP位址。

將 GDBServer 附加至選取的進程

在 WSL Linux 控制臺上,輸入 gdbserver localhost:1234 python3 以在埠 1234 上啟動 gdbserver,並將它附加至 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 [開始偵錯] 畫面的螢幕快照,其中顯示 連接字串。

按兩下 [確定] 按鈕之後,調試程式應該會連線到 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 連線到單一進程之外,您還可以將一個進程設定為進程伺服器,並列出並附加至系統上的現有進程。 若要這樣做,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

按兩下 [確定] 按鈕之後,您應該以進程伺服器身分連線到 gdbserver。 如同 dbgsrv,您可以繁衍新的進程,也可以列出現有的進程並附加至其中一個進程。

在此範例中,使用 [附加至進程] 選項。

WinDbg [開始偵錯] 畫面的螢幕快照,其中顯示附加至具有 20 個以上已列出進程的處理程式。

請注意,您會看到許多對 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* 可以指向一或多個 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 符號公用程式的詳細資訊,例如 !sourcemap 和 ,請參閱 Linux 符號和來源!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. 儲存 (CTRL-O) 並結束 [CTRL-X] nano 編輯器。

  2. 使用 g++ 編譯 C++ 檔案。 -o 選項可用來指定輸出檔名,而 -g 選項會產生符號檔:

g++ DisplayGreeting.cpp -g -o DisplayGreeting

  1. 如果您的程式代碼中沒有任何錯誤,g++ 命令會在目錄中建立名為 DisplayGreeting 的可執行檔。

  2. 您可以使用下列命令來執行程式:

./DisplayGreeting

  1. 按下傳回鍵會在應用程式中顯示訊息。 查看輸出時,[問候語] 看起來會遭到截斷,並改為顯示 「????」。

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 將符號路徑新增至調試程式會話。 在此範例中,程式代碼正在 WSL Linux Ubuntu 的這個位置中執行,使用者名為 Bob。

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

在 WSL 中,此目錄會對應至 Windows OS 位置: C:\Users\Bob\

因此會使用這兩個命令。

.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 中。

在 WinDbg 中設定斷點的螢幕 DisplayGreeting.cpp快照,其中設定了第 19 行 wprint 的斷點

使用 『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 命令來檢視局部變數 問候語。 請注意,其大小為 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 連線進行疑難解答

--debug使用 選項,在 gdbserver 控制台上顯示其他資訊,以收集連線狀態的詳細資訊。 例如,若要啟動進程伺服器,請使用此命令。

gdbserver --debug --multi localhost:1234

另請參閱

Linux 符號和來源

原始程式碼延伸存取

ELFUTILS debuginfod

選擇最佳遠端偵錯方法