使用 kgdb、kdb 和核心偵錯程式內部機制

作者:

Jason Wessel

簡介

核心有兩個不同的偵錯程式前端 (kdb 和 kgdb),它們與除錯核心介面。如果正確配置核心,可以在編譯時和執行時動態地在這兩個偵錯程式前端之間切換。

Kdb 是一個簡單的 shell 風格介面,可以在帶有鍵盤的系統控制檯或序列控制檯上使用。 可以使用它來檢查記憶體、暫存器、程序列表、dmesg,甚至設定斷點以在特定位置停止。 Kdb 不是原始碼級偵錯程式,但可以設定斷點並執行一些基本的核心執行控制。 Kdb 主要用於進行一些分析,以幫助開發或診斷核心問題。如果程式碼是用 CONFIG_KALLSYMS 構建的,則可以在核心內建程式或核心模組中按名稱訪問某些符號。

Kgdb 旨在用作 Linux 核心的原始碼級偵錯程式。 它與 gdb 一起用於除錯 Linux 核心。 預期可以使用 gdb “中斷”到核心中,以檢查記憶體、變數並檢視呼叫堆疊資訊,類似於應用程式開發人員使用 gdb 除錯應用程式的方式。 可以在核心程式碼中放置斷點並執行一些有限的執行步驟。

使用 kgdb 需要兩臺機器。 其中一臺機器是開發機器,另一臺是目標機器。 要除錯的核心在目標機器上執行。 開發機器針對 vmlinux 檔案執行 gdb 例項,該檔案包含符號(不是引導映像,例如 bzImage、zImage、uImage...)。 在 gdb 中,開發人員指定連線引數並連線到 kgdb。 開發人員與 gdb 建立的連線型別取決於在測試機器的核心中編譯為內建程式或可載入核心模組的 kgdb I/O 模組的可用性。

編譯核心

  • 為了啟用 kdb 的編譯,必須首先啟用 kgdb。

  • kgdb 測試編譯選項在 kgdb 測試套件一章中描述。

kgdb 的核心配置選項

要啟用 CONFIG_KGDB,應在 Kernel hacking ‣ Kernel debugging 下查詢,然後選擇 KGDB: kernel debugger

雖然這不是對 vmlinux 檔案中包含符號的硬性要求,但沒有符號資料的 gdb 往往不是很有用,因此需要啟用 CONFIG_DEBUG_INFO,這在配置選單中稱為 Compile the kernel with debug info

建議(但不是必需)啟用 CONFIG_FRAME_POINTER 核心選項,這在配置選單中稱為 Compile the kernel with frame pointers。 此選項會將程式碼插入到已編譯的可執行檔案中,該程式碼會將幀資訊儲存在暫存器中或堆疊中的不同位置,從而允許 gdb 等偵錯程式在除錯核心時更準確地構建堆疊回溯。

如果使用的架構支援核心選項 CONFIG_STRICT_KERNEL_RWX,則應考慮將其關閉。 此選項將阻止使用軟體斷點,因為它將核心記憶體空間的某些區域標記為只讀。 如果 kgdb 支援你使用的架構,則如果希望在啟用 CONFIG_STRICT_KERNEL_RWX 選項的情況下執行,則可以使用硬體斷點,否則需要關閉此選項。

接下來,應選擇一個或多個 I/O 驅動程式來互連除錯主機和除錯目標。 早期啟動除錯需要支援早期除錯的 KGDB I/O 驅動程式,並且驅動程式必須直接構建到核心中。 Kgdb I/O 驅動程式配置透過核心或模組引數進行,可以在描述引數 kgdboc 的部分中瞭解更多資訊。

以下是啟用或停用 kgdb 的 .config 符號的示例集

# CONFIG_STRICT_KERNEL_RWX is not set
CONFIG_FRAME_POINTER=y
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y

kdb 的核心配置選項

Kdb 比坐在核心除錯核心之上的簡單 gdbstub 複雜得多。 Kdb 必須實現一個 shell,並且還在核心的其他部分中新增一些輔助函式,這些函式負責打印出有趣的資料,例如執行 lsmodps 時看到的內容。 為了將 kdb 構建到核心中,請按照與 kgdb 相同的步驟操作。

kdb 的主要配置選項是 CONFIG_KGDB_KDB,這在配置選單中稱為 KGDB_KDB: include kdb frontend for kgdb。 從理論上講,如果計劃在序列埠上使用 kdb,則在配置 kgdb 時,你可能已經選擇了 I/O 驅動程式,例如 CONFIG_KGDB_SERIAL_CONSOLE 介面。

如果想將 PS/2 樣式的鍵盤與 kdb 一起使用,則應選擇 CONFIG_KDB_KEYBOARD,這在配置選單中稱為 KGDB_KDB: keyboard as input deviceCONFIG_KDB_KEYBOARD 選項未用於 kgdb 的 gdb 介面中的任何內容。 CONFIG_KDB_KEYBOARD 選項僅適用於 kdb。

