从零开始学习MySQL调试跟踪

Posted GreatSQL

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始学习MySQL调试跟踪相关的知识,希望对你有一定的参考价值。

  • GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源。
  • GreatSQL是MySQL的国产分支版本,使用上与MySQL一致。
  • 作者: Yejinrong/叶金荣
  • 文章来源:GreatSQL社区投稿

    1. 编译GreatSQL
    1. 安装gdb
    1. 开始调试GreatSQL源码
    • 3.1 利用gdb设置断点
    • 3.2 使用 Trace 文件调试

有时为了跟踪故障需要调试MySQL/GreatSQL源码,本文介绍如何在Linux下构建MySQL/GreatSQL源码调试环境。

在这之前,我也是一名小白,一起从零开始探索吧。

本文以CentOS 8.x环境下的GreatSQL 8.0.25-16版本为例。

1. 编译GreatSQL

查看系统环境:

$ cat /etc/system-release

CentOS Linux release 8.4.2105

首先,从https://gitee.com/GreatSQL/GreatSQL/releases/ 下载GreatSQL 8.0.25-16的源码包

  1. Source Code
Packages Size
greatsql-8.0.25-16.tar.gz 503M

接下来,参考文章 在Linux下源码编译安装GreatSQL 构建好编译环境。然后开始编译GreatSQL源码,编译参数中增加/修改debug相关选项,这样编译后得到的二进制文件才能支持调试模式,例如:

$ cd /opt/greatsql-8.0.25-16
$ mkdir -p build
$ cd build
$ cmake3 .. \\
-DBOOST_INCLUDE_DIR=/opt/boost_73_0 \\
-DLOCAL_BOOST_DIR=/opt/boost_73_0 \\
-DCMAKE_INSTALL_PREFIX=/usr/local/GreatSQL-8.0.25-16-Linux-glibc2.28-x86_64 \\
-DWITH_ZLIB=bundled \\
-DWITH_NUMA=ON \\
-DCMAKE_EXE_LINKER_FLAGS="-ljemalloc" \\
-DBUILD_CONFIG=mysql_release \\
-DWITH_TOKUDB=OFF \\
-DWITH_ROCKSDB=OFF \\
-DMAJOR_VERSION=8 \\
-DMINOR_VERSION=0 \\
-DPATCH_VERSION=25 \\
-DWITH_UNIT_TESTS=OFF \\
-DWITH_NDBCLUSTER=OFF \\
-DWITH_SSL=system \\
-DWITH_SYSTEMD=ON \\
-DWITH_LDAP=OFF \\
-DWITH_AUTHENTICATION_LDAP=OFF \\
-DWITH_DEBUG=1 \\
-DCMAKE_BUILD_TYPE=Debug \\
&& make -j8 VERBOSE=1 && make install

主要是增加两个参数 -DWITH_DEBUG=1-DCMAKE_BUILD_TYPE=Debug,注意不要有参数 -DCMAKE_BUILD_TYPE=RelWithDebInfo

编译完成后,即可得到包含debug功能的GreatSQL二进制文件,执行下面的命令检查:

$ cd /usr/local/GreatSQL-8.0.25-16-Linux-glibc2.28-x86_64
$ ./bin/mysqld-debug --verbose --version

/usr/local/GreatSQL-8.0.25-16-Linux-glibc2.28-x86_64/bin/mysqld-debug  Ver 8.0.25-16-debug for Linux on x86_64 (Source distribution)

可以看到,输出的结果中包含 debug 关键字,这就表示成功了。

2. 安装gdb

直接执行yum安装gdb即可:

$ yum install -y gdb
$ gdb --version
GNU gdb (GDB) Red Hat Enterprise Linux 9.2-4.el8
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.

gdb常用的调试相关指令有以下几个:

命令 缩写 备注
attach 挂接/进入准备调试的进程pid
detach 取消挂接进程(退出进程)
list l 显示多行源代码
break b 设置断点,程序运行到断点的位置会停下来
info i 描述程序的状态
run r 开始运行程序
display disp 跟踪查看某个变量,每次停下来都显示它的值
step s 执行下一条语句,如果该语句为函数调用,则进入函数执行其中的第一条语句
next n 执行下一条语句,如果该语句为函数调用,不会进入函数内部执行(即不会一步步地调试函数内部语句)
print p 打印内部变量值
continue c 继续程序的运行,直到遇到下一个断点
set var name=v 设置变量的值
start st 开始执行程序,在main函数的第一条语句前面停下来
file 装入需要调试的程序
kill k 终止正在调试的程序
watch 监视变量值的变化
backtrace bt 查看函数调用信息(堆栈)
frame f 查看栈帧
quit q 退出gdb

