Skip to content

Latest commit

 

History

History
478 lines (341 loc) · 14.4 KB

6.miniob-debug-environment-setup.md

File metadata and controls

478 lines (341 loc) · 14.4 KB

1.5 MiniOB 开发调试环境搭建

本文将介绍两种方式的构建方法,可根据实际情况选择手动构建或直接使用 Dockerfile 构建。

手动打造开发环境

MiniOB 编译

前提条件:系统上已经安装了 Make 等编译工具。

使用 MiniOB 需要具备以下两个条件。

  • CMake:3.10 版本以上。

  • GCC/Clang:GCC 建议 8.3 版本以上,编译器需要支持 C++20 等新标准。

安装 CMake

需要安装 3.10 版本或 3.10 以上版本的 CMake,在 CMake官网 下载对应系统的 CMake。

wget https://github.com/Kitware/CMake/releases/download/v3.24.0/cmake-3.24.0-linux-x86_64.sh
bash cmake-3.24.0-linux-x86_64.sh

如果是 MAC 系统,执行以下命令安装。

brew install cmake

安装 GCC

如果是个人学习使用,Clang 通常没有问题,如果想参加比赛或使用 OceanBase 官网训练营,建议安装 GCC。因为 Clang 某些情况下的表现会与 GCC 不一致。

另外,如果 GCC 的版本比较旧,需要安装较新版本的 GCC。有些比较旧的操作系统(如 CentOS 7),自带的编译器是 4.8.5 版本,该版本对 C++20 等新标准支持不友好,建议升级 GCC。

在 Linux 系统上,通常使用 yum install gcc gcc-g++ 就可以安装 GCC,但是有些时候安装的编译器版本比较旧,需要手动安装。

  • 如何查看 GCC 版本

    gcc --version
  • 下载 GCC 源码

    可以在 GCC 官网浏览,找到镜像入口,选择速度比较快的镜像,如 Russia, Novosibirsk GCC 镜像链接

    建议选择 8.3 或以上版本。MiniOB 是为了学习使用,可以选择较新版本的 GCC,不要求稳定。

  • 编译 GCC 源码

    注意

    编译高版本的 GCC 时,需要本地也有一个稍高一点版本的 GCC。比如编译 GCC 11,本地的 GCC 需要能够支持 stdc++11 才能编译,可以先编译 GCC 5.4,然后再编译高版本。

    # 解压
    tar xzvf gcc-11.3.0.tar.gz
    cd gcc-11.3.0
    
    # 下载依赖
    ./contrib/download_prerequisites
    
    # 配置。可以通过 prefix 参数设置编译完成的 GCC 的安装目录,如果不指定,会安装在 /usr/local下
    # 可以配置为当前用户的某个目录
    ./configure --prefix=/your/new/gcc/path --enable-threads=posix --disable-checking \
        --enable--long-long --with-system-zlib --enable-languages=c,c++
    
    # 开始编译
    make -j
    
    # 安装
    # 编译产生物会安装到 configure --prefix 指定的目录中,或系统默认目录下
    make install
    
    # 修改环境变量
    # 可以将下面的配置写到 .bashrc 或 .bash_profile 中,这样每次登录都会自动生效
    export PATH=/your/new/gcc/path/bin:$PATH
    export LD_LIBRARY_PATH=/your/new/gcc/path/lib64:$LD_LIBRARY_PATH
    export CC=/your/new/gcc/path/bin/gcc
    export CXX=/your/new/gcc/path/bin/g++
    
    # NOTE: 上面的环境变量 CC 和 CXX 是告诉 CMake 能够找到我们的编译器。CMake 优先查找的
    #       编译器是 CC 而不是 GCC,而一般系统中会默认带 CC,所以使用环境变量告诉 CMake。
    #       也可以使用 CMake 参数
    #       `-DCMAKE_C_COMPILER=/your/new/gcc/path/bin/gcc -DCMAKE_CXX_COMPILER=/your/new/gcc/path/bin/g++`
    #       来指定编译器

    如果 ./contrib/download_prerequisites 下载时特别慢或下载失败,可以手动从官网上下载依赖,解压相应的包到 GCC 的目录下。

    ftp://ftp.gnu.org/gnu/gmp
    https://mpfr.loria.fr/mpfr-current/#download
    https://www.multiprecision.org/mpc/download.html

