惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

F
Full Disclosure
Stack Overflow Blog
Stack Overflow Blog
H
Help Net Security
Microsoft Azure Blog
Microsoft Azure Blog
The GitHub Blog
The GitHub Blog
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
cs.AI updates on arXiv.org
cs.AI updates on arXiv.org
Recorded Future
Recorded Future
Y
Y Combinator Blog
Cloudbric
Cloudbric
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
S
Schneier on Security
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
C
Cybersecurity and Infrastructure Security Agency CISA
TaoSecurity Blog
TaoSecurity Blog
Security Latest
Security Latest
N
News and Events Feed by Topic
Hacker News - Newest:
Hacker News - Newest: "LLM"
D
DataBreaches.Net
Security Archives - TechRepublic
Security Archives - TechRepublic
H
Hacker News: Front Page
C
Cisco Blogs
L
LangChain Blog
aimingoo的专栏
aimingoo的专栏
Recent Commits to openclaw:main
Recent Commits to openclaw:main
V
Vulnerabilities – Threatpost
L
LINUX DO - 最新话题
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
Google Online Security Blog
Google Online Security Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
A
About on SuperTechFans
www.infosecurity-magazine.com
www.infosecurity-magazine.com
Jina AI
Jina AI
C
CXSECURITY Database RSS Feed - CXSecurity.com
Schneier on Security
Schneier on Security
T
Tenable Blog
N
News and Events Feed by Topic
W
WeLiveSecurity
有赞技术团队
有赞技术团队
AI
AI
爱范儿
爱范儿
大猫的无限游戏
大猫的无限游戏
Latest news
Latest news
T
The Blog of Author Tim Ferriss
S
Security Affairs
Know Your Adversary
Know Your Adversary
Simon Willison's Weblog
Simon Willison's Weblog
G
GRAHAM CLULEY
Google DeepMind News
Google DeepMind News
The Cloudflare Blog

董泽润的技术笔记

美图 kv 存储 titan | 董泽润的技术笔记 聊聊并发库 conc | 董泽润的技术笔记 浅析 redis lua 实现 | 董泽润的技术笔记 IO Pipeline 读 Minio 源码 聊聊最近基于 S3 的项目 | 董泽润的技术笔记 为什么泛型使你的程序变慢 | 董泽润的技术笔记 每个 gopher 都需要了解的 Go AST 小技巧!Wireshark 让调试 GRPC 不再困难 | 董泽润的技术笔记 小技巧!Mac 环境下编译 Go 服务 | 董泽润的技术笔记 小技巧!k8s 环境下调试服务 | 董泽润的技术笔记 小技巧!如何用 systemtap 排查问题 | 董泽润的技术笔记 小白的 redis 1 安全漏洞 | 董泽润的技术笔记 聊聊时钟源为什么会影响性能 | 董泽润的技术笔记 聊聊为什么 IDL 只能扩展字段而非修改 | 董泽润的技术笔记 什么是 Pause 容器 | 董泽润的技术笔记 弱智的 MySQL NULL | 董泽润的技术笔记 分享一个 UT failed 引出的思考 | 董泽润的技术笔记 聊聊如何做技术分享 | 董泽润的技术笔记 聊聊 Go 并发安全 | 董泽润的技术笔记
新手如何调试 MySQL | 董泽润的技术笔记
2022-05-18 · via 董泽润的技术笔记

前几天看到姜老师的旧文用 VSCode 编译和调试 MySQL,每个 DBA 都应 get 的小技能, 文末留了一个思考题,如何修改源码,自定义版本,使得 select version() 输出自定义内容

调试过程参考macOS VSCode 编译调试 MySQL 5.7

内部 Item 对象参考从SQL语句到MySQL内部对象

源码面前没有秘密,建义对 DB 感兴趣的尝试 debug 调试。本文环境为 mac + vscode + lldb

依赖及插件

vscode 插件:

  • C/C++
  • C/C++ Clang Command Adapter
  • CodeLLDB
  • CMake Tools

mysql 源码:

  • mysql-boost-5.7.35.tar.gz

补丁:
MySQL <= 8.0.21 需要对 cmake/mysql_version.cmake 文件打补丁 (没有严格测试所有版本)