以下是啟用/停用 kdb 的 .config 符號的示例集

# CONFIG_STRICT_KERNEL_RWX is not set
CONFIG_FRAME_POINTER=y
CONFIG_KGDB=y
CONFIG_KGDB_SERIAL_CONSOLE=y
CONFIG_KGDB_KDB=y
CONFIG_KDB_KEYBOARD=y

核心偵錯程式引導引數

本節介紹影響核心偵錯程式配置的各種執行時核心引數。 以下章節介紹瞭如何使用 kdb 和 kgdb,並提供了一些配置引數的示例。

核心引數:kgdboc

kgdboc 驅動程式最初是 meant to stand for “kgdb over console” 的縮寫。 如今,它是配置如何從 gdb 與 kgdb 通訊以及想要用於與 kdb shell 互動的裝置的主要機制。

對於 kgdb/gdb,kgdboc 旨在與單個序列埠一起使用。 它旨在涵蓋這樣一種情況,即希望使用序列控制檯作為主控制檯,並使用它來執行核心除錯。 也可以在未指定為系統控制檯的序列埠上使用 kgdb。 kgdboc 可以配置為核心內建程式或核心可載入模組。 如果將 kgdboc 構建到核心中作為內建程式,則只能使用 kgdbwait 和早期除錯。

可以選擇啟用 KMS(核心模式設定)整合。 將 KMS 與 kgdboc 一起使用,並且影片驅動程式具有原子模式設定掛鉤時,可以在圖形控制檯上進入偵錯程式。 恢復核心執行時,將恢復先前的圖形模式。 此整合可以用作一種有用的工具,可幫助診斷崩潰或使用 kdb 分析記憶體,同時允許執行完整的圖形控制檯應用程式。

kgdboc 引數

用法

kgdboc=[kms][[,]kbd][[,]serial_device][,baud]

如果一起使用任何可選配置,則必須遵守上面列出的順序。

縮寫

  • kms = 核心模式設定

  • kbd = 鍵盤

可以配置 kgdboc 以使用鍵盤和/或序列裝置,具體取決於是否在以下情況之一中使用 kdb 和/或 kgdb。 如果一起使用任何可選配置,則必須遵守上面列出的順序。 將 KMS 與僅 gdb 結合使用通常不是很有用的組合。

使用可載入模組或內建程式
  1. 作為核心內建程式

    使用核心引導引數

    kgdboc=<tty-device>,[baud]
    
  2. 作為核心可載入模組

    使用命令

    modprobe kgdboc kgdboc=<tty-device>,[baud]
    

    以下是如何格式化 kgdboc 字串的兩個示例。 第一個用於使用第一個序列埠的 x86 目標。 第二個示例用於使用第二個序列埠的 ARM Versatile AB。

    1. kgdboc=ttyS0,115200

    2. kgdboc=ttyAMA1,115200

使用 sysfs 在執行時配置 kgdboc

在執行時,可以透過將引數寫入 sysfs 來啟用或停用 kgdboc。 以下是兩個示例

  1. 在 ttyS0 上啟用 kgdboc

    echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc
    
  2. 停用 kgdboc

    echo "" > /sys/module/kgdboc/parameters/kgdboc
    

注意

如果要在 tty 上配置控制檯,則無需指定波特率,該控制檯已配置或開啟。

更多示例

可以配置 kgdboc 以使用鍵盤和/或序列裝置,具體取決於是否在以下情況之一中使用 kdb 和/或 kgdb。

  1. 僅透過序列埠的 kdb 和 kgdb

    kgdboc=<serial_device>[,baud]
    

    示例

    kgdboc=ttyS0,115200
    
  2. 帶有鍵盤和序列埠的 kdb 和 kgdb

    kgdboc=kbd,<serial_device>[,baud]
    

    示例

    kgdboc=kbd,ttyS0,115200
    
  3. 帶有鍵盤的 kdb

    kgdboc=kbd
    
  4. 帶有核心模式設定的 kdb

    kgdboc=kms,kbd
    
  5. 帶有核心模式設定和透過序列埠的 kgdb 的 kdb

    kgdboc=kms,kbd,ttyS0,115200
    

注意

Kgdboc 不支援透過 gdb 遠端協議中斷目標。 除非有代理將控制檯輸出拆分到終端程式,否則必須手動傳送 SysRq-G。 控制檯代理為偵錯程式提供單獨的 TCP 埠,為“人”控制檯提供單獨的 TCP 埠。 代理可以負責為你傳送 SysRq-G

當使用沒有偵錯程式代理的 kgdboc 時,可能會在兩個入口點之一連線偵錯程式。 如果在載入 kgdboc 後發生異常,則應在控制檯上列印一條訊息,說明它正在等待偵錯程式。 在這種情況下,斷開終端程式的連線,然後在它的位置連線偵錯程式。 如果要中斷目標系統並強制進入除錯會話,則必須發出 Sysrq 序列,然後鍵入字母 g。 然後斷開終端會話並連線 gdb。 如果不喜歡這樣做,可以選擇對 gdb 進行駭客攻擊,以便在初始連線時傳送 SysRq-G,或者使用允許未修改的 gdb 執行除錯的偵錯程式代理。

