| title | 调试 |
|---|
本文档介绍调试 OceanBase seekdb 的常用方法,包括 GDB 调试、日志调试、SQL 调试等。
seekdb 是一个复杂的分布式数据库系统,调试时需要根据不同的场景选择合适的方法。常用的调试方法包括:
- GDB 调试:适合单进程、单线程的调试场景
- 日志调试:最常用的调试方法,适用于大多数场景
- SQL 调试:使用 SQL 命令获取执行信息
- Debug Sync:特殊的调试同步机制
提示:建议编译 seekdb 时使用 debug 模式,这样更容易调试。
GDB 是一个强大的调试工具,但是使用 GDB 调试 seekdb 是比较困难的,而且场景比较有限。
适用场景:如果要调试单进程并且只有某一个线程,可以使用 GDB,否则建议使用日志调试。
假设已经部署了源码编译的 oceanbase。
调试 seekdb 与调试其他 C++ 程序类似,你可以使用 gdb,如下:
- 找到进程 id
ps -ef | grep seekdb或者
pidof seekdb- attach 进程
使用 GDB:
gdb seekdb <pid>或者使用 LLDB(macOS 上推荐):
lldb -p <pid>接着就可以设置断点,打印变量等。更多信息请参考 GDB 手册 或 LLDB 手册。
要调试RPM部署的seekdb,或者查看 coredump 文件,需要先安装或者加载 debug-info 包。推荐使用加载的模式,因为系统中会有很多 debug-info 包,而且很难清理。
首先,从网上下载 debug-info 包,然后加载到gdb。之后,你就可以很容易地调试 seekdb 了。
下面是一些提示。
如何查找 debug-info 包
使用下面的命令获取包的revision。
# in the seekdb runtime path
clusters/local/bin [83] $ ./seekdb -V
./seekdb -V
seekdb (OceanBase seekdb 1.0.0.0)
REVISION: 102000042023061314-43bca414d5065272a730c92a645c3e25768c1d05
BUILD_BRANCH: HEAD
BUILD_TIME: Nov 1 2025 14:26:23
BUILD_FLAGS: RelWithDebInfo
BUILD_INFO:
Copyright (c) 2011-2022 OceanBase Inc.如果看到下面的错误信息
./seekdb -V
./seekdb: error while loading shared libraries: libmariadb.so.3: cannot open shared object file: No such file or directory
就换成这个命令来获取revision
clusters/local/bin [83] $ LD_LIBRARY_PATH=../lib:$LD_LIBRARY_PATH ./seekdb -V
./seekdb -V
seekdb (OceanBase seekdb 1.0.0.0)
REVISION: 102000042023061314-43bca414d5065272a730c92a645c3e25768c1d05
Copyright (c) 2011-2022 OceanBase Inc.下载 debug-info 包
上面输出的版本信息中,我们需要的是 revision 字段的前半部分,即
REVISION: 102000042023061314-43bca414d5065272a730c92a645c3e25768c1d05
这个是我们需要的:102000042023061314。
接着在RPM网站上搜索 102000042023061314。
RPM网站列表:
从RPM中提取 debug-info package
从RPM中提取 debug-info 包,例如
rpm2cpio seekdb-debuginfo-1.0.0.0-102000042023061314.el7.x86_64.rpm | cpio -div解开后是这样的
~/tmp/debug-info [83] $ tree -a
.
└── usr
└── lib
└── debug
├── .build-id
│ └── ee
│ ├── f87ee72d228069aab083d8e6d2fa2fcb5c03f2 -> ../../../../../home/admin/oceanbase/bin/seekdb
│ └── f87ee72d228069aab083d8e6d2fa2fcb5c03f2.debug -> ../../home/admin/oceanbase/bin/seekdb.debug
└── home
└── admin
└── oceanbase
└── bin
└── seekdb.debugseekdb.debug 是我们要的 debug-info 包,f87ee72d228069aab083d8e6d2fa2fcb5c03f2.debug 是一个软链接。
使用 debug-info 包调试 seekdb
使用gdb命令 attach 到一个进程或者打开coredump文件。
# attach 进程
gdb ./seekdb `pidof seekdb`或
# 打开coredump文件
gdb ./seekdb <coredump file name>正常情况下,会看到这个信息
Type "apropos word" to search for commands related to "word"...
Reading symbols from clusters/local/bin/seekdb...
(No debugging symbols found in clusters/local/bin/seekdb)
Attaching to program: clusters/local/bin/seekdb, process 57296
意思是没有调试符号。
运行一些调试命令,比如 bt,会看到这个信息。
(gdb) bt
#0 0x00007fb6e9c36d62 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1 0x00007fb6f9f44862 in ob_pthread_cond_timedwait ()
#2 0x00007fb6eee8d206 in oceanbase::common::ObThreadCond::wait_us(unsigned long) ()
#3 0x00007fb6f34b21c8 in oceanbase::observer::ObUniqTaskQueue<oceanbase::observer::ObServerSchemaTask, oceanbase::observer::ObServerSchemaUpdater>::run1() ()
#4 0x00007fb6f9f44259 in oceanbase::lib::Threads::run(long) ()
#5 0x00007fb6f9f40aca in oceanbase::lib::Thread::__th_start(void*) ()看不到源码文件名和参数信息。
现在加载 debug-info 包。
(gdb) symbol-file usr/lib/debug/home/admin/oceanbase/bin/seekdb.debug
Reading symbols from usr/lib/debug/home/admin/oceanbase/bin/seekdb.debug...使用debug-info文件的全路径更好
再次执行调试命令,就可以看到详细信息。
(gdb) bt
#0 0x00007fb6e9c36d62 in pthread_cond_timedwait@@GLIBC_2.3.2 () from /lib64/libpthread.so.0
#1 0x00007fb6f9f44862 in ob_pthread_cond_timedwait (__cond=0x7fb6fb1d5340, __mutex=0x7fb6fb1d5318, __abstime=0x7fb6b3ed41d0)
at deps/oblib/src/lib/thread/ob_tenant_hook.cpp:124
#2 0x00007fb6eee8d206 in oceanbase::common::ObThreadCond::wait_us (this=<optimized out>, time_us=140422679606016)
at deps/oblib/src/lib/lock/ob_thread_cond.cpp:106
#3 0x00007fb6f34b21c8 in oceanbase::common::ObThreadCond::wait (this=0x7fb6fb1d5310, time_ms=200)
at deps/oblib/src/lib/lock/ob_thread_cond.h:69
#4 oceanbase::observer::ObUniqTaskQueue<oceanbase::observer::ObServerSchemaTask, oceanbase::observer::ObServerSchemaUpdater>::run1 (
this=<optimized out>) at src/observer/ob_uniq_task_queue.h:417日志是调试 seekdb 最常用的方法,易于使用,适用于大多数场景。
在常见的场景中,可以在代码中添加日志并打印变量,然后重新编译和部署 seekdb。
提示:关于日志的详细用法,请参考 日志系统 文档。
可以在源码中找到日志代码,比如
LOG_DEBUG("insert sql generated", K(insert_sql));LOG_DEBUG 是打印DEBUG级别的日志宏。
这跟其它的程序有点不太一样。第一个参数是一个字符串,其他的参数通常是 K(variable_name)。K 是一个宏,用来打印变量名和值。
日志文件在 seekdb 运行目录的 log 目录下。你可以使用 grep 命令搜索日志。
一个日志的例子。
[2023-07-05 16:40:42.635136] INFO [SQL.EXE] explicit_start_trans (ob_sql_trans_control.cpp:194) [88022][T1003_ArbSer][T1003][YD9F97F000001-0005FFB71FCF95C7-0-0] [lt=42] start_trans(ret=0, tx_id={txid:2118151}, session={this:0x7ff2663d6188, id:1, tenant:"sys", tenant_id:1, effective_tenant:"sys", effective_tenant_id:1003, database:"oceanbase", user:"root@%", consistency_level:3, session_state:0, autocommit:true, tx:0x7ff26b0e4300}, read_only=false, ctx.get_execution_id()=18446744073709551615)
时间戳([2023-07-05 16:40:42.635136]), 日志级别(INFO), 模块名称([SQL.EXE]), 函数名称(explicit_start_trans), 代码文件(ob_sql_trans_control.cpp), 行号(194), 线程号(88022), 线程名称(T1003_ArbSer), trace id(YD9F97F000001-0005FFB71FCF95C7-0-0), 等等.
每个SQL请求都有一个唯一的 trace id。可以通过 trace id 来找到所有与指定SQL请求相关的日志。
Trace ID
使用下面的SQL命令可以获取上一次执行SQL请求的 trace id。
select last_trace_id();日志级别
使用下面的命令调整日志级别。
set ob_log_level=debug;Log 流量控制
如果找不到想要的日志,可能是被限流了,可以使用下面的命令调整日志流量控制。
alter system set syslog_io_bandwidth_limit='1G';
alter system set diag_syslog_per_error_limit=1000;同步打印日志
用下面的命令可以同步打印日志。
alter system set enable_async_syslog='False';打印调用栈
在日志中可以这样打印调用栈
LOG_DEBUG("insert sql generated", K(insert_sql), K(lbt()));假设看到这样的信息:
lbt()="0x14371609 0xe4ce783 0x54fd9b6 0x54ebb1b 0x905e62e 0x92a4dc8 0x905df11 0x905dc94 0x13d2278e 0x13d22be3 0x6b10b81 0x6b0f0f7 0x62e2491 0x10ff6409 0x1475f87a 0x10ff6428 0x1475f1c2 0x1476ba83 0x14767fb5 0x14767ae8 0x7ff340250e25 0x7ff33fd0ff1d"用这个命令查看调用栈信息:
addr2line -pCfe ./bin/seekdb 0x14371609 0xe4ce783 0x54fd9b6 0x54ebb1b 0x905e62e 0x92a4dc8 0x905df11 0x905dc94 0x13d2278e 0x13d22be3 0x6b10b81 0x6b0f0f7 0x62e2491 0x10ff6409 0x1475f87a 0x10ff6428 0x1475f1c2 0x1476ba83 0x14767fb5 0x14767ae8 0x7ff340250e25 0x7ff33fd0ff1d我测试时看到的是这样的
oceanbase::common::lbt() at /home/distcc/tmp/./deps/oblib/src/lib/utility/ob_backtrace.cpp:130 (discriminator 2)
operator() at /home/distcc/tmp/./src/sql/session/ob_basic_session_info.cpp:599 (discriminator 2)
oceanbase::sql::ObBasicSessionInfo::switch_tenant(unsigned long) at /home/distcc/tmp/./src/sql/session/ob_basic_session_info.cpp:604
oceanbase::observer::ObInnerSQLConnection::switch_tenant(unsigned long) at /home/distcc/tmp/./src/observer/ob_inner_sql_connection.cpp:1813 (discriminator 2)
...
oceanbase::lib::Thread::run() at /home/distcc/tmp/./deps/oblib/src/lib/thread/thread.cpp:162
oceanbase::lib::Thread::__th_start(void*) at /home/distcc/tmp/./deps/oblib/src/lib/thread/thread.cpp:312
?? ??:0
?? ??:0一些 SQL 命令也可以帮助调试。首先运行下面的命令开启 trace 功能:
-- on 4.x
set ob_enable_show_trace=1;然后运行SQL命令,比如:
select * from t, t1 where t.id=t1.id;之后,你可以运行下面的命令获取trace信息。
show trace;假设看到这样的信息
obclient> show trace;
+-------------------------------------------+----------------------------+------------+
| Operation | StartTime | ElapseTime |
+-------------------------------------------+----------------------------+------------+
| com_query_process | 2023-07-06 15:30:49.907532 | 9.547 ms |
| └── mpquery_single_stmt | 2023-07-06 15:30:49.907552 | 9.506 ms |
| ├── sql_compile | 2023-07-06 15:30:49.907615 | 6.605 ms |
| │ ├── pc_get_plan | 2023-07-06 15:30:49.907658 | 0.024 ms |
| │ └── hard_parse | 2023-07-06 15:30:49.907763 | 6.421 ms |
| │ ├── parse | 2023-07-06 15:30:49.907773 | 0.119 ms |
| │ ├── resolve | 2023-07-06 15:30:49.907952 | 0.780 ms |
| │ ├── rewrite | 2023-07-06 15:30:49.908857 | 1.320 ms |
| │ ├── optimize | 2023-07-06 15:30:49.910209 | 3.002 ms |
| │ ├── code_generate | 2023-07-06 15:30:49.913243 | 0.459 ms |
| │ └── pc_add_plan | 2023-07-06 15:30:49.914016 | 0.140 ms |
| └── sql_execute | 2023-07-06 15:30:49.914239 | 2.675 ms |
| ├── open | 2023-07-06 15:30:49.914246 | 0.217 ms |
| ├── response_result | 2023-07-06 15:30:49.914496 | 1.956 ms |
| │ └── do_local_das_task | 2023-07-06 15:30:49.914584 | 0.862 ms |
| └── close | 2023-07-06 15:30:49.916474 | 0.415 ms |
| ├── close_das_task | 2023-07-06 15:30:49.916486 | 0.037 ms |
| └── end_transaction | 2023-07-06 15:30:49.916796 | 0.064 ms |
+-------------------------------------------+----------------------------+------------+
18 rows in set (0.01 sec)在使用 GDB 调试 seekdb 的时候,可能会出现问题,因为 GDB 会挂起进程,而 seekdb 依赖心跳来正常工作。所以我们提供了一个 debug sync 机制来解决这个问题。
注意:Debug Sync 在 release 模式下也可以用,所以它在生产环境中是开启的。
在代码中增加一个 debug sync 点,特定的线程会在这个点挂起,然后你可以做一些事情来调试这个进程,比如使用 gdb attach 进程,或者执行一些 SQL 命令来获取一些信息。
打开文件 ob_debug_sync_point.h,在宏 OB_DEBUG_SYNC_POINT_DEF 中增加 debug sync 定义。比如:
#define OB_DEBUG_SYNC_POINT_DEF(ACT) \
ACT(INVALID_DEBUG_SYNC_POINT, = 0) \
ACT(NOW,) \
ACT(MAJOR_FREEZE_BEFORE_SYS_COORDINATE_COMMIT,) \
ACT(BEFORE_REBALANCE_TASK_EXECUTE,) \
ACT(REBALANCE_TASK_MGR_BEFORE_EXECUTE_OVER,) \
ACT(UNIT_BALANCE_BEFORE_PARTITION_BALANCE,)要调试哪个函数,就在对应的函数加上 debug sync。比如:
int ObRootService::do_restart()
{
int ret = OB_SUCCESS;
const int64_t tenant_id = OB_SYS_TENANT_ID;
SpinWLockGuard rs_list_guard(broadcast_rs_list_lock_);
...
DEBUG_SYNC(BEFORE_UNIT_MANAGER_LOAD);
...
}可以在任何地方加 debug sync。
默认情况下,debug sync 是关闭的,通过下面的 SQL 命令开启它:
alter system set debug_sync_timeout='100000s';将 debug_sync_timeout 设置为大于0的数字就可以开启。
注意:
debug_sync_timeout的单位是微秒。
用这个命令开启自己加上的 debug sync:
set ob_global_debug_sync = 'BEFORE_UNIT_MANAGER_LOAD wait_for signal_name execute 10000';execute 的意思是 debug sync 动作会在执行 10000 次后被禁用。
signal_name 是唤醒的名字。
当某个线程执行到debug sync时,就会停下来,这时候就可以执行一些调试操作。
用下面的命令唤醒 debug sync:
set ob_global_debug_sync = 'now signal signal_name';
-- or
set ob_global_debug_sync = 'now broadcast signal_name';signal_name 是你在开启 debug sync 点时设置的名字。
然后hang的线程会继续执行。
调试完成后,需要清理掉 debug sync 点,可以使用下面的命令:
set ob_global_debug_sync = 'BEFORE_UNIT_MANAGER_LOAD clear';使用下面的命令关闭 debug sync:
alter system set debug_sync_timeout=0;当运行到指定的 debug sync 时,进程会使用 condition_variable 来等待信号,然后会挂起在 debug sync 点。当收到信号后,进程会继续执行。
可以查看代码 ob_debug_sync.cpp/.h 来了解更多关于 debug sync 机制的信息。
