作者:柯煜昌 顧問軟體工程師
目前從事 RadonDB MySQL 容器化研發,華中科技大學研究生畢業,有多年的資料庫內核開發經驗,
掌握 MySQL 內核原始碼的閱讀和除錯能力,不僅是資料庫研發人員的日常,也是 DBA 進階的必經之路,
閱讀本文你將了解:
- 如何準備 MySQL 除錯環境
- GDB 除錯入門及操作示例
- Trace 檔案除錯及操作示例
| 一、準備 Debug 環境
首先用原始碼編譯安裝一個用來除錯的 MySQL 環境,
開啟 -DWITH_DEBUG ,在原始碼路徑創建 build 目錄,進入目錄并執行:
cmake .. -DWITH_BOOST=../../boost -DWITH_DEBUG=1
然后通過如下方式,確認是否編譯成功,
方式一:
$ ./bin/mysqld --verbose --version
回顯 debug 版本資訊,則編譯的是 debug 版本,
ver 8.0.18-debug for Linux on x86_64 (Source distribution)
方式二:
連接資料庫,執行查看版本命令,回顯包含了 debug 字樣,則編譯的是 debug 版本,
$ mysql> select version();
+--------------+
| version() |
+--------------+
| 8.0.18-debug |
+--------------+
1 row in set (0.00 sec)
| 二、使用 GDB 除錯
GDB 全稱 “GNU symbolic debugger”,是 Linux 下常用的程式除錯器,通常以 gdb 命令的形式在終端(Shell)中使用,
啟動 GDB 編譯器
執行如下命令啟動 GDB 編譯器(假設 my.cnf 在用戶根目錄中),進入 GDB 后,敲入 run 即可運行,
gdb --args ./bin/mysqld --defaults-file=~/my.cnf --gdb
其中 --gdb 引數允許你隨時 Ctrl+C 的方式中斷 mysqld 行程,進行除錯命令,
GDB 常用命令
使用多視窗查看原始碼與除錯的讀者,可以使用 layout 命令,在 gdb 中執行 help layout 可以查看更多 gdb 命令用法,
(gdb) help layout
(gdb) help layoutChange the layout of windows.
Usage: layout prev | next | <layout_name>
Layout names are:
src : Displays source and command windows.
asm : Displays disassembly and command windows.
split : Displays source, disassembly and command windows.
regs : Displays register window. If existing layout
is source/command or assembly/command, the
register window is displayed. If the
source/assembly/command (split) is displayed,
the register window is displayed with
the window that has current logical focus.
(gdb)
可以通過 GDB cheat sheet[1],了解更多 GDB 使用方式,
Debug 示例
安裝好 Debug 環境后,我們用以下兩個例子,來簡單演示使用思路及技巧,
1、取變數值
在某種情況下發現 mysqld 已經 crash,系統只有一個 core 檔案,而我們要知道某個系統變數的值,但是系統變數的值,不見得與 my.cnf 檔案一致,
此時,就可以用 gdb 命令將變數列印出來,獲取變數值,
如下所示,需獲取變數 version 的值,只需要在前面加 mysql_sysvar_ 前綴列印即可,
Thread 1 "mysqld" received signal SIGINT, Interrupt.
0x00007ffff5f74cb9 in __GI___poll (fds=0x55555e8a3de0, nfds=2, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
29 ../sysdeps/unix/sysv/linux/poll.c: No such file or directory.
(gdb) p mysql_sysvar_version
$1 = {flags = 68101, name = 0x55555e7ff738 "innodb_version", comment = 0x55555ca953e2 "InnoDB version", check = 0x555558e222f1 <check_func_str(THD*, SYS_VAR*, void*, st_mysql_value*)>, update = 0x555558e22881 <update_func_str(THD*, SYS_VAR*, void*, void const*)>,
value = https://www.cnblogs.com/radondb/archive/2021/12/17/0x55555def1c20 , def_val = 0x55555ca89598"8.0.18"}
(gdb)
2、除錯腳本
假設需獲取某一個連接進入 dispatch_command 有哪些 command ,可以執行 gdb 腳本[2] 獲取,
gdb 腳本內容如下:
b dispatch_command
commands
print command
continue
end
執行 gdb 腳本,然后使用 mysql 客戶端連接資料庫,并執行 SQL 陳述句操作,即可查看到 gdb 除錯資訊,
(gdb) b dispatch_command
Breakpoint 3 at 0x555558ddb37c: file /home/kyc/mysql8/sql/sql_parse.cc, line 1581.
(gdb) commands
Type commands for breakpoint(s) 3, one per line.
End with a line saying just "end".
>print command
>continue
>end
(gdb) c
Continuing.
[Switching to Thread 0x7fffe01fc700 (LWP 5941)]
Thread 49 "mysqld" hit Breakpoint 3, dispatch_command (thd=0x7fff4c000f70, com_data=https://www.cnblogs.com/radondb/archive/2021/12/17/0x7fffe01fbba0, command=COM_QUERY) at /home/kyc/galaxyengine/sql/sql_parse.cc:1581
1581 enum enum_server_command command) {
$4 = COM_QUERY
| 三、使用 Trace 檔案除錯
MySQL 的 debug 版提供了一個專門的 DBUG 包[3],通過這個 DBUG 包,可獲取正在執行操作程式的 Trace 檔案,
通過控制 DBUG 開關,可以將 MySQL 的任何操作,以及所涉及的呼叫模塊、函式、狀態資訊記錄在 Trace 檔案中,
設定 debug 引數
通過設定 debug 引數選項,指定跟蹤方式,
--debug [ = debug_options ]
[ = debug _ options ] 可識別字符 d、t、i 、o 等,
Debug 示例
若需獲取代碼中 DBUG_PRINT("info:" 列印的日志,可以使用 MySQL 客戶端連上服務器,并執行如下命令,開啟 debug 引數,
set debug = 'd,info';
use test;
查看 mysqld.trace 檔案,可獲取 use test 在 MySQL 中的執行流程,
do_command: info: Command on socket (46) = 3 (Query)
do_command: info: packet: ' '; command: 3
dispatch_command: info: command: 3
gtid_pre_statement_checks: info: gtid_next->type=0 owned_gtid.{sidno,gno}={0,0}
THD::is_ddl_gtid_compatible: info: SQLCOM_CREATE:0 CREATE-TMP:0 SELECT:1 SQLCOM_DROP:0 DROP-TMP:0 trx:0
SELECT_LEX::prepare: info: setup_ref_array this 0x7fff1400d298 3 : 0 0 1 2 0 0
setup_fields: info: thd->mark_used_columns: 1
setup_fields: info: thd->mark_used_columns: 1
SELECT_LEX::setup_conds: info: thd->mark_used_columns: 1
THD::decide_logging_format: info: query: SELECT DATABASE()
THD::decide_logging_format: info: variables.binlog_format: 2
................
MDL_context::release_locks_stored_before: info: found lock to release ticket=0x7fff14019ae0
MDL_context::release_locks_stored_before: info: found lock to release ticket=0x7fff1412dd20
MDL_context::release_locks_stored_before: info: found lock to release ticket=0x7fff1412dcc0
net_send_ok: info: affected_rows: 0 id: 0 status: 2 warning_count: 0
net_send_ok: info: OK sent, so no more error sending allowed
本文使用幾個簡單的示例,演示了 MySQL 內核的 Debug 的幾種常見方法,當然,僅僅起到拋磚引玉的作用,更多好玩的技巧,還需讀者自行深度挖掘,
參考
[1]: GDB cheat sheet:https://gist.github.com/rkubik/b96c23bd8ed58333de37f2b8cd052c30
[2]: GDB 腳本除錯:https://sourceware.org/gdb/current/onlinedocs/gdb/Commands.html#Commands
[3]: DBUG Package[:https://dev.mysql.com/doc/refman/8.0/en/dbug-package.html
轉載請註明出處,本文鏈接:https://www.uj5u.com/shujuku/384104.html
標籤:其他
上一篇:最左前綴有手就會,那索引下推呢?
下一篇:關于SQL陳述句的執行順序