构建 MiniOB

  1. 下载源码

    git clone https://github.com/oceanbase/miniob.git
  2. 环境初始化

    如果是第一次在这个环境上编译 MiniOB,需要执行如下命令安装 MiniOB 的依赖库。

    cd miniob
    bash build.sh init

    命令执行后,脚本会自动拉取依赖库,并编译安装到系统目录。

    说明

    如果使用 GitPod 开发,可以跳过这步,GitPod 会自动执行。

  3. 编译

    执行如下命令将编译 debug 版本的 MiniOB。

    bash build.sh

    执行如下命令将编译 release 版本的 MiniOB。

    bash build.sh release

    详细使用方法可执行 bash build.sh -h 查看帮助信息。

MiniOB 运行

编译完成后,可在 build 目录(build_debug 或 build_release)下找到 bin/observer 目录,这是 MiniOB 的服务端进程,bin/obclient 是自带的客户端进程,当前服务端程序启动已经支持了多种模式,可以以 TCP、unix socket 方式启动,这时需要启动客户端以发起命令。observer 还支持直接执行命令的模式,这时不需要启动客户端,直接在命令行输入命令即可。

说明

observer 提供的参数可通过 ./bin/observer -h 命令查看帮助。

以直接执行命令启动服务端进程

您可执行如下命令以直接执行命令的方式启动服务端程序,这种情况下可以直接输入命令,不需要启动客户端,所有请求都会以单线程的方式运行,配置项中的线程数不再有实际意义。

./bin/observer -f ../etc/observer.ini -P cli

以监听 TCP 端口方式启动服务端进程

您可执行如下命令以监听端口的方式启动服务端程序,此处以 6789 端口为例。

./bin/observer -f ../etc/observer.ini -p 6789

这种情况下需执行如下命令启动客户端程序,启动后客户端会连接到服务端的 6789 端口。

./bin/obclient -p 6789

以监听 unix socket 方式启动服务端程序

您可执行如下命令以监听 unix socket 的方式启动服务端程序。

./bin/observer -f ../etc/observer.ini -s miniob.sock

这种情况下需执行如下命令启动客户端程序,启动后客户端会连接到服务端的 miniob.sock 文件。

./bin/obclient -s miniob.sock

并发模式

默认情况下,编译后的程序不支持并发操作,如若需要支持并发,需要在编译时增加 -DCONCURRENCY=ON 选项,示例如下。

cmake -DCONCURRENCY=ON ..

或执行如下命令。

bash build.sh -DCONCURRENCY=ON

之后使用上述命令启动服务端进程,即可支持并发操作。

常见问题

问题现象:在启动服务端进程时出现报错找不到链接库 A。

可能原因:安装依赖时,默认安装在 /usr/local/ 目录下,而环境变量中没有将目录包含到动态链接库查找路径。

解决方法

您可将如下命令添加到 HOME 目录的 .bashrc 中,之后执行 source ~/.bashrc 命令加载环境变量后重新启动程序。

export LD_LIBRARY_PATH=/usr/local/lib64:$LD_LIBRARY_PATH

LD_LIBRARY_PATH 是 Linux 环境中运行时查找动态链接库的路径,路径之间以冒号(:)分隔。

将数据写入 bashrc 或其它文件中,下次启动程序时会自动加载,而不需要再次执行 source 命令加载。

注意

如果你的终端脚本使用的不是 bash,而是 zsh,那么就需要修改 .zshrc

MiniOB 调试