1
2
3
4
tar -zxf mysql-boost-5.7.35.tar.gz
cd mysql-5.7.35
mv VERSION MYSQL_VERSION
sed -i '' 's|${CMAKE_SOURCE_DIR}/VERSION|${CMAKE_SOURCE_DIR}/MYSQL_VERSION|g' cmake/mysql_version.cmake

创建 cmake-build-debug 目录,后续 mysql 编译结果,以及启动后生成的文件都在这里

1
mkdir -p cmake-build-debug/{data,etc}

配置 CMake 与编译

在 mysql 工程目录下面创建 .vscode/settings.json 文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
{
"cmake.buildBeforeRun": true,
"cmake.buildDirectory": "${workspaceFolder}/cmake-build-debug/build",
"cmake.configureSettings": {
"WITH_DEBUG": "1",
"CMAKE_INSTALL_PREFIX": "${workspaceFolder}/cmake-build-debug",
"MYSQL_DATADIR": "${workspaceFolder}/cmake-build-debug/data",
"SYSCONFDIR": "${workspaceFolder}/cmake-build-debug/etc",
"MYSQL_TCP_PORT": "3307",
"MYSQL_UNIX_ADDR": "${workspaceFolder}/cmake-build-debug/data/mysql-debug.sock",
"WITH_BOOST": "${workspaceFolder}/boost",
"DOWNLOAD_BOOST": "0",
"DOWNLOAD_BOOST_TIMEOUT": "600"
}
}

内容没啥好说的,都是指定目录及 boost 配置,其中 WITH_DEBUG 打开 debug 模式,会在 /tmp/debug.trace 生成 debug 信息

View -> Command Palette -> CMake: Configure 执行后生成 cmake 配置

View -> Command Palette -> CMake: Build 编译生成最终 mysql 相关命令

发现老版本编译很麻烦,各种报错,mysql 5.7 代码量远超过 5.5, 只能硬着头皮看 5.7

初始化数据库

首先初始化 my.cnf 配置,简单的就可以,共它均默认

1
2
3
4
5
6
7
8
9
10
cd cmake-build-debug
cat > etc/my.cnf <<EOF
[mysqld]
port=3307
socket=mysql.sock
innodb_file_per_table=1
log_bin = on
server-id = 10086
binlog_format = ROW
EOF

初始化数据文件,非安全模式,调试用

1
./build/sql/mysqld --defaults-file=etc/my.cnf --initialize-insecure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
tree -L 1 data
data
├── auto.cnf
├── ca-key.pem
├── ca.pem
├── client-cert.pem
├── client-key.pem
├── ib_buffer_pool
├── ib_logfile0
├── ib_logfile1
├── ibdata1
├── mysql
├── on.000001
├── on.index
├── performance_schema
├── private_key.pem
├── public_key.pem
├── server-cert.pem
├── server-key.pem
└── sys

运行 MySQL

由于用 vscode 接管 mysql, 所以需要配置 .vscode/launch.json

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
{
// 使用 IntelliSense 了解相关属性。
// 悬停以查看现有属性的描述。
// 欲了解更多信息,请访问: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "launch",
"name": "Debug mysqld",
"program": "${workspaceFolder}/cmake-build-debug/build/sql/mysqld",
"args": [
"--defaults-file=${workspaceFolder}/cmake-build-debug/etc/my.cnf", "--debug"
],
"cwd": "${workspaceFolder}"
},
{
"type": "lldb",
"request": "launch",
"name": "Debug mysql",
"program": "${workspaceFolder}/cmake-build-debug/build/client/mysql",
"args": [
"-uroot",
"-P3307",
"-h127.0.0.1"
],
"cwd": "${workspaceFolder}"
}
]
}

然后点击 run and debug mysqld

mysql 启动,看到输出日志无异常,此时可以用 mysql-client 连接

1
mysql -uroot -S ./data/mysql.sock

调试

首先在 sql_parser.cc:5435 处打断点

1
void mysql_parse(THD *thd, Parser_state *parser_state)

mysql_parse 是 sql 处理的入口,至于 tcp connection 连接先可以忽略

1
mysql> select version();

