MariaDB 源码调试-Java-优质IT资源分享社区

admin
管理员
管理员
  • UID1
  • 粉丝32
  • 关注4
  • 发帖数581
  • 社区居民
  • 忠实会员
  • 原创写手
阅读:251回复:0

  MariaDB 源码调试

楼主#
更多 发布于:2016-05-30 22:13

MariaDB 源码编译

[root@jg-72 source]# pwd

/data/source

[root@jg-72 source]# ls

mariadb-10.1.11.tar.gz

先将源码压缩包解压缩

tar -zxvf mariadb-10.1.11.tar.gz

进入到BUILD子目录,它现已供给了一些一键编译的脚本

cd mariadb-10.1.11/BUILD

挑选履行 compile-amd64-debug-all

脚本,由于咱们要编译X86_64平台上带DEBUG调试信息的mysql server。

[root@jg-72 BUILD]# ./compile-amd64-debug-all

You must run this script from the MySQL top-level

directory

cd mariadb-10.1.11

BUILD/compile-amd64-debug-all

静静等候编译结束,大约几分钟

能够看到编译生成的mysqld 文件大小 261

M,而实践出产环境中运用的不带调试信息的mysqld文件,只有约89M。

client 和

extra目录下的可履行文件是编译生成的mysql自带的工具集,这儿咱们只重视mysqld。

运用编译的mysqld发动mysql实例

最简单的办法,在已装置好的mariadb的装置目录下,把mysqld用编译出来的版别替换掉即可。

可是咱们不想损坏现已装置好的用于出产的MariaDB,所以这儿咱们在它的源码目录下构建归于它自己的mysql

根底目录(mysql basedir)

实践上即是参照装置的mysql的目录结构安排一下文件即可

把编译生成的可履行文件都copy到创立的bin目录下

(下面一切操作都是在MariaDB的源代码根目录下)

[root@jg-72 mariadb-10.1.11]# mkdir bin

cp sql/mysqld bin/

cp scripts/mysqld_safe bin/

cp

sql/{add_errmsg,gen_lex_hash,gen_lex_token,mysql_tzinfo_to_sql} bin/

cp

client/{async_example,mysqlbinlog,mysql,mysqladmin,mysqlcheck,mysqldump,mysql_plugin,mysqlslap,mysql_upgrade,mysqltest,mysqlshow,mysqlimport}

bin/

cp

extra/{comp_err,my_print_defaults,mysql_waitpid,perror,replace,resolveip,resolve_stack_dump}

bin/

cp -r sql/share/ .

