前言
作为一个把开源 MySQL 当黑盒子研究的砖家(初级 DBA),在学习和研究的时候必须要掌握一些黑盒子研究工具,用于观察程序的行为,那么 strace 应该是最简单上手的黑盒子研究工具之一。
strace是什么?
按照 strace 官网的描述, strace 是一个可用于诊断、调试和教学的 Linux 用户空间跟踪器。我们用它来监控用户空间进程和内核的交互,比如系统调用、信号传递、进程状态变更等。
strace 底层使用内核的 ptrace 特性来实现其功能。
strace能做什么?
strace 常用来跟踪进程执行时的系统调用和所接收的信号。
在 Linux 世界,进程不能直接访问硬件设备,当进程需要访问硬件设备(比如读取磁盘文件,接收网络数据等等)时,必须由用户态模式切换至内核态模式,通过系统调用访问硬件设备。strace 可以跟踪到一个进程产生的系统调用,包括参数,返回值,执行消耗的时间。
在运维的日常工作中,故障处理和问题诊断是个主要的内容,也是必备的技能。strace 作为一种动态跟踪工具,能够帮助运维高效地定位进程和服务故障。它像是一个侦探,通过系统调用的蛛丝马迹,告诉你异常的真相。
输出参数的含义
每一行都是一条系统调用,等号左边是系统调用的函数名及其参数,右边是该调用的返回值。strace 显示这些调用的参数并返回符号形式的值。strace 从内核接收信息,而且不需要以任何特殊的方式来构建内核。
$strace cat /dev/null
execve("/bin/cat", ["cat", "/dev/null"], [/* 22 vars */]) = 0
brk(0) = 0xab1000
access("/etc/ld.so.nohwcap", F_OK) = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f29379a7000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
...复制
参数
-c 统计每一系统调用的所执行的时间,次数和出错的次数等.
-d 输出strace关于标准错误的调试信息.
-f 跟踪由fork调用所产生的子进程.
-ff 如果提供-o filename,则所有进程的跟踪结果输出到相应的filename.pid中,pid是各进程的进程号.
-F 尝试跟踪vfork调用.在-f时,vfork不被跟踪.
-h 输出简要的帮助信息.
-i 输出系统调用的入口指针.
-q 禁止输出关于脱离的消息.
-r 打印出相对时间关于,,每一个系统调用.
-t 在输出中的每一行前加上时间信息.
-tt 在输出中的每一行前加上时间信息,微秒级.
-ttt 微秒级输出,以秒了表示时间.
-T 显示每一调用所耗的时间.
-v 输出所有的系统调用.一些调用关于环境变量,状态,输入输出等调用由于使用频繁,默认不输出.
-V 输出strace的版本信息.
-x 以十六进制形式输出非标准字符串
-xx 所有字符串以十六进制形式输出.
-a column
设置返回值的输出位置.默认 为40.
-e expr
指定一个表达式,用来控制如何跟踪.格式如下:
[qualifier=][!]value1[,value2]...
qualifier只能是 trace,abbrev,verbose,raw,signal,read,write其中之一.value是用来限定的符号或数字.默认的 qualifier是 trace.感叹号是否定符号.例如:
-eopen等价于 -e trace=open,表示只跟踪open调用.而-etrace!=open表示跟踪除了open以外的其他调用.有两个特殊的符号 all 和 none.
注意有些shell使用!来执行历史记录里的命令,所以要使用\\.
-e trace=set
只跟踪指定的系统 调用.例如:-e trace=open,close,rean,write表示只跟踪这四个系统调用.默认的为set=all.
-e trace=file
只跟踪有关文件操作的系统调用.
-e trace=process
只跟踪有关进程控制的系统调用.
-e trace=network
跟踪与网络有关的所有系统调用.
-e strace=signal
跟踪所有与系统信号有关的 系统调用
-e trace=ipc
跟踪所有与进程通讯有关的系统调用
-e abbrev=set
设定 strace输出的系统调用的结果集.-v 等与 abbrev=none.默认为abbrev=all.
-e raw=set
将指 定的系统调用的参数以十六进制显示.
-e signal=set
指定跟踪的系统信号.默认为all.如 signal=!SIGIO(或者signal=!io),表示不跟踪SIGIO信号.
-e read=set
输出从指定文件中读出 的数据.例如:
-e read=3,5
-e write=set
输出写入到指定文件中的数据.
-o filename
将strace的输出写入文件filename
-p pid
跟踪指定的进程pid.
-s strsize
指定输出的字符串的最大长度.默认为32.文件名一直全部输出.
-u username
以username 的UID和GID执行被跟踪的命令复制
安装
yum -y install strace
复制
主要用法有两种:
1.从命令行调试一个新开始的程序
strace /etc/init.d/mysqld start
2.绑定到一个已有的PID上来调试一个正在运行的程序
strace -c -p 7945复制
常用命令举例:
strace -o ~/mysqld.strace -T -tt -e trace=all -p <PID>
strace -p <PID>
strace -cp <PID>复制
实用例子:
一、mysqld启动时读了的哪个配置文件?
我们学习 MySQL 时,从官方文档(https://dev.mysql.com/doc/refman/5.7/en/option-files.html)知道,MySQL5.7 如果不指定读取的配置文件,他是会按次序搜寻和读取加载 my.cnf 的,如下:
sequence | File Name | Purpose |
---|---|---|
1 | /etc/my.cnf | Global options |
2 | /etc/mysql/my.cnf | Global options |
3 | SYSCONFDIR /my.cnf | Global options |
4 | $MYSQL_HOME /my.cnf | Server-specific options (server only) |
5 | defaults-extra-file | The file specified with --defaults-extra-file, if any |
6 | ~/.my.cnf | User-specific options |
7 | ~/.mylogin.cnf | User-specific login path options (clients only) |
上面这个表格包含了 server(mysqld)、client(mysqladmin、mysqldump、mysql、mysqlbinlog 等等)的 my.cnf 搜寻顺序,还包含了编译版安装,特殊参数下的路径搜寻顺序,更准确的 mysqld 搜寻路径见下面这个:
mysqld --verbose --help |less
...
# 在第一页找到以下这两行
Default options are read from the following files in the given order:
/etc/my.cnf /etc/mysql/my.cnf /usr/local/mysql/etc/my.cnf ~/.my.cnf
...复制
能看出 mysqld 对 my.cnf 文件的搜寻和载入顺序如下:
序号 | 文件路径 |
---|---|
1 | /etc/my.cnf |
2 | /etc/mysql/my.cnf |
3 | /usr/local/mysql/etc/my.cnf |
4 | ~/.my.cnf |
下面我们用 strace 来调试一下看是不是,见标蓝色部分,完全一致。
[root@fander bin]# pwd
/usr/local/mysql/bin
[root@fander bin]# ./mysqld --version
./mysqld Ver 5.7.36 for linux-glibc2.12 on x86_64 (MySQL Community Server (GPL))
[root@fander bin]# strace ./mysqld 2>&1 |grep my.cnf
stat("/etc/my.cnf", 0x7ffd55e7c080) = -1 ENOENT (No such file or directory)
stat("/etc/mysql/my.cnf", 0x7ffd55e7c080) = -1 ENOENT (No such file or directory)
stat("/usr/local/mysql/etc/my.cnf", 0x7ffd55e7c080) = -1 ENOENT (No such file or directory)
stat("/root/.my.cnf", 0x7ffd55e7c080) = -1 ENOENT (No such file or directory)复制
二、为什么我的mysqld没有读取/etc/my.cnf文件?
[root@fander bin]# strace ./mysqld --defaults-file=/data/mysql/mysql3307/my.cnf 2>&1 |grep my.cnf
stat("/data/mysql/mysql3307/my.cnf", {st_mode=S_IFREG|0644, st_size=8434, ...}) = 0
open("/data/mysql/mysql3307/my.cnf", O_RDONLY) = 3复制
可见,指定了"--defaults-file"启动,只会读取此配置文件,而非按顺序把/etc/my.cnf
、/etc/mysql/my.cnf
、/usr/local/mysql/etc/my.cnf
、~/.my.cnf
都加载一遍。
三、我的mysql客户端都读取和加载了哪些配置文件?
[root@fander bin]# strace ./mysql -uroot -proot -S /tmp/mysql3307.sock 2>&1 |grep my.cnf
stat("/etc/my.cnf", {st_mode=S_IFREG|0644, st_size=10, ...}) = 0
open("/etc/my.cnf", O_RDONLY) = 3
stat("/etc/mysql/my.cnf", 0x7fff7159ba50) = -1 ENOENT (No such file or directory)
stat("/usr/local/mysql/etc/my.cnf", 0x7fff7159ba50) = -1 ENOENT (No such file or directory)
stat("/root/.my.cnf", 0x7fff7159ba50) = -1 ENOENT (No such file or directory)复制
明显的,stat 表示在寻找这个文件,值为 0 则找到了, -1 则找不到。可见很多文件都不存在,最后只找到了一个配置文件,就是 etc/my.cnf,open 就是打开和加载,3 表示加载成功,然后先后加载了里面 [mysql] 和 [client] 部分
[root@fander ~]# cat /etc/my.cnf
[mysql]
prompt = "fander 's mysql>"
[client]
prompt = "fander 's client>"
[root@fander ~]# mysql -uroot -proot -S /tmp/mysql3307.sock
mysql: [Warning] Using a password on the command line interface can be insecure.
Welcome to the MySQL monitor. Commands end with ; or \g.
Your MySQL connection id is 10
Server version: 5.7.36-log MySQL Community Server (GPL)
Copyright (c) 2000, 2021, Oracle and/or its affiliates.
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
fander 's client> #看这里,这里证明了我的 mysql 客户端读取加载了 /etc/my.cnf复制
四、是谁偷走了mysqld的时间?
[root@192-168-199-110 ~]# ps -ef |grep mysqld
root 7440 1 0 10:50 pts/2 00:00:00 /bin/sh /opt/fanderdb/mysql//bin/mysqld_safe --datadir=/opt/fanderdb/mysql/data/ --pid-file=/opt/fanderdb/mysql/data//192-168-199-110.pid
mysql 7945 7440 1 10:50 pts/2 00:00:04 /opt/fanderdb/mysql/bin/mysqld --basedir=/opt/fanderdb/mysql/ --datadir=/opt/fanderdb/mysql/data/ --plugin-dir=/opt/fanderdb/mysql//lib/plugin --user=mysql --log-error=/opt/fanderdb/mysql/data//mysql-error.log --open-files-limit=65535 --pid-file=/opt/fanderdb/mysql/data//192-168-199-110.pid --socket=/tmp/mysql.sock --port=3306
root 8035 5157 0 10:53 pts/2 00:00:00 grep --color=auto mysqld
[root@192-168-199-110 ~]# strace -c -p 7945
Process 7945 attached - interrupt to quit
^CProcess 7945 detached
% time seconds usecs/call calls errors syscall
------ ----------- ----------- --------- --------- ----------------
99.92 3.130370 22045 142 poll
0.06 0.002008 7 286 futex
0.02 0.000526 2 286 setsockopt
0.00 0.000000 0 143 accept
0.00 0.000000 0 429 fcntl
0.00 0.000000 0 1 restart_syscall
------ ----------- ----------- --------- --------- ----------------
100.00 3.132904 1287 total复制
抛砖引玉,更多玩法请自行探索。
Enjoy MySQL!
参考:
https://linuxtools-rst.readthedocs.io/zh_CN/latest/tool/strace.html
https://www.linuxidc.com/Linux/2018-01/150654.htm
https://blog.csdn.net/csdn265/article/details/70050168