3. 开始调试GreatSQL源码

第一次运行gdb准备调试时,可能会提示类似下面的信息

warning: Unable to find libthread_db matching inferior\'s thread library, thread debugging will not be available.
0x00007ffb358ada41 in poll () from /lib64/libc.so.6
Missing separate debuginfos, use: dnf debuginfo-install keyutils-libs-1.5.10-9.el8.x86_64 ...

这表示缺少一些相关的debuginfo包,可以根据提示内容补充安装,例如:

dnf debuginfo-install keyutils-libs-1.5.10-9.el8.x86_64 ...

如果提示找不到这些安装包:

Could not find debuginfo package for the following installed packages: keyutils-libs-1.5.10-9.el8.x86_64 ...

可以检查yum配置文件 /etc/yum.repos.d/CentOS-Linux-Debuginfo.repo,确认是否设置了 enable = 1,例如:

# CentOS-Linux-Debuginfo.repo
#
# All debug packages are merged into a single repo, split by basearch, and are
# not signed.

[debuginfo]
name=CentOS Linux $releasever - Debuginfo
baseurl=http://debuginfo.centos.org/$releasever/$basearch/
gpgcheck=1
enabled=1    #<---这里要设置1
gpgkey=file:///etc/pki/rpm-gpg/RPM-GPG-KEY-centosofficial

此外,还要把GreatSQL 8.0.25-16的源码包解压缩到 /opt 目录下:

$ tar zxf PATH/greatsql-8.0.25-16.tar.gz -C /opt/

接下来,演示如何跟踪调试。

先初始化GreatSQL数据文件,然后再启动GreatSQL服务进程:

# 初始化GreatSQL
$ cd /usr/local/GreatSQL-8.0.25-16-Linux-glibc2.28-x86_64
$ ./bin/mysqld-debug --no-defaults --initialize-insecure --user=mysql --datadir=./data

# 启动GreatSQL
$ ./bin/mysqld-debug --no-defaults --user=mysql --datadir=./data1 &

# 查看进程pid
$ ps -ef | grep mysqld
...
mysql    2644322 2542442  3 14:38 pts/7    00:00:01 ./bin/mysqld-debug --no-defaults --user=mysql --datadir=./data1

# 在另一个终端(终端#2),连入GreatSQL
$ mysql -S/tmp/mysql.sock
Welcome to the MySQL monitor.  Commands end with ; or \\g.
Your MySQL connection id is 7
Server version: 8.0.25-16-debug Source distribution
...
mysql>\\s
...
Server version:         8.0.25-16-debug Source distribution
...

启动gdb,准备调试跟踪GreatSQL,我们分别演示几种不同方式。

3.1 利用gdb设置断点

终端#1 中启动gdb,并挂接GreatSQL进程,准备跟踪

$ gdb -p 2644322
GNU gdb (GDB) Red Hat Enterprise Linux 9.2-4.el8
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
...
Attaching to process 2644322
[New LWP 2643482]
[New LWP 2643483]
[New LWP 2643484]
...
[New LWP 2643522]
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib64/libthread_db.so.1".
--Type <RET> for more, q to quit, c to continue without paging--  #<-- 这里按下回车,即可进入
0x00007fb7ae93ba41 in __GI___poll (fds=0x7fb7ae229140, nfds=2, timeout=-1) at ../sysdeps/unix/sysv/linux/poll.c:29
29        return SYSCALL_CANCEL (poll, fds, nfds, timeout);
(gdb)
(gdb) p mysql_sysvar_version  #<-- 打印变量,查看GreatSQL版本号
$1 = flags = 68101, name = 0x7f10d1c6cc90 "innodb_version", comment = 0x6c47f92 "InnoDB version", check = 0x37dd9e2
     <check_func_str(THD*, SYS_VAR*, void*, st_mysql_value*)>, update = 0x37ddeb0 <update_func_str(THD*, SYS_VAR*, void*, void const*)>,
  value = 0x7e7c768 <innodb_version_str>, def_val = 0x6c38440 "8.0.25-15"
(gdb) 
(gdb) 
(gdb) b mysql_execute_command  #<--- 输入指令"b dispatch_command"设置断点,意为当GreatSQL程序运行到这个函数时,就会停下来
Breakpoint 3 at 0x379c3f2: file /opt/greatsql-8.0.25-16/sql/sql_parse.cc, line 2875.
(gdb)

切换到 终端#2,随便执行一条SQL命令:

mysql> select \'debug\' from dual;

回到 终端#1,继续调试:

