前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >GDB + bpftrace 分分钟入门 MySQL 源码

GDB + bpftrace 分分钟入门 MySQL 源码

作者头像
初代庄主
发布2023-04-20 19:07:59
4440
发布2023-04-20 19:07:59
举报
文章被收录于专栏:初代庄主初代庄主

背景

以前我都是通过 MySQL 官方文档来学习 MySQL 的相关知识;入行久了之后发现有些问题在官方文档上是找不到答案的。如果想更进一步,就只能是学习源代码了。君子善假于物,有一些小工具可以让我们的整个学习过程平滑不少。下面我就来深入介绍一下。


工具一 GDB

MySQL 源代码压缩后都有几百MB ,文件数量又是个天文数字。我们又怎么确认数据库服务端的第一行代码在哪里呢?

这个问题对于 gdb 来讲就是 p !!! 理由有两个 1. gdb 可以根据函数名来设置断点, 2. MySQL 是一个 C++ 程序它的入口函数是 main 。也就是说我们只要在 main 函数上打断点,就能直接找到 MySQL 服务端启动后的第一行代码了。

1.1 用 gdb 拉起 MySQL

代码语言:javascript
复制
gdb --args /usr/local/mysql/bin/mysqld --defaults-file=/usr/local/mysql/my.cnf

1.2 在 main 函数上打上断点,这个时候 gdb 会自动打印 main 函数所在的位置

代码语言:javascript
复制
(gdb) b main
Breakpoint 1 at 0x3326cb5: 
file /data/repos/mysql-server-mysql-8.0.32/sql/main.cc, line 25.

可以看到 gdb 告诉我们 main 函数在 main.cc 的第 25 行处。



工具二 bpftrace

用 gdb 来分析 MySQL 的启动流程一点问题都没有,如果是想用它来分析特定 SQL 的执行流程,那我们就会发现自己做了太多的无用功。这个场景 bpftrace 就会非常直接。

要用 bpftrace 来确定 SQL 的执行流程,技术上讲是要拿到 MySQL 执行过程中的函数堆栈;也就是说我们只需要在 MySQL 发送执行结果(基本执行完成)的时候,打印一下当前线程的堆栈。在真正定代码之前我们还要补充另外一个知识,就是 send_result_set_row 函数负责发送数据给客户端。

2.1 确认 send_result_set_row 函数的探针名

代码语言:javascript
复制
bpftrace -l 'uprobe:/usr/local/mysql/bin/mysqld:*send_result_set_row*'

uprobe:/usr/local/mysql/bin/mysqld:_ZN3THD19send_result_set_rowERK14mem_root_dequeIP4ItemE

2.2 有了探针就可以给这个探针加 “监控” 了,专业一点叫追踪。下面的代码可以让我们打印 send_result_set_row 函数的堆栈。

代码语言:javascript
复制
#!/usr/bin/env bpftrace

/**
MySQL 前端线程堆栈追踪
作者: 蒋乐兴|neeky
时间: 2022-05
*/

BEGIN 
{
    print("MySQL 前端线程堆栈追踪 \n")
}

uprobe:/usr/local/mysql/bin/mysqld:_ZN3THD19send_result_set_rowERK14mem_root_dequeIP4ItemE
{
    printf("%s\n", ustack(perf)); 
}

END
{
    print("exit \n")
}

2.3 启动追踪程序

代码语言:javascript
复制
bpftrace trace-mysql-thread-stack.bt
Attaching 3 probes...
MySQL 前端线程堆栈追踪

2.4 执行一条 select 观察语句,用来观察 select 的执行流程

代码语言:javascript
复制
mysql> select user,host from mysql.user limit 1;
+------------------+-----------+
| user             | host      |
+------------------+-----------+
| mysql.infoschema | localhost |
+------------------+-----------+
1 row in set (0.00 sec)

2.5 这个时候我们的 bpftace 程序就能看到完整的执行流程了

