10—mysql内核调试方法

最后更新于:2022-04-01 16:04:58

在前面三篇,bingxi和alex聊了关于innodb的hash、list、以及动态数组的实现方法,这三个结构比较常用。讲完前9篇内容,本篇会描述在windows环境下debug mysql的方法,强烈建议通过debug的方式进行学习。在本篇里,bingxi和alex会聊到windows下常用的调试mysql代码的方法,仅供参考。 ## 1)在windows和linux下调试的异同? Bingxi:“alex,咱们看myslq代码的方法,是通过windows看好呢,还是linux/unix下看呢,两者之间最大的差异是什么?” Alex:“在mysql 5.1的高版本开始,windows环境与linux环境使用同一套代码。我电脑里面正好有两个版本的代码,我们看下mysql-6.0.4-alpha目录下的INSTALL-WIN-SOURCE文件,其中有这么一段: To  build MySQL on Windows from source, you must satisfy the  following system, compiler, and resource requirements:    * Windows 2000, Windows XP, or newer version. Windows Vista is      not supported until Microsoft certifies Visual Studio 2005 on      Vista.    * CMake, which can be downloaded from http://www.cmake.org.      After  installing, modify your path to include the cmake      binary.    * Microsoft Visual C++ 2005 Express Edition, Visual Studio .Net      2003 (7.1), or Visual Studio 2005 (8.0) compiler system.    * If you are using Visual C++ 2005 Express Edition, you must      also install an appropriate Platform SDK. More information and      links to downloads for various Windows platforms is available      from http://msdn.microsoft.com/platformsdk/.    * If you are compiling from a BitKeeper tree or making changes      to  the parser, you need bison for Windows, which can be      downloaded from      http://gnuwin32.sourceforge.net/packages/bison.htm.Download      the package labeled "Complete package, excluding sources".      After installing the package, modify your path to include the      bison binary and ensure that this binary is accessible from      Visual Studio.    * Cygwin might be necessary if you want to run the test script      or package the compiled binaries and support files into a Zip      archive.  (Cygwin  is needed only to test or package the      distribution, not to build it.) Cygwin is available from      http://cygwin.com.    * 3GB to 5GB of disk space. 可以通过这样的方式来生成一份代码,然后用vs2005或者更高版本来调试。 ” Bingxi:“alex,你电脑里面的另外一个软件包是mysql-5.1.7的吧。” Alex:“嗯,这个版本是mysql5.1.7代码刚出来的时候进行下载的。这个版本的代码直接解压缩之后,可以直接用vs2003进行编译调试。对innodb而言,用这个版本的就可以了,innodb的变化不大,如果需要理解查询引擎,则需要使用更新的版本进行学习。” Bingxi:“mysql-5.1.7-beta-win-src.zip,这个软件包的内容,我们学了之后,会不会和linux下不一样,有人会有这样的疑问,毕竟在很多公司里面,mysql是运行在linux/unix环境的。我们知道windows与linux/unix的差异还是存在的,尤其是底层的系统函数。” Alex:“嗯,这个是很多人的疑问。其实mysql进行了代码的封装,比如在5.1.7的windows版本的代码中,也是可以看到系统函数的封装。比如event semaphore。看下对应的代码: ~~~ /************************************************************* Creates an event semaphore, i.e., a semaphore which may just have two states: signaled and nonsignaled. The created event is manual reset: it must be reset explicitly by calling sync_os_reset_event. */ os_event_t os_event_create( /*============*/                             /* out: the event handle */        const char*    name)     /* in: the name of the event, if NULL                             the event is created without a name */ { #ifdef __WIN__         os_event_t event;        event = ut_malloc(sizeof(struct os_event_struct));        event->handle = CreateEvent(NULL,/* No security attributes */                      TRUE,           /* Manual reset */                      FALSE,          /* Initial state nonsignaled */                      (LPCTSTR) name);        if (!event->handle) {                fprintf(stderr, "InnoDB: Could not create a Windows event semaphore; Windows error %lu/n",                 (ulong) GetLastError());        } #else /* Unix */        os_event_t      event;        UT_NOT_USED(name);        event = ut_malloc(sizeof(struct os_event_struct));        os_fast_mutex_init(&(event->os_mutex)); #if defined(UNIV_HOTBACKUP) && defined(UNIV_HPUX10)        ut_a(0 == pthread_cond_init(&(event->cond_var),                                    pthread_condattr_default)); #else        ut_a(0 == pthread_cond_init(&(event->cond_var), NULL)); #endif        event->is_set = FALSE;        event->signal_count = 0; #endif /* __WIN__ */         /* Put to the list of events */        os_mutex_enter(os_sync_mutex);        UT_LIST_ADD_FIRST(os_event_list, os_event_list, event);        os_event_count++;        os_mutex_exit(os_sync_mutex);        return(event); } ~~~ 在os_event_create函数体,屏蔽了系统的差异性。开发人员在开发时,需要创建event,只需要os_event_create就行了。 ” Bingxi:“alex,那么按照这个思路,是不是可以获得两个信息:1)如果需要debug系统封装函数,还是建议在linux/unix下也调试下,2)对我们查看非系统函数,在windows与linux/unix下调试,两者都是可以的。” Alex:“嗯,用哪种调试方法都是可以的。用哪个版本的也是可以的,本系列以描述innodb存储为主,因此使用5.1.7就可以了。” ## 2)搭建windows环境下的mysql5.1.7的调试环境 Bingxi:“好吧,那我们就开始搭建环境吧。” Alex:“ok,我们先将代码找一个目录进行解压缩,本文中的解压缩位置为d:/。使用vs2003打开D:/bin-mysql-5.1.7-beta/mysql.sln项目文件。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c1a8026.gif) Bingxi,打开之后会有46个project,我们要编译其中哪些工具呢?  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c1d03f0.gif) ” Bingxi:“ok,至少要包含下面三个内容:1)服务端程序,2)客户端程序,3)mysqladmin工具(用于退出调试时使用,直接使用中断调试太暴力了)。顺着这个思路,我们一步步来编译。 首先编译服务端程序,也就是编译mysqld项目。这里有三个建议,1)因为本系列主要调试mysqld的代码,因此需要将mysqld设置为启动项目,2)设置启动方式为console方法,这样可以在console窗口中看到打印信息,3)将D:/bin-mysql-5.1.7-beta下的data文件夹进行压缩保存,这样,我们需要恢复到原始的数据,直接用保存的data进行覆盖就可以了。 设置为启动项目: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c1e9b1c.gif) 设置为console启动方式:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c20f588.gif) 根据个人习惯,决定是否将data进行压缩保存。 接着,我们编译mysqld项目、mysql项目、mysqladmin项目。编译产生的工具在D:/mysql-5.1.7-beta/client_debug目录。 设置断点,比如查询的总入口是handle_select函数(在sql_select.cpp文件中)。使用“进入单步执行新实例”进行调试。如图:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c22d215.gif) 执行时,停止在main函数开始处(可以一步步看看mysql是如何启动的),我们按F5,程序会直接执行,如果断点被执行,那么就会停在断点处,因为我们此处设置的是查询函数,所以没有被执行。因此,我们需要通过客户端执行一条语句,来触发断点对应的代码被执行。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c249c2e.gif) 执行show databases命令之后,我们可以看到断点生效了。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c2612ca.gif) 这样我们通过F10/F11/F5/shift+F11等常用的快捷键进行调试了。如果需要退出调试状态,则使用mysqladmin,如图(图中打印的一行错误信息不用理它,是系统的一个bug):  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c28613d.gif) ” ## 3)调试的技巧 Alex:“bingxi,这个我理解了,有没有一些常用的技巧。Alex常用的是通过快速监控,见下面的两图。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c29f48b.gif) ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c2b9352.gif) 这样,我们可以看到变量的值。但是,遇到想测试函数,或者看宏的值就有点麻烦了。Bingxi,你给我讲讲。 ” Bingxi:“最常用的方法是直接分析算法,如果有些确实不太明白,可以通过自己写测试函数的方法进行调试。如果我想知道某一页属于哪个簇描述符,可以在fsp0fsp.c的文件尾加上我们自己的测试函数,同时设置断点: ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c2dd4a3.gif) 接着在该文件的文件头(添加内容为红色选中处),声明定义:  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c30a43b.gif) 接着找一个函数,进行调用函数test_bingxi,这里我们选择fsp_get_space_header函数,因为这个函数在同一个文件,并且启动的时候会被执行。添加调用,添加内容为红色选中处。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c32282a.gif) 然后,我们启动调试,按F5进入断点。通过j值就可以知道UNIV_PAGE_SIZE的值,如果是多个宏计算后的值,也是一样的方法。通过i1值就可以知道xdes_calc_descriptor_page(0)的返回值。  ![](https://docs.gechiui.com/gc-content/uploads/sites/kancloud/2016-07-22_5791c9c33a584.gif) 类似这样的方法,我们可以通过添加测试代码,将疑问的地方进行测试。今天调试的就说到这儿吧。 ” Alex:“ok,说完调试,开始正式进入innodb存储了。咱们也是初学者,需要多debug来解惑。今天不说晚安了,马上6点了,早安。” Bingxi:“早安。”
';