核心引數:kgdboc_earlycon

如果指定核心引數 kgdboc_earlycon 並且序列驅動程式註冊了支援輪詢的引導控制檯(不需要中斷並實現非阻塞 read() 函式),則 kgdb 將嘗試使用引導控制檯工作,直到它可以轉換為 kgdboc 引數指定的常規 tty 驅動程式。

通常只有一個引導控制檯(尤其是實現了 read() 函式的控制檯),因此僅新增 kgdboc_earlycon 本身就足以使其工作。 如果有多個引導控制檯,則可以新增引導控制檯的名稱以進行區分。 請注意,透過引導控制檯層和 tty 層註冊的名稱對於同一埠而言並不相同。

例如,在一個板上要明確地說明,可以執行以下操作

kgdboc_earlycon=qcom_geni kgdboc=ttyMSM0

如果裝置上唯一的引導控制檯是“qcom_geni”,則可以簡化為

kgdboc_earlycon kgdboc=ttyMSM0

核心引數:kgdbwait

核心命令列選項 kgdbwait 使 kgdb 在核心引導期間等待偵錯程式連線。 只有在將 kgdb I/O 驅動程式編譯到核心中,並將 I/O 驅動程式配置指定為核心命令列選項時,才能使用此選項。 kgdbwait 引數應始終跟在核心命令列中 kgdb I/O 驅動程式的配置引數之後,否則 I/O 驅動程式將不會在要求核心使用它進行等待之前配置。

當使用此選項時,核心將在 I/O 驅動程式和架構允許的情況下儘早停止並等待。 如果將 kgdb I/O 驅動程式構建為可載入核心模組,則 kgdbwait 將不會執行任何操作。

核心引數:kgdbcon

kgdbcon 功能允許在 gdb 連線到核心時在 gdb 中看到 printk() 訊息。 Kdb 不使用 kgdbcon 功能。

當偵錯程式連線並執行時,Kgdb 支援使用 gdb 序列協議將控制檯訊息傳送到偵錯程式。 有兩種方法可以啟用此功能。

  1. 使用核心命令列選項啟用

    kgdbcon
    
  2. 在配置 I/O 驅動程式之前使用 sysfs

    echo 1 > /sys/module/debug_core/parameters/kgdb_use_con
    

注意

如果在配置 kgdb I/O 驅動程式之後執行此操作,則該設定將不會生效,直到下次重新配置 I/O 時。

重要說明

不能在作為活動系統控制檯的 tty 上使用 kgdboc + kgdbcon。 不正確用法的示例是

console=ttyS0,115200 kgdboc=ttyS0 kgdbcon

可以將此選項與不在系統控制檯上的 tty 上的 kgdboc 一起使用。

執行時引數:kgdbreboot

kgdbreboot 功能允許更改偵錯程式處理重新啟動通知的方式。 對於該行為,有 3 個選項。 預設行為始終設定為 0。

1

echo -1 > /sys/module/debug_core/parameters/kgdbreboot

完全忽略重新啟動通知。

2

echo 0 > /sys/module/debug_core/parameters/kgdbreboot

將分離訊息傳送到任何附加的偵錯程式客戶端。

3

echo 1 > /sys/module/debug_core/parameters/kgdbreboot

在重新啟動通知時進入偵錯程式。

核心引數:nokaslr

如果使用的架構預設啟用 KASLR,則應考慮將其關閉。 KASLR 隨機化核心映像對映到的虛擬地址,並混淆 gdb,gdb 從 vmlinux 的符號表中解析核心符號的地址。

使用 kdb

在序列埠上快速啟動 kdb

這是一個如何使用 kdb 的快速示例。

  1. 使用核心引數在引導時配置 kgdboc

    console=ttyS0,115200 kgdboc=ttyS0,115200 nokaslr
    

    在核心引導後配置 kgdboc;假設使用的是序列埠控制檯

    echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc
    
  2. 手動或透過等待 oops 或故障進入核心偵錯程式。 可以透過多種方式手動進入核心偵錯程式;所有方式都涉及使用 SysRq-G,這意味著必須在核心配置中啟用 CONFIG_MAGIC_SYSRQ=y

    • 以 root 身份登入或使用超級使用者會話時,可以執行

      echo g > /proc/sysrq-trigger
      
    • 使用 minicom 2.2 的示例

      按:CTRL-A f g

    • 當 telnet 到支援傳送遠端中斷的終端伺服器時

      按:CTRL-]

      鍵入:send break

      按:Enter g

  3. 在 kdb 提示符下,可以執行 help 命令以檢視可用命令的完整列表。

    kdb 中一些有用的命令包括

    lsmod

    顯示核心模組的載入位置

    ps

    僅顯示活動程序

    ps A

    顯示所有程序

    summary

    顯示核心版本資訊和記憶體使用情況

    bt

    使用 dump_stack() 獲取當前程序的回溯

    dmesg

    檢視核心系統日誌緩衝區

    go

    繼續系統

  4. 完成使用 kdb 後,需要考慮重新啟動系統或使用 go 命令恢復正常的核心執行。 如果已將核心暫停了很長時間,則依賴於及時聯網或與實際掛鐘時間有關的應用程式可能會受到不利影響,因此在使用核心偵錯程式時應考慮到這一點。

