数据库的路上

MYSQL5.7如何对应GDB线程ID和会话ID

概述

mysql是一个单进程多线程的架构数据库,空闲情况下线程已经有二十个了,如何在有多个会话的情况下,通过会话的ID找到对应的线程ID,对于gdb调试来讲,是很有必要的。gdb 可以通过python 扩展实现这个功能。

查看GDB是否支持Python

使用 help命令提示查看,如果不支持python,需要重新编译带有python扩展的版本。

(gdb) help python
python, py
Evaluate a Python command.

The command can be given as an argument, for instance:

    python print (23)

If no argument is given, the following lines are read and used
as the Python commands.  Type a line containing "end" to indicate
the end of the command.

查看当前会话的ID

mysql> select connection_id();
+-----------------+
| connection_id() |
+-----------------+
|              18 |
+-----------------+
1 row in set (0.00 sec)

如何找到对应的线程ID?

mysql 每个会话线程都会创建一个THD类对象,这个对象有一个函数thread_id可以返回线程ID,该ID就是会话ID。

my_thread_id thread_id() const { return m_thread_id; }

通过轮询找到对应的ID是个很简单的方法。

编写python函数实现

通过引入gdb模块,实现灵活的操作gdb变量

import gdb

class FindGDBThread(gdb.Command):
    def __init__(self):
        super(FindGDBThread, self).__init__("find_gdb_thread", gdb.COMMAND_USER)

    def invoke(self, arg, from_tty):
        # 解析参数
        args = gdb.string_to_argv(arg)
        if len(args) != 1:
            print("Usage: find_gdb_thread <user_thread_id>")
            return
        
        target_user_id = int(args[0])
        print(f"Searching for GDB thread with User Thread ID: {target_user_id}")
        
        threads = gdb.selected_inferior().threads()
        found = False
        
        for thread in threads:
            thread.switch()  # 切换到当前线程
            
            # 查找do_command frame
            current_frame = gdb.newest_frame()
            found_do_command_frame = False
            while current_frame is not None:
                if current_frame.name() == "do_command":
                    current_frame.select()  # 切换到do_command frame
                    found_do_command_frame = True
                    break
                current_frame = current_frame.older()
            
            if not found_do_command_frame:
                continue  # 未找到do_command frame,跳过此线程
            
            try:
                # 获取thd变量
                thd = gdb.parse_and_eval("thd")
                if thd == 0:  # 检查是否为空指针
                    continue
                
                # 获取线程ID
                thread_id_value = gdb.parse_and_eval(f"((THD*){thd})->thread_id()")
                
                # 处理不同类型的返回值
                if thread_id_value.type.code == gdb.TYPE_CODE_INT:
                    thread_id = int(thread_id_value)
                elif thread_id_value.type.code == gdb.TYPE_CODE_PTR:
                    thread_id = int(thread_id_value.dereference())
                else:
                    thread_id = int(thread_id_value)
                
                # 检查是否匹配目标用户线程ID
                if thread_id == target_user_id:
                    print(f"Found match!")
                    print(f"GDB Thread ID: {thread.num}")
                    print(f"User Thread ID: {thread_id}")
                    found = True
            except (gdb.error, ValueError):
                continue  # 忽略错误,继续处理下一个线程
        
        if not found:
            print(f"No thread found with User Thread ID: {target_user_id}")

FindGDBThread()

脚本使用

加载到gdb后,可以直接通过注册的函数名调用。

(gdb) source /home/mysql/test/find_gdb_thread.py
(gdb) find_gdb_thread 18
Searching for GDB thread with User Thread ID: 18
Found match!
GDB Thread ID: 29
User Thread ID: 18
(gdb) thread 29
[Switching to thread 29 (Thread 0x7f2b0c3a46c0 (LWP 306385))]
#0  0x00007f2b2c8a7b96 in ?? () from /usr/lib64/libc.so.6

小结

通过gdb的python扩展,可以很灵活的使用gdb,提高debug效率。