(gdb) 
(gdb) bt  #<-- 打印函数调用信息
#0  dispatch_command (thd=0x7f10a3a0b000, com_data=0x7f10d12a7370, command=COM_QUERY) at /opt/greatsql-8.0.25-16/sql/sql_parse.cc:1605
#1  0x0000000003797c48 in do_command (thd=0x7f10a3a0b000) at /opt/greatsql-8.0.25-16/sql/sql_parse.cc:1388
#2  0x0000000003991168 in handle_connection (arg=0x7f10d1f9d120) at /opt/greatsql-8.0.25-16/sql/conn_handler/connection_handler_per_thread.cc:307
#3  0x00000000052e4b22 in pfs_spawn_thread (arg=0x7f10e8a45660) at /opt/greatsql-8.0.25-16/storage/perfschema/pfs.cc:2899
#4  0x00007f10eb1e917a in start_thread (arg=<optimized out>) at pthread_create.c:479
#5  0x00007f10e9128dc3 in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:95
(gdb)
(gdb) p thd->m_query_string  #<-- 打印SQL语句
$14 = str = 0x7f10a3a0e828 "select \'debug\' from dual", length = 24
(gdb)
(gdb) c  #<-- 继续执行,终端#2里被阻塞的SQL语句就可以执行了
Continuing.

切回 终端#2 查看SQL语句执行结果:

mysql> select \'debug\' from dual;
+-------+
| debug |
+-------+
| debug |
+-------+
1 row in set (12 min 11.55 sec)

可以看到,因为一直被阻塞,这条SQL请求耗时超过12分钟。当 终端#2 的连接断开退出后,可以看到gdb端也有相应提示:

Thread 39 "mysqld-debug" hit Breakpoint 1, dispatch_command (thd=0x7f10a3a0b000, com_data=0x7f10d12a7370, command=COM_QUIT)
    at /opt/greatsql-8.0.25-16/sql/sql_parse.cc:1605
1605      bool error = false;
(gdb)

如果不想继续跟踪调试了,只需输入指令 qquit 即可退出gdb。

(gdb) quit
A debugging session is active.

        Inferior 1 [process 2644322] will be detached.

Quit anyway? (y or n) y
Detaching from program: /usr/local/GreatSQL-8.0.25-16-Linux-glibc2.28-x86_64/bin/mysqld-debug, process 2644322
[Inferior 1 (process 2644322) detached]

3.2 使用 Trace 文件调试

还可以在GreatSQL客户端中设置变量 debug 为不同值,就可以输出GreatSQL运行过程中涉及的调用模块、函数、状态信息等全部信息,并记录到本地文件中。用法示例:

mysql> SET SESSION debug = \'debug_options\';

变量 debug 支持多种设置模式:

debug_options = field_1:field_2:...:field_N
field = [+|-]flag[,modifier,modifier,...,modifier]

+, - 表示从当前debug值添加或者减少某些选项。

flag相关可选项如下:

flag 说明
d 开启DBUG
f 只跟踪指定的函数
F 跟踪指定的源码文件
i 跟踪指定的线程
L 跟踪指定的源码行数
n 打印函数调用层次序号
N 输出日志从0开始打印行号
o 指定输出到某个文件
O 类似o,每次写文件都会flush,reopen
P 匹配DBUG_PROCESS
p 打印process name
t 打印函数调用和退出

使用案例1(精简模式)

# 设置debug选项
mysql> set session debug=\'d:t:o,/tmp/mysqld.trace\';

# 执行SQL请求
mysql> select \'debug\' from dual;
+-------+
| debug |
+-------+
| debug |
+-------+
1 row in set (0.00 sec)

查看生成的trace文件:

$ cat /tmp/mysqld.trace
...
>do_command
| >THD::clear_error
| <THD::clear_error
| >Diagnostics_area::reset_diagnostics_area
| <Diagnostics_area::reset_diagnostics_area
| >my_net_set_read_timeout
| | enter: timeout: 28800
| | >vio_socket_timeout
| | <vio_socket_timeout
| <my_net_set_read_timeout
| >vio_is_blocking
| <vio_is_blocking
| >net_read_raw_loop
| | >vio_read
| | | >vio_is_blocking
| | | <vio_is_blocking
| | | >vio_io_wait
| | | <vio_io_wait
| | <vio_read
| <net_read_raw_loop
| THD::enter_stage: \'starting\' /opt/greatsql-8.0.25-16/sql/conn_handler/init_net_server_extension.cc:102
...

使用案例2(复杂模式)增加了打印文件名和行号等信息,更方便定位查找。