使用鍵盤連線的控制檯快速啟動 kdb

這是一個如何將 kdb 與鍵盤一起使用的快速示例。

  1. 使用核心引數在引導時配置 kgdboc

    kgdboc=kbd
    

    在核心引導後配置 kgdboc

    echo kbd > /sys/module/kgdboc/parameters/kgdboc
    
  2. 手動或透過等待 oops 或故障進入核心偵錯程式。 可以透過多種方式手動進入核心偵錯程式;所有方式都涉及使用 SysRq-G,這意味著必須在核心配置中啟用 CONFIG_MAGIC_SYSRQ=y

    • 以 root 身份登入或使用超級使用者會話時,可以執行

      echo g > /proc/sysrq-trigger
      
    • 使用筆記型電腦鍵盤的示例

      按住:Alt

      按住:Fn

      按並釋放帶有標籤的鍵:SysRq

      釋放:Fn

      按並釋放:g

      釋放:Alt

    • 使用 PS/2 101 鍵鍵盤的示例

      按住:Alt

      按並釋放帶有標籤的鍵:SysRq

      按並釋放:g

      釋放:Alt

  3. 現在鍵入 kdb 命令,例如 helpdmesgbtgo 以繼續核心執行。

使用 kgdb / gdb

為了使用 kgdb,必須透過將配置資訊傳遞給 kgdb I/O 驅動程式之一來啟用它。 如果未傳遞任何配置資訊,kgdb 將不會執行任何操作。 僅當載入和配置 kgdb I/O 驅動程式時,Kgdb 才會主動連線到核心陷阱掛鉤。 如果取消配置 kgdb I/O 驅動程式,kgdb 將取消註冊所有核心掛鉤點。

如果啟用了 CONFIG_SYSFSCONFIG_MODULES,則可以透過將新的配置字串回顯到 /sys/module/<driver>/parameter/<option> 來在執行時重新配置所有 kgdb I/O 驅動程式。 可以透過傳遞空字串來取消配置驅動程式。 連線偵錯程式時,無法更改配置。 在嘗試取消配置 kgdb I/O 驅動程式之前,請確保使用 detach 命令分離偵錯程式。

使用 gdb 連線到序列埠

  1. 配置 kgdboc

    使用核心引數在引導時配置 kgdboc

    kgdboc=ttyS0,115200
    

    在核心引導後配置 kgdboc

    echo ttyS0 > /sys/module/kgdboc/parameters/kgdboc
    
  2. 停止核心執行(中斷到偵錯程式)

    為了透過 kgdboc 連線到 gdb,必須首先停止核心。 有多種方法可以停止核心,包括使用 kgdbwait 作為引導引數,透過 SysRq-G,或執行核心直到它出現異常並等待偵錯程式連線。

    • 以 root 身份登入或使用超級使用者會話時,可以執行

      echo g > /proc/sysrq-trigger
      
    • 使用 minicom 2.2 的示例

      按:CTRL-A f g

    • 當 telnet 到支援傳送遠端中斷的終端伺服器時

      按:CTRL-]

      鍵入:send break

      按:Enter g

  3. 從 gdb 連線

    示例(使用直接連線的埠)

    % gdb ./vmlinux
    (gdb) set serial baud 115200
    (gdb) target remote /dev/ttyS0
    

    示例(kgdb 到 TCP 埠 2012 上的終端伺服器)

    % gdb ./vmlinux
    (gdb) target remote 192.168.2.2:2012
    

    連線後,可以像除錯應用程式一樣除錯核心。

    如果在連線時遇到問題或在除錯時出現嚴重錯誤,則最常見的情況是希望 gdb 詳細說明其目標通訊。 在鍵入 target remote 命令之前,透過鍵入以下內容來執行此操作

    set debug remote 1
    

請記住,如果在 gdb 中繼續,並且需要再次“中斷”,則需要發出另一個 SysRq-G。 透過在 sys_sync 處放置斷點可以輕鬆建立一個簡單的入口點,然後可以從 shell 或指令碼執行 sync 以中斷到偵錯程式中。

kgdb 和 kdb 互操作性

可以在 kdb 和 kgdb 之間動態轉換。 除錯核心會記住上次使用的模式,並自動以相同的模式啟動。

在 kdb 和 kgdb 之間切換