cp scripts/*.sql share/

创立一个 my.cnf 文件, 其间basedir 设置为MariaDB源码的根目录

[mysqld]

user=mysql

port = 3310

basedir = /data/source/mariadb-10.1.11

socket = /data/source/3310/mysql.sock

datadir = /data/source/3310/data

log-error = /data/source/3310/mysqld.err

pid-file = /data/source/3310/mysqld.pid

character-set-server=utf8

… …

然后,初始化MySQL体系数据库,能够看到,它运用咱们编译的mysqld来做初始化

[root@jg-72mariadb-10.1.11]#

scripts/mysql_install_db –defaults-file=/data/source/3310/my.cnf –user=mysql

Installing MariaDB/MySQL system tables in

‘/data/source/3310/data’ …

2016-02-25 0:58:35 139708455159584 [Note]

/data/source/mariadb-10.1.11/bin/mysqld (mysqld 10.1.11-MariaDB-debug) starting

as process 21624 …

OK

Filling help tables…

2016-02-25 0:59:00 140501225490208 [Note]

/data/source/mariadb-10.1.11/bin/mysqld (mysqld 10.1.11-MariaDB-debug) starting

as process 21664 …

OK

Creating OpenGIS required SP-s…

2016-02-25 0:59:07 140226519934752 [Note]

/data/source/mariadb-10.1.11/bin/mysqld (mysqld 10.1.11-MariaDB-debug) starting

as process 21703 …

OK

… …

发动实例:

能够看到是运用咱们编译的mysqld发动的

[root@jg-72mariadb-10.1.11]# bin/mysqld_safe

–defaults-file=/data/source/3310/my.cnf –user=mysql &

[1] 21808

[root@jg-72 mariadb-10.1.11]# ps -ef|grep mysql

|grep 3310

root 21808 19360 0 01:04 pts/5 00:00:00 /bin/sh

bin/mysqld_safe –defaults-file=/data/source/3310/my.cnf –user=mysql

mysql 22096 21808 28 01:04 pts/5 00:00:03

/data/source/mariadb-10.1.11/bin/mysqld –defaults-file=/data/source/3310/my.cnf

–basedir=/data/source/mariadb-10.1.11 –datadir=/data/source/3310/data

–plugin-dir=/usr/local/mysql/lib/plugin –user=mysql

–log-error=/data/source/3310/mysqld.err –pid-file=/data/source/3310/mysqld.pid

–socket=/data/source/3310/mysql.sock –port=3310

运用gdb调试mysql进程

gdb 衔接到要调试的进程的指令很简单,上面知道这个进程的id是22096

指令: gdb – 22096

Loaded symbols for /usr/lib64/libltdl.so.7

Reading symbols from /lib64/libfreebl3.so…(no

debugging symbols found)…done.

Loaded symbols for /lib64/libfreebl3.so

Reading symbols from /lib64/libnss_files.so.2…(no

debugging symbols found)…done.

Loaded symbols for /lib64/libnss_files.so.2

0x0000003893adf1b3 in poll () from

/lib64/libc.so.6

Missing separate debuginfos, use:

debuginfo-install bzip2-libs-1.0.5-7.el6_0.x86_64 glibc-2.12-1.149.el6.x86_64

libaio-0.3.107-10.el6.x86_64 libgcc-4.4.7-11.el6.x86_64

libstdc++-4.4.7-11.el6.x86_64 libtool-ltdl-2.2.6-15.5.el6.x86_64

libxml2-2.7.6-14.el6_5.2.x86_64 nss-softokn-freebl-3.14.3-17.el6.x86_64

snappy-1.1.0-1.el6.x86_64 unixODBC-2.2.14-14.el6.x86_64

xz-libs-4.999.9-0.5.beta.20091007git.el6.x86_64 zlib-1.2.3-29.el6.x86_64

(gdb)

最终会看到如上的输出,此刻这个mysql进程现已被gdb挂起,用客户端衔接是没有响应的。

设置函数断点,这么当mysql进程履行到这个函数的时分,就会被gdb捕获到而且停在函数的入口处。

(gdb) b dict_index_too_big_for_tree

Breakpoint 1 at 0xdd291b: file

/data/source/mariadb-10.1.11/storage/xtradb/dict/dict0dict.cc, line 2390.

(gdb)

输入c 指令,让程序正常运转

(gdb) c

Continuing.

此刻在另一个终端运用mysql客户端进行衔接

[root@jg-72 mariadb-10.1.11]# ./bin/mysql -uroot

-h168.168.207.72 -P3310

Welcome to the MariaDB monitor. Commands end with

; or \g.

Your MariaDB connection id is 2

Server version: 10.1.11-MariaDB-debug Source

distribution

Copyright (c) 2000, 2015, Oracle, MariaDB

Corporation Ab and others.

Type ‘help;’ or ‘h’ for help. Type ‘c’ to clear

the current input statement.

MariaDB [(none)]>

咱们能够正常的履行一些SQL

MariaDB [(none)]> use test;

Database changed

MariaDB [test]> show variables like

‘char%';

+————————–+———————————————-+

| Variable_name | Value |

+————————–+———————————————-+

| character_set_client | utf8 |

| character_set_connection | utf8 |

| character_set_database | utf8 |

| character_set_filesystem | binary |

| character_set_results | utf8 |

| character_set_server | utf8 |

| character_set_system | utf8 |

| character_sets_dir |

/data/source/mariadb-10.1.11/share/charsets/ |

+————————–+———————————————-+

8 rows in set (0.00 sec)

测验创立一个表,发现hang住了,

MariaDB [test]> create table testtable ( c1

varchar(100), c2 varchar(100), c3 varchar(100));

切换到gdb地点的终端,

能够看到,gdb在设置断点的函数的入口处停了一下,等候咱们单步调试

L 指令能够看接下来10行的代码,后边能够跟数字

l 100即是看100行代码, l -10 即是看上面10行的代码

单步调试的指令是 n,也即是一行行履行代码(next)

想看某个变量的值,运用 p 指令(print)

在履行到某函数调用途,比方上图的2398行,也能够运用 s

(step)指令,这个时分会进入被调用的函数dict_table_is_comp()内部,持续单步履行

comp = 1这说明,当时创立的表的格局是 compact的。

咱们这个表有3个nullable的列,每条记载的长度约束,page_rec_max 是

8126,是经过

2425行的函数核算出来的。

接下来进入核算每一个字段占用空间的核算。这儿咱们看到,

这个表总共有 6 个

fields,而不是咱们界说的三个,这是由于,mysql隐含的会增加三个字段

Records in the clustered index contain fields for

all user-defined columns. In addition, there is a 6-byte transaction ID field

and a 7-byte roll pointer field.

If no primary key was defined for a table, each

clustered index record also contains a 6-byte row ID field.

咱们用 p 看第一个field的信息,确实是 ROW_ID,长度确实是6 byte,和

文档相互印证。

关于隐含列的处理,咱们也许不关心,所以想直接越过接下来的代码,那能够设置一个行断点,

b 指令不加参数即是在当时行设置断点, b 即是在当时文件的指定行设置行断点, b :

即是在指定文件的指定行设置行断点

接下来 continue,咱们发现它运转到

2451行后再次停下,说明循环进入了第2次迭代(i现已成为1)

能够看到,第二个字段也是默许增加的 TRX_ID 列,长度是6个字节,

第三个字段是默许增加的ROLL_PTR列,长度为7字节

能够预期,接下来即是咱们建表的实在的字段信息。

假如想看当时程序的函数调用栈,能够运用 bt 或许where 指令

Detach指令使gdb释放对mysql进程的衔接,quit指令退出gdb

如何从MySQL的一条报错信息定位到源代码

以下面这个疑问举例:

297个字段 varchar(73)字段的表

create table t_f73_ag_xjllb_yh

(

jydm VARCHAR(73),

rq VARCHAR(73),

cb VARCHAR(73),

khdkjdkjjse VARCHAR(73),

xyhjkjzje VARCHAR(73),

… …

xjdjwdqmye_yoy VARCHAR(73),

jxjdjwdqcye_yoy VARCHAR(73),

xjjxjdjwjzje1_yoy VARCHAR(73)

);

报如下过错

ERROR 1118 (42000): Row size too large (>

8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or

ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes

is stored inline.

在源码根目录查找音讯的部分内容

[root@jg-72 mariadb-10.1.11]# find * |xargs grep

‘Row size too large’

输出许多,可是咱们应当要点重视源代码和文本文件,所以

share/errmsg-utf8.txt

sql/share/errmsg-utf8.txt

storage/innobase/handler/ha_innodb.cc

storage/xtradb/handler/ha_innodb.cc

这几个文件需求要点重视

由于MariaDB的INNODB存储引擎实践上是xtraDB,所以先看

持续寻觅这个函数调用的当地

jydm VARCHAR(73),

rq VARCHAR(73),

cb VARCHAR(73),

khdkjdkjjse VARCHAR(73),

xyhjkjzje VARCHAR(73),

… …

xjdjwdqmye_yoy VARCHAR(73),

jxjdjwdqcye_yoy VARCHAR(73),

xjjxjdjwjzje1_yoy VARCHAR(73)

);

报如下过错

ERROR 1118 (42000): Row size too large (>

8126). Changing some columns to TEXT or BLOB or using ROW_FORMAT=DYNAMIC or

ROW_FORMAT=COMPRESSED may help. In current row format, BLOB prefix of 768 bytes

is stored inline.

在源码根目录查找音讯的部分内容

[root@jg-72 mariadb-10.1.11]# find * |xargs grep

‘Row size too large’

输出许多,可是咱们应当要点重视源代码和文本文件,所以

share/errmsg-utf8.txt

sql/share/errmsg-utf8.txt

storage/innobase/handler/ha_innodb.cc

storage/xtradb/handler/ha_innodb.cc

这几个文件需求要点重视

由于MariaDB的INNODB存储引擎实践上是xtraDB,所以先看

storage/xtradb/handler/ha_innodb.cc

发现两个要害的宏界说 ER_TOO_BIG_ROWSIZE

和DB_TOO_BIG_RECORD

从errmsg-utf8.txt文件的内容ER_TOO_BIG_ROWSIZE 42000

和过错音讯头 1118 (42000),能够推测出ER_TOO_BIG_ROWSIZE 应当即是过错音讯号

1118,可是errmsg-utf.txt中的1118过错的内容,不包括 (> xxx),进一步发现,包括这个过错内容的只有上面的

ha_innodb.cc的2058行, 函数是 convert_error_code_to_mysql()

持续寻觅这个函数调用的当地

find * |xargs grep

‘convert_error_code_to_mysql’

发现,全部都在ha_innodb.cc 文件中(大约有几十处调用)

经过遍历这个文件调用这个函数的当地,能够看到许多当地,error号都是经过

row_create_index_for_mysql()函数的回来值得到,咱们更是找到一个函数

这儿,首要需求知道的一个概念

从文档中能够知道,InnoDB的表是一种索引安排表,也即是说,它的表实践上即是索引(clustered

index),他的索引也即是表,它的clustered index实践上包括了一切用户界说的字段。

别的,能够在表的选定的字段上创立二级索引,假如表中界说了主键,那么二级索引将隐含的包括主键这一字段。

The data in each InnoDB table is divided into

pages. The pages that make up each table are arranged in a tree data structure

called a B-tree index. Table data and secondary indexes both use this type of

structure. The B-tree index that represents an entire table is known as the

clustered index, which is organized according to the primary key columns. The

nodes of the index data structure contain the values of all the columns in that

row (for the clustered index) or the index columns and the primary key columns

(for secondary indexes).

所以,根本能够判定,这个函数create_clustered_index_when_no_primary()实践上即是咱们创立一个不包括主键的InnoDB表首要要调用的函数。而它的首要作业,在

row_create_index_for_mysql()中完结。

相同的方法,在storage/innobase/row/row0mysql.cc中找到

row_create_index_for_mysql() 的界说

经过大约阅读代码知道这个函数会调用que_run_threads()去做实践的作业。

找到它的界说处 storage/xtradb/que/que0que.cc

1183,发现它实践调用的是

que_run_threads_low(), 这个也界说在相同的文件里,进入这个函数

在同一个文件里,咱们也找到que_thr_step()函数的界说,实践上能够推测出,它里面会依据不一样的句子调用不一样的入口函数

很走运,它用了老土的 if else条件判别,而不是函数指针,所以能够知道调用了

dict_create_index_step()

这儿也也许是dict_create_table_step()呀?

一方面,咱们是从create_index一路跟下来的,

另一方面,不放心的话,搜一下 QUE_NODE_CREATE_INDEX这个type,

发现是在storage/xtradb/dict/dict0crea.cc

ind_create_graph_create()函数中赋值的,而这个函数,

row_create_index_for_mysql()在调用que_run_threads()之前调用了,所以,能够断定,接下来履行的函数是dict_create_index_step()

在storage/innobase/dict/dict0crea.cc 中找到这个函数的界说

这个时分发现这个函数真TM长,分了好几个期间,

它调用的函数有

dict_build_index_def_step()

dict_build_field_def_step()

dict_index_add_to_cache()

dict_index_get_if_in_cache_low()

dict_create_index_tree_step()

一个个函数跟进去相当于测验好几条岔道,通常这种情况,每个函数都跟进去会掉进无限的调用圈套里,明显不是个好办法。

所以回到最开端,想办法自底向上断定函数调用栈,之前是查找ER_TOO_BIG_ROWSIZE从上往下找,如今查找

DB_TOO_BIG_RECORD 这个要害字

要点重视其间回来 DB_TOO_BIG_RECORD的函数

在storage/xtradb/btr/btr0cur.cc

文件中,几处回来DB_TOO_BIG_RECORD的函数从姓名看不是update即是insert,明显不是咱们的create table

所以在storage/xtradb/dict/dict0dict.cc

中,发现回来DB_TOO_BIG_RECORD的函数

恰好是dict_index_add_to_cache(),

是被dict_create_index_step()调用的函数之一。

到此,根本能够断定,最终经过函数dict_index_too_big_for_tree()来查看创立一个innodb表是不是会超出innodb表的一些长度约束。

经过前面的gdb调试中的bt指令,也能够印证这一点,能够看到函数调用的仓库

Create innodb table without primary key …

->create_clustered_index_when_no_primary()

->row_create_index_for_mysql()

->que_run_threads()

->que_run_threads_low()

->que_thr_step()

->dict_create_index_step()

->dict_index_add_to_cache()

->dict_index_too_big_for_tree()

Tip:

当然,这个查找进程能够不经过指令行,也能够运用一些工具,比方sourceinsight把全部mariadb的源代码导入,然后在sourceinsight里履行相似的查找进程。Source

Insight的装置运用这儿就不说了。

优质IT资源分享社区为你提供此文。

本站有大量优质Java教程视频,资料等资源,包含java基础教程,高级进阶教程等等,教程视频资源涵盖传智播客,极客学院,达内,北大青鸟,猎豹网校等等IT职业培训机构的培训教学视频,价值巨大。欢迎点击下方链接查看。

java教程视频

优质IT资源分享社区(www.itziyuan.top)
一个免费,自由,开放,共享,平等,互助的优质IT资源分享网站。
专注免费分享各大IT培训机构最新培训教学视频,为你的IT学习助力!

!!!回帖受限制请看点击这里!!!
!!!资源失效请在此版块发帖说明!!!

[PS:按 CTRL+D收藏本站网址~]

——“优质IT资源分享社区”管理员专用签名~

本版相似帖子

游客