mysql> set session debug=\'d:t:L:F:o,/tmp/mysqld.trace\';
mysql> select \'debug\' from dual;
...

查看生成的trace文件:

$ cat /tmp/mysqld.trace
...
  sql_parse.cc: <do_command
  sql_parse.cc:  1269: >do_command
   sql_class.h:  3287: | >THD::clear_error
   sql_class.h: | <THD::clear_error
  sql_error.cc:   357: | >Diagnostics_area::reset_diagnostics_area
  sql_error.cc: | <Diagnostics_area::reset_diagnostics_area
   net_serv.cc:  2246: | >my_net_set_read_timeout
   net_serv.cc:  2247: | | enter: timeout: 28800
  viosocket.cc:   380: | | >vio_socket_timeout
  viosocket.cc: | | <vio_socket_timeout
   net_serv.cc: | <my_net_set_read_timeout
  viosocket.cc:   373: | >vio_is_blocking
  viosocket.cc: | <vio_is_blocking
   net_serv.cc:  1341: | >net_read_raw_loop
  viosocket.cc:   169: | | >vio_read
  viosocket.cc:   373: | | | >vio_is_blocking
  viosocket.cc: | | | <vio_is_blocking
  viosocket.cc:  1118: | | | >vio_io_wait
  viosocket.cc: | | | <vio_io_wait
  viosocket.cc: | | <vio_read
   net_serv.cc: | <net_read_raw_loop
  sql_parse.cc:   320: | THD::enter_stage: \'starting\' /opt/greatsql-8.0.25-16/sql/conn_handler/init_net_server_extension.cc:102
...

本文简单演示了如何跟踪调试GreatSQL的几种方法,更多有趣实用的方法还有待进一步挖掘,一起探索新世界吧。

P.S,我也在MacOS环境下构建了基于vscode的跟踪调试环境,但还是更喜欢在Linux终端命令行模式下工作,所以本文没介绍如何利用vscode跟踪调试,有兴趣的读者可以根据其他资料自行构建。


Enjoy GreatSQL

Java从零开始学习——Java基础

一、IDE的应用

IDE(Integrated Development Environment)集成开发环境是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。

 

快捷代码

  1. psvm   == public static void main(String[] args)

  2. sout     == System.out.println();

  3. ctrl + D     == 复制当前行到下一行

 

二、基础语法

注释

  1. 单行注释

    //

  2. 多行注释

    /*

    */

  3. 文档注释

    /**

    */


//新建空项目的时候,需要配置项目框架,选择Java版本


 

标识符

关键字

技术图片

 

标识符使用注意点

技术图片

 

三、数据类型

Java为强类型语言,要求变量的使用要严格符合规定,所有变量都必须先定义后才能使用。

两大数据类型

基本类(primitive type)

数值类型——整数型——byte 占1字节:-128~127

         ——short 占2字节:-32768~32767

         ——int 占4字节:-2147483648~2147483647

         ——long 占8字节:-9223372036854775808~9223372036854775807

    ——浮点型——float 占4字节

         ——double 占8字节

    ——字符型——char 占2字节

布尔类型(boolean)   ——占1位,其值只有true和false

 

引用类(reference type)

  1. 类(如String、Integer)

  2. 接口

  3. 数组

 

技术图片


整数拓展

十进制

二进制(0b)

八进制(0)

十六进制(0x)


浮点数扩展

1 float  f = 0.1f;
2 double d = 1.0/10;
3 System.out.println(f==d);//false
4 ?
5 //float 有限、离散、舍入误差、大约、接近但不等于,最好避免使用浮点数进行比较
6 float a = 123123123123123f;
7 float b = a + 18 System.out.println(a==b);//true

 


字符型扩展

 1 char c1 = ‘a‘;
 2 System.out.println((int)c1);//97
 3 char c2 = ‘中‘;
 4 System.out.println((int)c2));//20013
 5 //所有字符的本质还是数值
 6 //编码 Unicode 表(97 = a;65 = A) 2字节 
 7 //0 ~ 65536;U0000 ~ UFFFF
 8 ?
 9 //转义字符
10 // 	制表符
11 // 
换行
12 System.out.println("Hello	World");

 


布尔值扩展

1 boolean flag = true;
2 if(flag == true){}
3 if(flag){}
4 //效果一样,下方代码更精简

 


其他扩展

1 String s1 = new String(original:"Hello World");
2 String s2 = new String(original:"Hello World");
3 System.out.println(s1==s2);//false
4 ?
5 String s3 = "Hello World";
6 String s4 = "Hello World";
7 System.out.println(s3==s4);//true
8 //对象要从内存分析

 