從 kgdb 切換到 kdb

有兩種方法可以從 kgdb 切換到 kdb:可以使用 gdb 發出維護資料包,也可以盲目地鍵入命令 $3#33。 每當核心偵錯程式以 kgdb 模式停止時,它將列印訊息 KGDB or $3#33 for KDB。 重要的是要注意必須一次性正確鍵入序列。 不能鍵入退格鍵或刪除鍵,因為 kgdb 會將其解釋為除錯流的一部分。

  1. 透過盲目鍵入從 kgdb 更改為 kdb

    $3#33
    
  2. 使用 gdb 從 kgdb 更改為 kdb

    maintenance packet 3
    

    注意

    現在必須終止 gdb。 通常,按 CTRL-Z 併發出命令

    kill -9 %
    

從 kdb 更改為 kgdb

可以透過兩種方式從 kdb 更改為 kgdb。 可以透過從 kdb shell 提示符發出 kgdb 命令來手動進入 kgdb 模式,也可以在 kdb shell 提示符處於活動狀態時連線 gdb。 kdb shell 查詢 gdb 透過 gdb 遠端協議發出的典型第一個命令,如果看到其中一個命令,它會自動更改為 kgdb 模式。

  1. 從 kdb 發出命令

    kgdb
    
  2. 在 kdb 提示符下,斷開終端程式的連線,並在其位置連線 gdb。

從 gdb 執行 kdb 命令

可以使用 gdb monitor 命令從 gdb 執行一組有限的 kdb 命令。 不希望執行任何執行控制或斷點操作,因為它會中斷核心偵錯程式的狀態。 如果已連線 gdb,則應使用 gdb 執行斷點和執行控制操作。 更實用的命令是諸如 lsmod、dmesg、ps 或可能一些記憶體資訊命令之類的命令。 要檢視可以執行的所有 kdb 命令,可以執行 monitor help

示例

(gdb) monitor ps
1 idle process (state I) and
27 sleeping system daemon (state M) processes suppressed,
use 'ps A' to see all.
Task Addr       Pid   Parent [*] cpu State Thread     Command

0xc78291d0        1        0  0    0   S  0xc7829404  init
0xc7954150      942        1  0    0   S  0xc7954384  dropbear
0xc78789c0      944        1  0    0   S  0xc7878bf4  sh
(gdb)

kgdb 測試套件

在核心配置中啟用 kgdb 時,還可以選擇啟用配置引數 KGDB_TESTS。 啟用此選項將啟用一個特殊的 kgdb I/O 模組,該模組旨在測試 kgdb 內部函式。

kgdb 測試主要供開發人員測試 kgdb 內部機制以及開發新的 kgdb 架構特定實現的工具。 這些測試實際上不適用於 Linux 核心的終端使用者。 主要的文件來源是檢視 drivers/misc/kgdbts.c 檔案。

還可以透過設定核心配置引數 KGDB_TESTS_ON_BOOT 在編譯時配置 kgdb 測試套件以執行核心測試集。 此特定選項旨在用於自動化迴歸測試,不需要修改核心引導配置引數。 如果啟用了此選項,則可以透過指定 kgdbts= 作為核心引導引數來停用 kgdb 測試套件。

核心偵錯程式內部機制

架構特定資訊