调试 C/C++ 程序,常用的有两种方式,一是通过打印日志进行调试,二是借助 GDB 调试器进行调试。通过调试程序,可以熟悉程序执行的每一步细节,不仅可以定位问题,也可以用来熟悉代码,增加编程能力。

MiniOB 的关键数据结构

parse_def.h:
	struct Selects; //查询相关
	struct CreateTable; //建表相关
	struct DropTable; //删表相关
	enum SqlCommandFlag; //SQL 语句对应的 command 枚举
	union Queries; //各类 DML 和 DDL 操作的联合
table.h
	class Table;
db.h
	class Db;

MiniOB 的关键接口

RC parse(const char *st, Query *sqln); //sql parse 入口
ExecuteStage::handle_request
ExecuteStage::do_select
DefaultStorageStage::handle_event
DefaultHandler::create_index
DefaultHandler::insert_record
DefaultHandler::delete_record
DefaultHandler::update_record
Db::create_table
Db::find_table
Table::create
Table::scan_record
Table::insert_record
Table::update_record
Table::delete_record
Table::scan_record
Table::create_index

打印日志调试

MiniOB 提供的日志接口

deps/common/log/log.h:
#define LOG_PANIC(fmt, ...)
#define LOG_ERROR(fmt, ...)
#define LOG_WARN(fmt, ...)
#define LOG_INFO(fmt, ...)
#define LOG_DEBUG(fmt, ...)
#define LOG_TRACE(fmt, ...)

日志相关配置项 observer.ini

LOG_FILE_NAME = observer.log
#  LOG_LEVEL_PANIC = 0,
#  LOG_LEVEL_ERR = 1,
#  LOG_LEVEL_WARN = 2,
#  LOG_LEVEL_INFO = 3,
#  LOG_LEVEL_DEBUG = 4,
#  LOG_LEVEL_TRACE = 5,
#  LOG_LEVEL_LAST
LOG_FILE_LEVEL=5
LOG_CONSOLE_LEVEL=1