代码语言:javascript
复制
3419e3e THD::send_result_set_row(mem_root_deque<Item*> const&)+0 (/usr/local/mysql-8.0.32/bin/mysqld)
36327e8 Query_expression::ExecuteIteratorQuery(THD*)+1648 (/usr/local/mysql-8.0.32/bin/mysqld)
3632a36 Query_expression::execute(THD*)+252 (/usr/local/mysql-8.0.32/bin/mysqld)
358199c Sql_cmd_dml::execute_inner(THD*)+206 (/usr/local/mysql-8.0.32/bin/mysqld)
3580f06 Sql_cmd_dml::execute(THD*)+1524 (/usr/local/mysql-8.0.32/bin/mysqld)
34ff4cc mysql_execute_command(THD*, bool)+21683 (/usr/local/mysql-8.0.32/bin/mysqld)
35016b3 dispatch_sql_command(THD*, Parser_state*)+1878 (/usr/local/mysql-8.0.32/bin/mysqld)
34f7609 dispatch_command(THD*, COM_DATA const*, enum_server_command)+5544 (/usr/local/mysql-8.0.32/bin/mysqld)
34f569a do_command(THD*)+1469 (/usr/local/mysql-8.0.32/bin/mysqld)
3714f7b handle_connection+479 (/usr/local/mysql-8.0.32/bin/mysqld)
5785c05 pfs_spawn_thread+336 (/usr/local/mysql-8.0.32/bin/mysqld)
7f370d88daaf start_thread+671 (/usr/lib64/libc.so.6)


bpftrace 实时追踪

让我们加大点难度,把 SQL 语句与当时的堆栈都拿出来,时间点也可以带出来这里我留白吧。


2.1 编写 bpftrace 追踪代码

代码语言:javascript
复制
#!/usr/bin/env bpftrace

/**
追踪前端线程执行的 SQL 语句与错误
作者: 蒋乐兴|neeky
时间: 2022-05
*/

/* 引入头文件 */
struct PS_PARAM {
  unsigned char null_bit;
  unsigned char unsigned_type;
  const unsigned char *value;
  unsigned long length;
  const unsigned char *name;
  unsigned long name_length;
};

struct COM_QUERY_DATA {
  const char *query;
  unsigned int length;
  struct PS_PARAM *parameters;
  unsigned long parameter_count;
};

BEGIN 
{
    print("MySQL 前端线程 SQL 追踪 \n")
}

uprobe:/usr/local/mysql/bin/mysqld:dispatch_command
{
  $parser_state = (struct COM_QUERY_DATA*)arg1;
  printf("sql = %s\n",     str($parser_state->query));
  printf("stack info:");
  printf("%s\n", ustack(perf)); 
  print("---------------------------------\n\n");
}

uprobe:/usr/local/mysql/bin/mysqld:my_message_sql
{
  printf("errno = %d\n", arg0);
  printf("errmsg = %s\n", str(arg1));
  print("---------------------------------\n\n");
}

END
{
    print("exit \n")
}

3.2 运行 bpftrace 追踪程序

代码语言:javascript
复制
bpftrace mysql-sql-stmt-trace.bt 
Attaching 5 probes...
MySQL 前端线程 SQL 追踪

3.3 执行 sql 查询

代码语言:javascript
复制
mysql> select user,host from mysql.user limit 1;
+------------------+-----------+
| user             | host      |
+------------------+-----------+
| mysql.infoschema | localhost |
+------------------+-----------+
1 row in set (0.00 sec)

3.4 观察 bpftrace 的输出

代码语言:javascript
复制
sql = select user,host from mysql.user limit 1
stack info:
        34f6061 dispatch_command(THD*, COM_DATA const*, enum_server_command)+0 (/usr/local/mysql-8.0.32/bin/mysqld)
        3714f7b handle_connection+479 (/usr/local/mysql-8.0.32/bin/mysqld)
        5785c05 pfs_spawn_thread+336 (/usr/local/mysql-8.0.32/bin/mysqld)
        7f370d88daaf start_thread+671 (/usr/lib64/libc.so.6)

---------------------------------

可以看到我们已经“抓到”了 MySQL 当前执行的 SQL 语句,就是这么简单。更多的 bpftrace 相关内容可以看这本书。


本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2023-03-21,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 初代庄主 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
云数据库 MySQL
腾讯云数据库 MySQL(TencentDB for MySQL)为用户提供安全可靠,性能卓越、易于维护的企业级云数据库服务。其具备6大企业级特性,包括企业级定制内核、企业级高可用、企业级高可靠、企业级安全、企业级扩展以及企业级智能运维。通过使用腾讯云数据库 MySQL,可实现分钟级别的数据库部署、弹性扩展以及全自动化的运维管理,不仅经济实惠,而且稳定可靠,易于运维。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档