核心偵錯程式被組織成許多元件

  1. 除錯核心

    除錯核心位於 kernel/debugger/debug_core.c 中。 它包含

    • 一個通用的 OS 異常處理程式,其中包括在多 CPU 系統上將處理器同步到停止狀態。

    • 與 kgdb I/O 驅動程式通訊的 API

    • 呼叫架構特定的 kgdb 實現的 API

    • 在使用偵錯程式時執行安全記憶體讀取和寫入記憶體的邏輯

    • 除非被架構覆蓋,否則軟體斷點的完整實現

    • 呼叫 kdb 或 kgdb 前端到除錯核心的 API。

    • 原子核心模式設定的結構和回撥 API。

      注意

      kgdboc 是呼叫 KMS 回撥的位置。

  2. kgdb 架構特定的實現

    此實現通常位於 arch/*/kernel/kgdb.c 中。 例如,arch/x86/kernel/kgdb.c 包含實現 HW 斷點以及動態註冊和取消註冊此架構上的陷阱處理程式的初始化的詳細資訊。 架構特定的部分實現

    • 包含一個架構特定的陷阱捕捉器,該捕捉器呼叫 kgdb_handle_exception() 以啟動 kgdb 關於執行其工作

    • 與 struct pt_regs 之間轉換 gdb 特定資料包格式

    • 註冊和取消註冊架構特定的陷阱掛鉤

    • 任何特殊的異常處理和清理

    • NMI 異常處理和清理

    • (可選)HW 斷點

  3. gdbstub 前端(又名 kgdb)

    gdbstub 位於 kernel/debug/gdbstub.c 中。 它包含

    • 實現 gdb 序列協議的所有邏輯

  4. kdb 前端

    kdb 偵錯程式 shell 分解為許多元件。 kdb 核心位於 kernel/debug/kdb 中。 其他一些核心元件中有許多輔助函式,使 kdb 能夠檢查和報告有關核心的資訊,而無需獲取可能導致核心死鎖的鎖。 kdb 核心實現以下功能。

    • 一個簡單的 shell

    • kdb 核心命令集

    • 用於註冊其他 kdb shell 命令的註冊 API。

      • 一個自包含的 kdb 模組的一個很好的例子是用於轉儲 ftrace 緩衝區的 ftdump 命令。 請參閱:kernel/trace/trace_kdb.c

      • 有關如何動態註冊新 kdb 命令的示例,可以從 samples/kdb/kdb_hello.c 構建 kdb_hello.ko 核心模組。 要構建此示例,可以在核心配置中設定 CONFIG_SAMPLES=yCONFIG_SAMPLE_KDB=m。 稍後執行 modprobe kdb_hello,下次進入 kdb shell 時,可以執行 hello 命令。

    • kdb_printf() 的實現,它將訊息直接傳送到 I/O 驅動程式,繞過核心日誌。

    • kdb shell 的 SW / HW 斷點管理

  5. kgdb I/O 驅動程式

    每個 kgdb I/O 驅動程式都必須為以下內容提供實現

    • 透過內建程式或模組進行配置

    • 動態配置和 kgdb 掛鉤註冊呼叫

    • 讀取和寫入字元介面

    • 用於從 kgdb 核心取消配置的清理處理程式

    • (可選)早期除錯方法

    任何給定的 kgdb I/O 驅動程式都必須與硬體非常緊密地配合,並且必須以一種既不啟用中斷又不更改系統上下文的其他部分的方式進行操作,而不會完全恢復它們。 當 kgdb 核心需要輸入時,它將重複“輪詢”kgdb I/O 驅動程式以獲取字元。 如果沒有可用資料,則 I/O 驅動程式應立即返回。 這樣做可以為將來以某種方式接觸看門狗硬體的可能性,從而使目標系統在啟用它們時不會重置。

如果要為新架構新增 kgdb 架構特定的支援,則架構應在其架構特定的 Kconfig 檔案中定義 HAVE_ARCH_KGDB。 這將為該架構啟用 kgdb,並且此時必須建立架構特定的 kgdb 實現。

在其 asm/kgdb.h 檔案中必須在每個架構上設定幾個標誌。 這些是

  • NUMREGBYTES:

    所有暫存器的大小(以位元組為單位),以便可以確保它們都適合一個數據包。

  • BUFMAX:

    GDB 將讀入的緩衝區的大小(以位元組為單位)。 這必須大於 NUMREGBYTES。

  • CACHE_FLUSH_IS_SAFE:

    如果呼叫 flush_cache_range 或 flush_icache_range 始終安全,則設定為 1。 在某些架構上,由於我們將其他 CPU 保留在保持模式中,因此呼叫這些函式對於 SMP 可能不安全。

對於常見的後端,在 kernel/kgdb.c 中還可以找到以下函式,除非標記為(可選),否則必須由特定於架構的後端提供。如果架構不需要提供特定的實現,則可以使用預設函式。

int kgdb_skipexception(int exception, struct pt_regs *regs)

(可選)提前退出 kgdb_handle_exception

引數

int exception

異常向量號

struct pt_regs *regs

當前的 struct pt_regs

在某些架構上,需要在刪除斷點後發生的斷點異常時跳過該異常。這可以在 kgdb 的特定於架構的部分中實現。

void kgdb_breakpoint(void)

編譯到斷點中

引數

void

無引數

描述

這將實現為每個架構的靜態內聯。此函式由 kgdb 核心呼叫,以執行特定於架構的陷阱,以使 kgdb 進入異常處理。

int kgdb_arch_init(void)

執行任何特定於架構的初始化。

引數

void

無引數

描述

此函式將處理任何特定於架構的回撥的初始化。

void kgdb_arch_exit(void)

執行任何特定於架構的反初始化。

引數

void

無引數

描述

此函式將處理任何特定於架構的回撥的反初始化,用於動態註冊和取消註冊。

void pt_regs_to_gdb_regs(unsigned long *gdb_regs, struct pt_regs *regs)

將 ptrace regs 轉換為 GDB regs

引數

unsigned long *gdb_regs

指向以 GDB 想要的順序儲存暫存器的指標。

struct pt_regs *regs

當前程序的 struct pt_regs

regs 中的 pt_regs 轉換為 GDB 期望的暫存器格式,儲存在 gdb_regs 中。

void sleeping_thread_to_gdb_regs(unsigned long *gdb_regs, struct task_struct *p)

將 ptrace regs 轉換為 GDB regs

引數

unsigned long *gdb_regs

指向以 GDB 想要的順序儲存暫存器的指標。

struct task_struct *p

所需程序的 struct task_struct

p 中睡眠程序的暫存器值轉換為 GDB 期望的格式。當 kgdb 無法訪問 struct pt_regs 時,將呼叫此函式,因此它應該用在 switch_to 期間儲存在 struct thread_struct 執行緒欄位中的內容填充 gdb 暫存器 gdb_regs

void gdb_regs_to_pt_regs(unsigned long *gdb_regs, struct pt_regs *regs)

將 GDB regs 轉換為 ptrace regs。

引數

unsigned long *gdb_regs

指向儲存從 GDB 收到的暫存器的指標。

struct pt_regs *regs

指向 struct pt_regs 的指標,用於儲存這些值。

gdb_regs 中的 GDB regs 轉換為 pt_regs,並將它們儲存在 regs 中。

int kgdb_arch_handle_exception(int vector, int signo, int err_code, char *remcom_in_buffer, char *remcom_out_buffer, struct pt_regs *regs)

處理特定於架構的 GDB 資料包。

引數

int vector

發生的異常的錯誤向量。

int signo

發生的異常的訊號編號。

int err_code

發生的異常的錯誤程式碼。

char *remcom_in_buffer

已讀取的資料包的緩衝區。

char *remcom_out_buffer

用於寫入資料包的 BUFMAX 位元組緩衝區。

struct pt_regs *regs

當前程序的 struct pt_regs

此函式必須處理“c”和“s”命令資料包,以及用於設定/刪除硬體斷點的資料包(如果使用)。如果硬體需要處理其他資料包,則在此處處理。如果程式碼想要處理更多資料包,則應返回 -1,如果想要退出 kgdb 回撥,則返回 01

void kgdb_arch_handle_qxfer_pkt(char *remcom_in_buffer, char *remcom_out_buffer)

處理特定於架構的 GDB XML 資料包。

引數

char *remcom_in_buffer

已讀取的資料包的緩衝區。

char *remcom_out_buffer

用於寫入資料包的 BUFMAX 位元組緩衝區。

void kgdb_call_nmi_hook(void *ignored)

在當前 CPU 上呼叫 kgdb_nmicallback()

引數

void *ignored

此引數僅在此處用於匹配原型。

如果您使用的是 kgdb_roundup_cpus() 的預設實現,則將為每個 CPU 呼叫此函式。如果您未實現 kgdb_call_nmi_hook(),則將使用預設值。

void kgdb_roundup_cpus(void)

使其他 CPU 進入保持模式

引數

void

無引數

描述

在 SMP 系統上,我們需要引起其他 CPU 的注意,並使它們進入已知狀態。這應該執行使其他 CPU 呼叫 kgdb_wait() 所需的操作。請注意,在某些架構上,NMI 方法不用於彙總所有 CPU。通常,這些架構可以不實現此功能並獲得預設值。

在非 SMP 系統上,不會呼叫此函式。

void kgdb_arch_set_pc(struct pt_regs *regs, unsigned long pc)

程式計數器的通用回撥

引數

struct pt_regs *regs

當前的 struct pt_regs

unsigned long pc

程式計數器的新值

此函式處理更新程式計數器,並且需要特定於架構的實現。

void kgdb_arch_late(void)

執行任何特定於架構的初始化。

引數

void

無引數

描述

此函式將處理任何特定於架構的回撥的後期初始化。這是一個可選函式,用於處理諸如硬體斷點的後期初始化之類的事情。預設實現不執行任何操作。

struct kgdb_arch

描述特定於架構的值。

定義:

struct kgdb_arch {
    unsigned char           gdb_bpt_instr[BREAK_INSTR_SIZE];
    unsigned long           flags;
    int (*set_breakpoint)(unsigned long, char *);
    int (*remove_breakpoint)(unsigned long, char *);
    int (*set_hw_breakpoint)(unsigned long, int, enum kgdb_bptype);
    int (*remove_hw_breakpoint)(unsigned long, int, enum kgdb_bptype);
    void (*disable_hw_break)(struct pt_regs *regs);
    void (*remove_all_hw_break)(void);
    void (*correct_hw_break)(void);
};

成員

gdb_bpt_instr

觸發斷點的指令。

標誌

斷點的標誌,目前只有 KGDB_HW_BREAKPOINT

set_breakpoint

允許架構指定如何設定軟體斷點。

remove_breakpoint

允許架構指定如何刪除軟體斷點。

set_hw_breakpoint

允許架構指定如何設定硬體斷點。

remove_hw_breakpoint

允許架構指定如何刪除硬體斷點。

disable_hw_break

允許架構指定如何為單個 cpu 停用硬體斷點。

remove_all_hw_break

允許架構指定如何刪除所有硬體斷點。

correct_hw_break

允許架構指定如何更正硬體除錯暫存器。

struct kgdb_io

描述 I/O 驅動程式與 KGDB 通訊的介面。

定義:

struct kgdb_io {
    const char              *name;
    int (*read_char) (void);
    void (*write_char) (u8);
    void (*flush) (void);
    int (*init) (void);
    void (*deinit) (void);
    void (*pre_exception) (void);
    void (*post_exception) (void);
    struct console          *cons;
};

成員

名稱

I/O 驅動程式的名稱。

read_char

指向將返回一個字元的函式的指標。

write_char

指向將寫入一個字元的函式的指標。

重新整理

指向將重新整理任何掛起寫入的函式的指標。

初始化

指向將初始化裝置的函式的指標。

反初始化

指向將反初始化裝置的函式的指標。這意味著此 I/O 驅動程式是臨時的,並且希望被替換。當 I/O 驅動程式被替換或顯式取消註冊時呼叫。

pre_exception

指向將為 I/O 驅動程式執行任何準備工作的函式的指標。

post_exception

指向將為 I/O 驅動程式執行任何清理工作的函式的指標。

cons

如果 I/O 裝置是控制檯則有效;否則為 NULL。

kgdboc 內部原理

kgdboc 和 uarts

kgdboc 驅動程式實際上是一個非常薄的驅動程式,它依賴於底層硬體驅動程式具有“輪詢鉤子”,tty 驅動程式連線到這些鉤子。在 kgdboc 的初始實現中,更改了 serial_core 以公開一個低階 UART 鉤子,用於在原子上下文中進行單個字元的輪詢模式讀取和寫入。當 kgdb 向偵錯程式發出 I/O 請求時,kgdboc 會呼叫 serial core 中的回撥,而 serial core 又使用 UART 驅動程式中的回撥。

將 kgdboc 與 UART 結合使用時,UART 驅動程式必須在 struct uart_ops 中實現兩個回撥。來自 drivers/8250.c 的示例

#ifdef CONFIG_CONSOLE_POLL
    .poll_get_char = serial8250_get_poll_char,
    .poll_put_char = serial8250_put_poll_char,
#endif

圍繞建立輪詢驅動程式的任何實現細節都使用 #ifdef CONFIG_CONSOLE_POLL,如上所示。請記住,必須以一種可以從原子上下文中呼叫的方式實現輪詢鉤子,並且必須在返回時恢復 UART 晶片的狀態,以便系統在偵錯程式分離時可以恢復正常。您需要非常小心您考慮的任何型別的鎖,因為在這裡失敗很可能意味著按下重置按鈕。

kgdboc 和鍵盤

kgdboc 驅動程式包含用於配置與連線的鍵盤的通訊的邏輯。僅當在核心配置中設定 CONFIG_KDB_KEYBOARD=y 時,鍵盤基礎架構才會編譯到核心中。

PS/2 型別鍵盤的核心輪詢鍵盤驅動程式位於 drivers/char/kdb_keyboard.c 中。當 kgdboc 在名為 kdb_poll_funcs[] 的陣列中填充回撥時,此驅動程式將掛接到除錯核心。kdb_get_kbd_char() 是輪詢硬體以獲取單個字元輸入的頂層函式。

kgdboc 和 kms

kgdboc 驅動程式包含邏輯,用於在您使用 kgdboc=kms,kbd 時請求圖形顯示切換到文字上下文,前提是您具有具有幀緩衝控制檯和原子核心模式設定支援的影片驅動程式。

每次進入核心偵錯程式時,它都會呼叫 kgdboc_pre_exp_handler(),而 kgdboc_pre_exp_handler() 又會在虛擬控制檯層中呼叫 con_debug_enter()。恢復核心執行時,核心偵錯程式會呼叫 kgdboc_post_exp_handler(),而 kgdboc_post_exp_handler() 又會呼叫 con_debug_leave()

任何想要與核心偵錯程式和原子 kms 回撥相容的影片驅動程式都必須實現 mode_set_base_atomicfb_debug_enterfb_debug_leave operations。對於 fb_debug_enterfb_debug_leave,可以選擇使用通用的 drm fb 輔助函式或為硬體實現自定義函式。以下示例顯示了 drivers/gpu/drm/i915/intel_display.c 中 .mode_set_base_atomic 操作的初始化

static const struct drm_crtc_helper_funcs intel_helper_funcs = {
[...]
        .mode_set_base_atomic = intel_pipe_set_base_atomic,
[...]
};

以下示例顯示了 i915 驅動程式如何在 drivers/gpu/drm/i915/intel_fb.c 中初始化 fb_debug_enter 和 fb_debug_leave 函式以使用通用的 drm 輔助函式

static struct fb_ops intelfb_ops = {
[...]
       .fb_debug_enter = drm_fb_helper_debug_enter,
       .fb_debug_leave = drm_fb_helper_debug_leave,
[...]
};

貢獻者

以下人員為本文件做出了貢獻

  1. Amit Kale <amitkale@linsyssoft.com>

  2. Tom Rini <trini@kernel.crashing.org>

在 2008 年 3 月,本文件由以下人員完全重寫

在 2010 年 1 月,本文件已更新為包含 kdb。