GDB 调试

  1. Attach 进程

    [admin@localhost run]$ gdb -p `pidof observer`
    
    GNU gdb (GDB) Red Hat Enterprise Linux 8.2-15.el8 Copyright (C) 2018 Free Software Foundation, Inc.
    
    (gdb)
  2. 设置断点

    (gdb) break do_select
    Breakpoint 1 at 0x44b636: file /home/admin/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp, line 526.
    (gdb) info b
    Num     Type           Disp Enb Address            What
    1       breakpoint     keep y   0x000000000044b636 in ExecuteStage::do_select(char const*, Query*, SessionEvent*)
                                                        at /home/admin/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp:526
    (gdb) break Table::scan_record
    Breakpoint 2 at 0x50b82b: Table::scan_record. (2 locations)
    (gdb) inf b
    Num     Type           Disp Enb Address            What
    1       breakpoint     keep y   0x000000000044b636 in ExecuteStage::do_select(char const*, Query*, SessionEvent*)
                                                        at /home/admin/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp:526
    2       breakpoint     keep y   <MULTIPLE>
    2.1                         y     0x000000000050b82b in Table::scan_record(Trx*, ConditionFilter*, int, void*, void (*)(char const*, void*))
                                                        at /home/admin/source/stunning-engine/src/observer/storage/common/table.cpp:421
    2.2                         y     0x000000000050ba00 in Table::scan_record(Trx*, ConditionFilter*, int, void*, RC (*)(Record*, void*))
                                                        at /home/admin/source/stunning-engine/src/observer/storage/common/table.cpp:426
    (gdb)
  3. 继续执行

    (gdb) c
    Continuing.
  4. 触发断点

    执行:miniob > select * from t1;

    [Switching to Thread 0x7f51345f9700 (LWP 54706)]
    
    Thread 8 "observer" hit Breakpoint 1, ExecuteStage::do_select (this=0x611000000540,
        db=0x6040000005e0 "sys", sql=0x620000023080, session_event=0x608000003d20)
        at /home/admin/source/stunning-engine/src/observer/sql/executor/execute_stage.cpp:526
    526	  RC rc = RC::SUCCESS;
    (gdb)
  5. 单步调试(跳过函数调用)

    575	  std::vector<TupleSet> tuple_sets;
    (gdb) next
    576	  for (SelectExeNode *&node: select_nodes) {
    (gdb) n
    577	    TupleSet tuple_set;
    (gdb)
    578	    rc = node->execute(tuple_set);
    (gdb)
  6. 单步调试(进入函数调用的内部)

    (gdb) s
    SelectExeNode::execute (this=0x60700002ce80, tuple_set=...)
        at /home/admin/source/stunning-engine/src/observer/sql/executor/execution_node.cpp:43
    43	  CompositeConditionFilter condition_filter;
    (gdb)
  7. 打印变量

    (gdb) p tuple_set
    $3 = (TupleSet &) @0x7f51345f1760: {tuples_ = std::vector of length 0, capacity 0, schema_ = {
        fields_ = std::vector of length 0, capacity 0}}
    (gdb)
  8. watch 变量

    (gdb) n
    443	  RC rc = RC::SUCCESS;
    (gdb) n
    444	  RecordFileScanner scanner;
    (gdb) n
    445	  rc = scanner.open_scan(*data_buffer_pool_, file_id_, filter);
    (gdb) watch -l rc
    Hardware watchpoint 3: -location rc
    (gdb) c
    Continuing.
    
    Thread 8 "observer" hit Hardware watchpoint 3: -location rc
    
    Old value = SUCCESS
    New value = RECORD_EOF
    0x000000000050c2de in Table::scan_record (this=0x60f000007840, trx=0x606000009920,
        filter=0x7f51345f12a0, limit=2147483647, context=0x7f51345f11c0,
        record_reader=0x50b74a <scan_record_reader_adapter(Record*, void*)>)
        at /home/admin/source/stunning-engine/src/observer/storage/common/table.cpp:454
    454	  for ( ; RC::SUCCESS == rc && record_count < limit; rc = scanner.get_next_record(&record)) {
    (gdb)
  9. 结束函数调用

    (gdb) finish
    Run till exit from #0  0x000000000050c2de in Table::scan_record (this=0x60f000007840,
        trx=0x606000009920, filter=0x7f51345f12a0, limit=2147483647, context=0x7f51345f11c0,
        record_reader=0x50b74a <scan_record_reader_adapter(Record*, void*)>)
        at /home/admin/source/stunning-engine/src/observer/storage/common/table.cpp:454
  10. 结束调试

    (gdb) quit
    A debugging session is active.
        
        Inferior 1 [process 54699] will be detached.
    
    Quit anyway? (y or n) y
    Detaching from program: /home/admin/local/bin/observer, process 54699
    [Inferior 1 (process 54699) detached]

使用 Dockerfile 构建

Docker 环境说明

MiniOB 镜像文件基于 CentOS:7 制作,镜像包含:

  • jsoncpp

  • google test

  • libevent

  • flex

  • bison(3.7)

  • gcc/g++ (version=11)

  • MiniOB 源码(/root/source/miniob)

在 Docker 中 /root/source/miniob 目录下载了 GitHub 的源码,可以根据个人需要,下载自己仓库的源代码,也可以直接使用 git pull 拉取最新代码。 /root/source/miniob/build.sh 提供了一个编译脚本,以 DEBUG 模式编译 MiniOB。

操作步骤

首先要确保本地已经安装了 Docker。

  1. 使用 Dockerfile 构建。

    Dockerfile: https://github.com/oceanbase/miniob/blob/main/Dockerfile

    # build
    docker build -t miniob .
    docker run -d --name='miniob' miniob:latest
    
    # 进入容器,开发调试
    docker exec -it miniob bash
  2. 使用 docker hub 镜像运行。

    docker run oceanbase/miniob