接前文postgresql编译安装与调试(一),继续说说postgresql的编译安装与调试。

上一篇已经详细说明了如何在Linux系统上编译安装postgresql,这次我们在此基础上简单讲讲如何在linux系统上调试和追踪代码。

我记得之前看过一篇关于posgresql的文章,postgresql最早只有20万左右的代码量,而如今已经过100万行了,如此巨大的代码量,在没有纲领的前提下简直是盲人摸象。

为方便调试工作,在进入具体的调试之前,我们先来好好了解下postgresql的代码体系结构。

  • 1.postgresql的目录结构

首先进入postgresql的第一级目录

config文件夹主要放的是一些配置文件;contrib文件夹里放的是一些第三方的插件、扩展程序等,常用的有pg_standby、postgres_fdw这些;doc文件夹不用说放的是一些帮助文档和manuals;最主要的是src目录,这里放置的是postgresql的源代码,也是我们调试和跟踪的主要文件目录了。然后INSTALL文档里较为详细的写了如何编译安装postgresql;configure和Makefile这些是程序编译时要用的文件了。

进去src目录,

首先那几个Makefile文件什么的就不用多介绍了,主要看看这几个文件夹。

bin/              放置了postgresql的unix命令,比如psql、initdb这些的源代码;

backend/          postgresql后端程序的源代码;

include/          头文件;

interfaces/      前端相关的库的代码(包括pgsql的C语言库libpq);

makefiles/       平台相关的make的设置文件;

pl/               存储过程语言的代码;

port/             平台移植相关的代码;

template/        平台相关的设置文件;

test/             postgresql自带的各种测试脚本;

timezone/        时区相关的代码文件;

tools/           各种开发工具和文档;

tutorial/        各种相关教程。

可以看出比较核心的是backend、bin、interfaces这三个目录,其中backend对应后端(服务器端),剩下两个对应前端(客户端)。

对于我们的调试工作,大部分关注点集中在后端,即backend目录,在该目录下细分了好多目录:

access/            各种存储访问方法(在各个子目录下) common(共同函数)、gin (Generalized Inverted Index通用逆向索引) 、gist (Generalized Search Tree通用索引)、hash (哈希索引)、heap (heap的访问方法)、index (通用索引函数)、 nbtree (Btree函数)、transam (事务处理)、 bootstrap/ 数据库的初始化处理(initdb的时候)

catalog/           系统目录

commands/          SELECT/INSERT/UPDATE/DELETE以为的SQL文的处理

executor/          执行器(访问的执行)

foreign/           FDW(Foreign Data Wrapper)处理

lib/               共同函数

libpq/             前端/后端通信处理

main/              postgres的主函数

nodes/             构文树节点相关的处理函数

optimizer/        优化器

parser/            SQL构文解析器

port/              平台相关的代码

postmaster/      postmaster的主函数 (常驻postgres)

replication/     streaming replication

regex/            正则处理

rewrite/          规则及视图相关的重写处理

snowball/         全文检索相关(语干处理)

storage/          共享内存、磁盘上的存储、缓存等全部一次/二次记录管理(以下的目录)buffer/(缓存管理)、 file/(文件)、freespace/(Fee Space Map管理) ipc/(进程间通信)、large_object /(大对象的访问函数)、 lmgr/(锁管理)、page/(页面访问相关函数)、 smgr/(存储管理器)

tcop/             postgres (数据库引擎的进程)的主要部分

tsearch/           全文检索

utils/            各种模块(以下目录) adt/(嵌入的数据类型)、cache/(缓存管理)、 error/(错误处理)、fmgr/(函数管理)、hash/(hash函数)、 init/(数据库初始化、postgres的初期处理)、mb/(多字节文字处理)、misc/(其他)、mmgr/(内存的管理函数)、 resowner/(查询处理中的数据(buffer pin及表锁)的管理)、sort/(排序处理)、time/(事务的 MVCC 管理)

  • 2.利用gdb调试postgresql

首先我们要有gdb这个工具,如果没有,可以用yum命令自动的去安装它。

