背景
近日有小伙伴遇到了一个生产问题,pg_rewind命令会偶发的出现这个错误消息
... // rewind 过程信息
The program "initdb" was found by "xxx/bin/pg_rewind"
but was not the same version as pg_rewind.
Check your installation.
Failure,exiting
各种环境检查、验证搞了一通之后没什么头绪,找我帮忙,我看了看代码,感觉是个bug,那就只能debug试试了。
网上逛了一圈发现,大部分教人跟踪PG源码的帖子,都是gdb attach进程的方式,并不适合这个场景,pg_rewind是个命令,一气呵成,中途不会有机会让你停下来去attach一把的。
研究了下gdb的help信息,发现--args
选项可行。
问题调查完,确实是个bug,但并不是社区版PG的bug,是我们定制版PG的bug。原因不重要,但这个调查方法我觉得可以总结一下。
跟踪PG进程的两条路
场景一、跟踪SQL进程
SQL的执行是在建立连接之后,因此,可以在建立连接之后,执行SQL之前,通过gdb的方式attach进程,附加断点,然后debug跟踪,举个栗子,开2个窗口,一边执行SQL,一边debug
- 窗口一:建连接,取pid
[guqi@localhost ~]$ psql -p 51005
psql (xxxx based on PG 11.6)
Type "help" for help.
postgres=# select pg_backend_pid();
pg_backend_pid
----------------
52069
(1 row)
- 窗口二:gdb attch pid
-- 格式:gdb postgres命令的路径 pid
[root@localhost ~]# gdb /data/postgres/app/bin/postgres 52069
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-119.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
...
0x00007fe6060bef23 in __epoll_wait_nocancel () from /lib64/libc.so.6
Missing separate debuginfos, use: debuginfo-install glibc-2.17-317.el7.x86_64
(gdb)
场景二、跟踪PG命令
PG安装路径的bin目录内有很多封装好的二进制命令,这些不像SQL需要单独建连接执行,因此跟踪这些命令的执行,也不能像场景一那样可以事先打好断点。
strace跟踪
strace
可以跟踪命令执行过程中的系统调用,并且-tt
选项可以打印调用的时间点,举个栗子:
[guqi@localhost ~]$ strace -tt createdb -h 127.1 -p 51005
14:59:41.171926 execve("/data/guqi/postgres/app/bin/createdb", ["createdb", "-h", "127.1", "-p", "51005"], 0x7ffc4ad5d878 /* 31 vars */) = 0
14:59:41.172721 brk(NULL) = 0xa55000
14:59:41.172861 mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc1f4f1f000
14:59:41.172982 access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
14:59:41.173238 open("/data/guqi/postgres/app/lib/tls/x86_64/libpq.so.5", O_RDONLY|O_CLOEXEC) = -1 ENOENT (No such file or directory)
...
14:59:41.207330 sendto(3, "X\0\0\0\4", 5, MSG_NOSIGNAL, NULL, 0) = 5
14:59:41.207446 close(3) = 0
14:59:41.207641 exit_group(1) = ?
14:59:41.208050 +++ exited with 1 +++
[guqi@localhost ~]$
gdb跟踪
gdb可以直接执行一个二进制命令
gdb [options] [executable-file [core-file or process-id]]
但是默认情况下,这个executable-file
不能带参数,否则会报错。gdb提供了一个--args
选项,可以传递参数。进入gdb交互之后,start
开始运行命令,gdb会在主函数的入口处自动打个断点(挺人性的),之后就和场景一一样了。
[guqi@localhost ~]$ gdb --args /data/guqi/postgres/bin/pg_rewind -D /data/guqi/data/master --source-server="host=127.0.0.1 port=51101 user=guqi"
GNU gdb (GDB) Red Hat Enterprise Linux 7.6.1-119.el7
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later
...
Reading symbols from /data/guqi/postgres/app/bin/pg_rewind...done.
(gdb) b 433
Breakpoint 1 at 0x40294e: file /data/guqi/src/build_alone/../xxx/src/bin/pg_rewind/pg_rewind.c, line 433.
(gdb) info b
Num Type Disp Enb Address What
1 breakpoint keep y 0x000000000040294e in main
at /data/guqi/src/build_alone/../xxx/src/bin/pg_rewind/pg_rewind.c:433
(gdb)start
Temporary breakpoint 2 at 0x4021a3: file /data/guqi/src/build_alone/../xxx/src/bin/pg_rewind/pg_rewind.c, line 125.
Starting program: /data/guqi/postgres/app/bin/pg_rewind -D /data/guqi/data/master --source-server=host=127.0.0.1\ port=51101\ user=guqi
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/usr/lib64/libthread_db.so.1".
Temporary breakpoint 2, main (argc=4, argv=0x7fffffffe388)
at /data/guqi/src/build_alone/../xxx/src/bin/pg_rewind/pg_rewind.c:125
125 set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_rewind"));
(gdb) c
Continuing.
场景二的两个方式,可以结合使用。
GDB常用的调试命令
- 在指定的文件,指定行,打断点
b postgres.c:line_num
- 查看/删除断点
info b
delete 1-5(断点序号)
- 执行
c:执行程序,直到断点或者结束为止
n:单步执行
s:单步执行,遇到函数调用,会进入函数内部
- 打印程序内的变量
-- 这个比较多变
-- 变量的形式支持类型强转,指针引用,内存地址等等,很强大
p var
- 主动调用函数
call func_name(pam_1,pam_2)
- 跳越到程序指定行去执行,类似goto语法
-- 跳跃过去之后,程序会直接开始执行,相当于从第xx行开始敲了个continue命令
jump line_num