执行上述 sql 自动跳转到断点处,Step Into, Step Over, Step Out 这些调试熟悉下即可

接下来分别调用主要函数:mysql_execute_command, execute_sqlcom_select, handle_query, select->join->exec(), Query_result_send::send_data, Item::send, Item_string:val_str, Protocol_text::store, net_send_ok

修改源码

启动 mysql 时 init_common_variables 会初始化一堆变量,其中会调用 set_server_version 生成版本信息,修改这个就可以

1
2
3
4
5
6
7
8
9
10
11
12
13
static void set_server_version(void)
{
char *end= strxmov(server_version, MYSQL_SERVER_VERSION,
MYSQL_SERVER_SUFFIX_STR, NullS);
......
#ifndef NDEBUG
if (!strstr(MYSQL_SERVER_SUFFIX_STR, "-debug"))
end= my_stpcpy(end, "-dongzerun");
#endif
if (opt_general_log || opt_slow_log || opt_bin_log)
end= my_stpcpy(end, "-log");
......
}

看好条件编译的是哪块,修改即可,重新 CMake: Build 编译再运行

1
2
3
4
5
6
7
mysql> select version();
+----------------------+
| version() |
+----------------------+
| 5.7.35-dongzerun-log |
+----------------------+
1 row in set (0.00 sec)

Item Class

这里不做过深分析,简单讲

  • select version(), select now() 这些简单 sql 在 server 层就能得到结果,无需进入引擎层
  • version(), now() 这些函数在 yacc&lex 词法解析时就会解析成对应的 Item
  • 最后 mysql 渲染结果时,就是由 Item::itemize 写到 result 中
1
2
3
4
5
6
7
8
9
10
function_call_generic:
IDENT_sys '(' opt_udf_expr_list ')'
{
$$= NEW_PTN PTI_function_call_generic_ident_sys(@1, $1, $3);
}
| ident '.' ident '(' opt_expr_list ')'
{
$$= NEW_PTN PTI_function_call_generic_2d(@$, $1, $3, $5);
}
;

sql_yacc.cc 函数 PTI_function_call_generic_ident_sys 解析 sql, 识别出 version() 是一个函数调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
  virtual bool itemize(Parse_context *pc, Item **res)
{
if (super::itemize(pc, res))
return true;
......










Create_func *builder= find_native_function_builder(thd, ident);
if (builder)
*res= builder->create_func(thd, ident, opt_udf_expr_list);
......
return *res == NULL || (*res)->itemize(pc, res);
}

find_native_function_builder 查找 hash 表,找到对应 version 函数注册的单例工厂函数

1
2
3
4
5
6
7
8
9
10
static Native_func_registry func_array[] =
{
{ { C_STRING_WITH_LEN("ABS") }, BUILDER(Create_func_abs)},
{ { C_STRING_WITH_LEN("ACOS") }, BUILDER(Create_func_acos)},
......
{ { C_STRING_WITH_LEN("VERSION") }, BUILDER(Create_func_version)},
......
{ {0, 0}, NULL}
};

mysql 启动时调用 item_create_init 将这些函数 builder 注册到 hash 表 native_functions_hash

1
2
3
4
5
6
7
Create_func_version Create_func_version::s_singleton;

Item*
Create_func_version::create(THD *thd)
{
return new (thd->mem_root) Item_func_version(POS());
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Item_func_version : public Item_static_string_func
{
typedef Item_static_string_func super;
public:
explicit Item_func_version(const POS &pos)
: Item_static_string_func(pos, NAME_STRING("version()"),
server_version,
strlen(server_version),
system_charset_info,
DERIVATION_SYSCONST)
{}

virtual bool itemize(Parse_context *pc, Item **res);
};

可以看到 Item_func_version 函数创建时传参即为 mysql server_version 版本信息

小结

MySQL 代码太庞大,5.1 大约 100w 行,5.5 130w 行,5.7 以后 330w 行,只能挑重点读源码。最近很多群里的人在背八股,没必要,有那时间学着调试下源码,读读多好

分享知识,长期输出价值,这是我做公众号的目标。同时写文章不容易,如果对大家有所帮助和启发,请帮忙点击在看点赞分享 三连