调试postgresql,我们先以最简单的SQL文作为例子演示如何调试跟踪代码。例如:

select 1;

首先,我们利用postgres用户进入postgresql:

既然要用gdb跟踪调试程序,我们首先要知道postgresql后端进程的pid,然后才能attach上进行调试(对gdb命令不熟悉的可以先自行百度下)。

要获取postgresql的pid,我们有两个办法。

方法1.使用ps命令查看

[root@promote ~]# ps -ef | grep postgres

我们可以看到

那个[local] idle 提示的那个就是我们要的,可知进程pid为16581;

方法2.直接在进入postgresql后运行下面的查询语句:

select pg_backend_pid();

也可以得到进程的pid,方便快捷。

得到进程的pid后,我们就可以进入gdb调试了。

另开一个窗口,我们输入如下命令:

[root@promote ~]# gdb postgres 16581

进入了gdb命令行界面。

在这个状态下,可以接受gdb命令,这里,我们使用b命令在ExecResult处打上断点:

这个时候我们再回到postgresql的窗口,执行SQL文:

我们可以看到因为postgres进程已经暂停,SQL会卡在那里动不了,这也是我们的目的,不然怎么一步一步(似魔鬼的步伐)的调试呢?

我们再回到gdb这边,运行c命令,程序就会继续执行下去,然后再断点处(ExecResult)停止。

作为一个好奇宝宝,我们当然会很好奇执行路径上走过了哪些文件调用了哪些函数(废话,不然干嘛要调试)?

好的,我们执行gdb的bt命令:

这一大串就是我们梦寐以求的函数调用的堆栈了。这样从程序开始到ExecResult为止的函数调用都有了。既然说是“堆栈”,我们自然是要反着看的,比如,我们可以看到最早调用的是main函数,它在(at)main.c文件里,在main函数的第228行,调用了PostmasterMain函数,依次类推即可知道函数的调用路径。

知道了函数的调用路径,我们可以一步一步地看看这条语句是怎么走的了。以postgresql9.5.4为例(限于篇幅和时间限制,只粗略的讲讲):

#13 main.c 内:

line99: 函数MemoryContextInit()启动必须的子系统error和memory管理系统;

line110:函数set_pglocale_pgservice()获取并设置环境变量;

line146~148: 函数init_locale初始化环境变量;

line219~228:根据输入参数确定程序走向,这里进入了PostmasterMain(),跳转至postmaster.c文件。

#12#11#10#9 postmaster.c 内:

该文件中定义了后端的常驻进程"postmaster"所使用的主要函数接口和数据结构定义。postmaster接受前端的请求,建立新的backend进程。

line561~623:读取上下文信息和配置文件,完成初始化;

line630~812:读取postmaster的参数;

line930~1000:建立socket通信;

line1100~1159:建立shared memory和semaphores以及堆栈和pipe,初始化子系统(stats collection、autovacuum);

line1296:进入ServerLoop()函数,跳转至line1604;

line1604:ServerLoop()函数入口。该函数循环监听端口上的连接请求;

line1673~1699:判断是否有"合法"的连接请求,fork一个子进程去处理它,进入BackendStartup()函数,跳转至line3857;

line3857:BackendStartup()函数入口。该函数负责开启一个新的backend进程;

line3858~3914:做一些初始化准备(数据结构,开启和关闭一些必要的进程等等);

line3917:进入BackendRun()函数,跳转至line4179;

line4179:BackendRun()函数入口,该函数运行backend进程,主要干两件事:1.建立参数列表并初始化2.调用PostgresMain()函数;

line4243:调用PostgresMain()函数,进入postgres.c文件.

#8#7 postgres.c 文件内:

该文件定义了postgres后端的主要模块,相当于后端的main,并且负责后端进程的调度。

line3572:PostgresMain()函数入口。根据输入的dbname,username和输入参数建立一个会话;

line3573~3801:初始化工作。开设初始化环境和默认参数,设置信号处理函数和其他参数,建立内存上下文,设置share buffer等等等等;