类型转换

 1 int i = 128;
 2 byte b = (byte)i;//内存溢出
 3 System.out.println(i);//128
 4 System.out.println(b);//-128
 5 ?
 6 //高转低,强制转换
 7 //低转告,自动转换
 8 double d = i;
 9 System.out.println(b);//128.0
10 ?
11 /*
12     1.不能对布尔值进行转换
13     2.不能把对象类型转换为不相干的类型
14     3.在把高容量转换成低容量时,强制转换(存在内存溢出和精度问题)
15     4.在把低容量转换成高容量时,自动转换
16 */
17 System.out.println((int)12.3);//12
18 System.out.println((int)12.34f);//12
19 ?
20 //JDK7特性,数字之间可以用下划线分割
21 //int 范围正负20E
22 int i = 1000_000_000;
23 System.out.println(i);//1000000000
24 ?
25 int a = 20;
26 int total = i * a;          //内存溢出
27 long total2 = i * a;        //内存溢出,赋值之前就已经出问题
28 long total3 = i * ((long)a);//a转换成long以后,赋值就以其为类型

 

四、变量、常量

技术图片

变量的三要素

  1. 变量名

  2. 变量类型

  3. 作用域

    类变量 //static

    实例变量 //变量类型 变量名字 = new HelloWorld();

    局部变量 //写在方法里

 

变量是特殊的常量

通过final这个修饰词来定义(修饰符不分先后顺序)

1 //静态常量
2 static final double PI = 3.14;

 

变量的命名规范

技术图片

 

五、运算符

技术图片

算术运算符

 1 int a = 10;
 2 int b = 20;
 3 System.out.println(a/b);//0,两者都是int类型,结果会有精度损失
 4 System.out.println(a/(double)b);/0.5
 5 long  a = 123456789123L;
 6 int   b = 123;
 7 short c = 10;
 8 byte  d = 8;
 9 System.out.println(a+b+c+d);//Long,有Long就是Long(有Double同理)
10 System.out.println(b+c+d);  //Int,没Long就是Int
11 System.out.println(c+d);    //Int

 

关系、逻辑运算符

1 int a = 3;
2 boolean b = (a < 2)&&(a++ < 4);
3 System.out.println(b);
4 System.out.println(a);

 

位运算

 1 /**
 2 0000 0000   0
 3 0000 0001   1
 4 0000 0010   2
 5 0000 0100   4
 6 0000 1000   8
 7 0001 0000   16
 8 ?
 9 A = 0011 1100;
10 B = 0000 1101;
11 ?
12 A&B = 0000 1100;
13 A|B = 0011 1101;
14 A^B = 0011 0001;
15 ~B  = 1111 0010;
16 ?
17 2*8如何计算最快,2*2*2*2
18 ?
19 << *2
20 >> /2
21 ?
22 2<<3 == 16;
23 */
24 int a = 10;
25 int b = 30;
26 //加号两侧有String类型的,则将另一侧转换为字符串类型进行拼接
27 System.out.println(""+a+b);//1030
28 //先运算前面的,然后再拼接
29 System.out.println(a+b+"");//30
30 //三元运算符
31 int score = 80;
32 //x ? y : z     如何x为ture,则y,否则z
33 String type = score < 60 ? "不及格" : "及格";

 

六、包(Package)

建立分级目录

建分级目录时(com.kevin.www),取消项目右上角的compact middle packlages,即可实现。

包命名规则

一般利用公司域名倒置作为包名。

使用import语句导入需要使用的包。

1 //导入路径下所有包文件
2 import com.kevin.www.*;

 

七、JavaDoc

1 /**
2  * @author Kevin    作者
3  * @version 1.0     版本号
4  * @since 1.8       最早使用的JDK版本
5  * @param name      参数名
6  * @return          返回值情况
7  * @throws Exception异常抛出情况
8  */

利用命令行生成文档

cmd中输入javadoc -encoding UTF-8 -charset UTF-8 name.java

可以生成一份网页文档,可以查看其中内容

 

利用IDEA生成文档

Tools-> Generate JavaDoc,打开 javadoc 文档配置页面

选择输出目录并添加参数 -encoding UTF-8 -charset UTF-8即可。

 

 

以上是关于从零开始学习MySQL调试跟踪的主要内容,如果未能解决你的问题,请参考以下文章

从零开始学习MySQL调试跟踪

从零开始学习MySQL调试跟踪

当回溯从零开始时如何调试崩溃

学习笔记GAN001:生成式对抗网络,只需10步,从零开始到调试

Java从零开始学习——Java基础

Java从零开始学习——Java基础