line3825:进入POSTGRES的主处理循环,这个if语句主要用于判断输入处理是否有异常等;

line3933:进入处理循环中。该循环监听新的查询请求并判断请求的类别;

line4045:判断查询请求为simple query,调用exec_simple_query()函数,跳转至line884;

line884:exec_simple_query()函数入口。该函数做一些初始化工作,建立一个transaction command,做简单的语法规则判断,分析重写,并为该查询建立查询计划,并返回查询结果;

line1104:进入函数PortalRun(),进入pquery.c文件.

#6#5 pquery.c 文件内:

该文件定义了postgres后端查询语句的代码。

line706:PortalRun()函数入口。该函数负责运行一个或一组查询;

line786:进入PortalRunSelect()函数,跳转至line888;

line888:PortalRunSelect()函数入口。该函数只能执行简单的SELECT查询操作;

line942:进入ExecutorRun()函数,进入execMain.c文件.

#4#3#2 execMain.c 文件内:

该文件给出了执行的四个接口函数,分别是ExecutorStart() ExecutorRun() ExecutorFinish() ExecutorEnd()。

line279:ExecutorRun()函数入口。该函数时执行模块的主要部分,它接受一个查询描述符并真正的执行一个查询语句;

line285:进入standard_ExecutorRun()函数。跳转至line289;

line289:standard_ExecutorRun()函数入口。它执行"标准"的查询;

line337:进入ExecutePlan()函数,跳转至line1517;

line1517:ExecutePlan()函数入口。还记得前面exec_simple_query()说的查询计划么?这里用上了,执行该查询计划。

line1541:进入查询计划执行的主循环;

line1549:进入ExecProcNode()函数,进入execProcnode.c文件.

#1 execProcnode.c 文件内:

该文件内提供了执行查询计划的调度函数,功能分别是:

ExecInitNode():初始化查询计划的节点以及其子查询计划;

ExecProcNode():通过执行查询计划获得元组;

ExecEndNode():关闭一个查询节点和它的子查询计划。

line367:ExecProcNode()函数入口;

line385:进入ExecResult()函数,跳转至文件nodeResult.c.

#0 nodeResult.c 文件内:

该文件主要为每个查询计划的节点提供支持。

line67:ExecResult()函数入口,该函数返回查询计划获得的元组。

这一段从#13到#0的函数调用简单分析就到这里,完全是自己的理解,如果有什么不对的地方,欢迎大家批评指正,共同进步。接下来还有函数调用的返回,这里就不细说了,留给自己和大家一起好好琢磨琢磨吧~

不得不说,postgresql的源码写的很优雅,注释也很到位,看起来很少有云山雾罩的感觉,真乃吾辈楷模。说起阅读源码,想推荐一本书,叫《代码阅读方法与实践》,书不太好找,我还是在托师弟在学校的图书馆才找到的。

另外,今天这种代码阅读方法仍然有些原始和低效,决定再看看使用Emacs的Tag或者Eclipse来调试一些更难一些的例子,这个例子毕竟比较简单。这些就留给postgresql编译安装与调试(三)来完成吧,感觉这个系列要出好多的样子呢,哈哈~

postgresql编译安装与调试(二)的更多相关文章

  1. postgresql编译安装与调试(一)

    因为最近组里的项目和postgresql有关,并且需要查看和调试源码,所以专门学习了一下如何安装和调试postgresql,此博文用来记录自己的安装和调试过程.安装环境是CentOS6(CentOS7 ...

  2. PostgreSQL编译安装

    PostgreSQL编译安装 安装语言包 ### PostgreSQL 初始化过程中,会读取操作系统字符编码, ### 若程序需要使用zh_CN.utf-8字符编码,需要在PostgreSQL 初始化 ...

  3. 在CentOS上编译安装PostgreSQL

    http://my.oschina.net/tashi/blog 第一步:准备阶段 获取必需软件包: CentOS中查看是否安装了某个软件的命令:rpm -qa | grep 软件名.which命令可 ...

  4. PostgreSQL(一) 编译安装运行

    原创,如转发需注明出处. 多年没写博客,一直用的个人笔记软件,最近准备阅读PostgreSQL源码,故记录.(这两年PostgreSQL数据库在某些环境下是比较火的,原因想必大家都清楚.) Postg ...

  5. rehat7.X下postgresql 11编译安装

    文档目录结构: 一.准备 操作系统版本:rehat7.6 Postgresql:11.2 软件安装目录:/pgsql11/basedir 数据文件存放目录:/pgsql11data/ 11.2的下载地 ...

  6. postgresql源码编译安装(centos)

    centos6.8安装postgresql-9.6.8 一.环境 centos6.8 postgresql-9.6.8 二.准备工作 虚拟机可以连接外网 三.先安装make,gcc,gcc-c++,r ...

  7. Linux环境PostgreSQL源码编译安装

    Linux环境PostgreSQL源码编译安装 Linux版本: Red Hat 6.4 PostgreSQL版本: postgresql-9.3.2.tar.gz 数据存放目录: /var/post ...

  8. ubuntu编译安装postgresql

    闲着没事用源码编译安装了postgresql,遇到了不少故障,记录一下. 1:用./configure配置时发生错误.看信息说是缺少相关包.有什么readline,zlip等. 我配置的很简单,只是配 ...

  9. Redhat 7.2 编译安装PostgreSQL 10

    1.环境说明 CentOS7.2 postgresql10.4 2.下载 postgresql的官方地址 https://www.postgresql.org/ftp/source/ 在下载列表中根据 ...

随机推荐

  1. React事件函数简介

    一.事件汇总 二.例子 <!DOCTYPE html> <html lang="zh-cn"> <head> <meta charset= ...

  2. 两个C++对象是否相等,要程序员自己下定义,通常是覆盖==操作符

    我曾经好多年对Java的==和equals的区别和联系搞不清楚,后来搞清楚了,笔记在这里: http://www.cnblogs.com/findumars/p/3240761.htmlhttp:// ...

  3. Netty4.x中文教程系列(二) Hello World !

    在中国程序界.我们都是学着Hello World !慢慢成长起来的.逐渐从一无所知到熟悉精通的. 第二章就从Hello World 开始讲述Netty的中文教程. 首先创建一个Java项目.引入一个N ...

  4. Index & Statistics ->> Rebuild Index会不会覆盖原先Index的WITH选项设置

    昨天因为工作中遇到要对某个数据库的表通通启用data_compression,突然有个念头,就是如果我当初用"ALTER INDEX XXX ON YYY REBUILD WITH (DAT ...

  5. android 更改avd路径

    第一种方法,适合还没有建立 AVD 的情况 即:在计算机右击的属性 选择环境变量,然后添加一个用户的环境变量,名字为 "ANDROID_SDK_HOME”,然后把变量值改为你想将" ...

  6. PostgreSQL的创建表

    PostgreSQL的CREATE TABLE语句是用来在任何指定的的数据库中创建一个新表. 语法 CREATE TABLE语句的基本语法如下: CREATE TABLE table_name( co ...

  7. Gliffy Diagrams 好用的流程图工具

    很好用!加上百度脑图!good!

  8. HDU 3537 (博弈 翻硬币) Daizhenyang's Coin

    可以参考Thomas S. Ferguson的<Game Theory>,网上的博客大多也是根据这个翻译过来的,第五章讲了很多关于翻硬币的博弈. 这种博弈属于Mock Turtles,它的 ...

  9. Postgresql两表联结更新

    Postgresql两表联合更新近日使用Postgresql感到有点不好用,一个联合更新非要这样写语法才对:update d_routetripset name=b.name ,    descrip ...

  10. Java 动态眨眼 EyesJPanel (整理)

    /** * Java 动态眨眼 EyesJPanel (整理) * * 2016-1-2 深圳 南山平山村 曾剑锋 * 注意事项: * 1.本程序为java程序,同时感谢您花费宝贵的时间来阅读本文档: ...