首页
学习
活动
专区
圈层
工具
发布
社区首页 >专栏 >pg_stat_backtrace:让stack抓取更easy

pg_stat_backtrace:让stack抓取更easy

作者头像
NickYoung
发布2026-07-09 16:39:11
发布2026-07-09 16:39:11
20
举报

老杨用AI做了一个玩具,可以随时随地通过SQL打印PG进程堆栈或记录至日志。分享给大家。

背景介绍

先看下这几个场景:

  • 这个后端 state=active、wait_event=NULL、CPU 100% 跑了 30 分钟 — 它到底在干嘛?
  • 逻辑复制 apply worker 不再推进 — 是卡在 apply_handle_insert、触发器、还是 heap_update?
  • autovacuum worker 在某张表上跑了 6 小时 — heap 扫描、索引清理、还是表截断
  • WAL sender 不发包 — 卡在 pq_putmessage、WalSndLoop、还是 XLogRead?
  • checkpointer 慢 — BufferSync、mdsync、还是别的地方?

我相信最简单的办法肯定是观察等待事件,抓取执行堆栈来分析。

抓取stack,基本一看函数调用链路就能知道进程目前的逻辑和行为。

特别是我们当前做DBA agent,分析一些性能问题或者“进程死锁”等问题时stack这个信息源显得尤为重要, 有了stack后agent就可以结合代码精准分析。

但是,不是所有人有权限登录机器来抓取stack,并且只能临时记录到屏显无持久化日志。 当前PG自身仅支持error类型的backtrace记录到日志,且需要事先配置guc backtrace_functions。

因此我想写一个扩展,可以随时通过SQL查看PG进程stack,或者记录stack到serverlog。

依然是奴役压榨AI,以下是我给AI的需求和要求:

需求:

生成一个扩展pg_stat_backtrace,实现两个函数,pg_get_backtrace(pid)查看进程stack,pg_log_backtrace(pid)打印stack到serverlog

要求:

1、切记你是一个顶级PostgreSQL内核专家

2、一切功能在扩展中实现,不要污染主干代码

3、不要使用hook,shared memory,signal handle的方案,不要影响其他正常功能

4、要做pid入参对账和权限管理。pid一定要是本实例的,superuser可打印所有backend及辅助进程stack,普通用户仅可以打印自身或同member内进程stack

5、代码和注释完全符合PostgreSQL风格

6、不存在性能隐患、安全隐患、稳定性隐患、内存泄露、非法内存访问、进程死锁等,不和其他插件冲突

7、一定遵循事实,不要存在事实错误,表述不准确的情况

8、进行回归、TAP、压测、可靠性测试等各种场景验证确保扩展高质量

9、绝对满足官方contrib要求

10、适配PG14 - 19版本

设置定时任务,AI自行多轮验证

1、请全面检查下插件功能是否已按照需求实现

2、代码和注释是否符合PostgreSQL代码风格

3、是否还存在风险,重点排查性能隐患,安全隐患,稳定性隐患,内存泄露,非法内存访问,进程死锁,和其他插件冲突等等

4、是否还存在事实错误,表述不准确等

5、在PG14 - 19版本编译,并进行回归、TAP、压测可靠性测试等各种场景验证

6、 是否可提交至官方contrib

方案效果

最终AI采用了ptrace + libunwind的方案实现了扩展,经过N轮验证基本稳定可靠。

测试报告

二、PG14-19 全矩阵编译

版本

编译标志

警告

.so 哈希

pg14

-Wall -Wextra -Wshadow -Wstrict-prototypes -Werror

0

a438594b

pg15

同上

0

6000d0ed

pg16

同上

0

85b21464

pg17

同上

0

a68ab5df

pg18

同上

0

3ec14d7f

pg19

同上

0

0f14931f

6 份 .so 哈希互不相同,证明独立编译。

三、回归 + TAP 测试

版本

regress

TAP(5 文件 / 87 子测试)

trace 字节数

pg14

✅ All 1 passed

✅ 87/87 PASS

784

pg15

✅ 87/87

783

pg16

✅ 87/87

792

pg17

✅ 87/87

893

pg18

✅ 87/87

892

pg19

✅ 87/87

892

总计522 个 TAP 断言+ 6 个回归套件 全部通过。trace 字节数随 PG 版本调用栈深度变化,是预期。

四、内存检查

测试

结果

target 端连续 25000 次抓栈

RSS 增长80 kB(一个 page,glibc arena 抖动)

caller 端 18000 次抓栈

12000 次后0 kB 增长,libunwind 缓存预热后稳定

valgrind --leak-check=full(10 次抓栈)

0 个泄漏来自插件,1 个definitely lost来自 PG 自身save_ps_display_args(与插件无关)

五、压测可靠性

测试

结果

单机串行 10000 次(含冷启动)

平均2.682 ms/call

单线程 100 次(含 pg_stat_statements)

3.04 ms/call

8 路并发 × 200 = 1600 次抓栈

0 失败,120 s 完成

六、异常路径验证

场景

行为

statement_timeout=100超时

正确 ERROR、ptrace 已 detach

caller 中途pg_cancel_backend

正确 ERROR、target 仍 active 不受影响

cancel 后再次抓栈同一 target

✅ 正常返回 892 字节 trace

七、边界值(10 个用例全部正确)

输入

行为

NULL

NULL ✅

0-1INT32_MIN

WARNING "invalid PID" + NULL ✅

INT32_MAX / 999999999

WARNING "not a PostgreSQL server process" + NULL ✅

自身 PID

ERROR "cannot ptrace self" + hint ✅

postmaster PID

ERROR "cannot ptrace the postmaster" + hint ✅

普通用户 → 超级用户 backend

ERROR "permission denied to target backend" + DETAIL ✅

八、代码风格

维度

结果

制表符缩进

604 行 tab,0 行空格缩进

行尾空白

0

行长度 ≤ 100

100% 符合 PostgreSQL

危险函数

0处 strcpy/strcat/sprintf/gets

ereport / errcode

13 ERROR 级别 100% 带 errcode;2 WARNING 不带(与 postgres_fdw 等官方扩展做法一致,41% 官方 contrib 也如此)

PG_TRY/CATCH/END_TRY

2/2/2 完美平衡

#include 顺序

postgres.h 第一,符合规范

九、ABI 兼容性 / 插件冲突

风险点

状态

_PG_init

不存在

任何 hook 注册

0 个

GUC(DefineCustom*)

0 个

共享内存(RequestAddinShmemSpace)

0 个

PG_MODULE_MAGIC/_EXT

通过#ifdef兼容 PG14-19

ProcArrayLock 用法

LWLockAcquire(LW_SHARED) / LWLockRelease微秒级,全版本稳定 API

BackendPidGetProcWithLock

PG14-19 全版本兼容,未触碰 PG18 ProcNumber 改造影响面

结论:本插件结构上不可能与任何其他扩展冲突—— 没有全局状态、没有 hook、没有共享内存。

结果验证

pg_get_backtrace:执行sql实时获取stack

代码语言:javascript
复制
postgres=# select *,pg_get_backtrace(pid) from pg_stat_activity where backend_type in ('client backend','checkpointer','background writer') and pid <> pg_backend_pid();
-[ RECORD 1 ]----+-------------------------------------------------------------
datid            | 5
datname          | postgres
pid              | 3133583
leader_pid       | 
usesysid         | 10
usename          | postgres
application_name | psql
client_addr      | 
client_hostname  | 
client_port      | -1
backend_start    | 2026-05-12 00:16:04.383869+08
xact_start       | 
query_start      | 2026-05-12 00:16:13.353197+08
state_change     | 2026-05-12 00:16:13.353285+08
wait_event_type  | Client
wait_event       | ClientRead
state            | idle
backend_xid      | 
backend_xmin     | 
query_id         | 
query            | select1;
backend_type     | client backend
pg_get_backtrace | #0   0x00007f48d004ff57 in epoll_wait+0x17                  +
                 | #1   0x000000000091416b in WaitEventSetWait+0x12b           +
                 | #2   0x00000000007882e4 in secure_read+0xd4                 +
                 | #3   0x00000000007930ce in pq_recvbuf.lto_priv.0+0x8e       +
                 | #4   0x00000000009319e5 in PostgresMain+0x14c5              +
                 | #5   0x0000000000932ec5 in BackendMain+0x45                 +
                 | #6   0x000000000088434d in postmaster_child_launch+0x12d    +
                 | #7   0x000000000088f2ad in ServerLoop.lto_priv.0+0x52d      +
                 | #8   0x0000000000885d19 in PostmasterMain+0xce9             +
                 | #9   0x000000000052fa80 in main+0x1d0                       +
                 | #10  0x00007f48cff612a0 in __libc_start_call_main+0x80      +
                 | #11  0x00007f48cff61359 in __libc_start_main+0x89           +
                 | #12  0x0000000000530075 in _start+0x25
-[ RECORD 2 ]----+-------------------------------------------------------------
datid            | 
datname          | 
pid              | 373780
leader_pid       | 
usesysid         | 
usename          | 
application_name | 
client_addr      | 
client_hostname  | 
client_port      | 
backend_start    | 2026-05-08 11:04:19.226274+08
xact_start       | 
query_start      | 
state_change     | 
wait_event_type  | Activity
wait_event       | BgwriterHibernate
state            | 
backend_xid      | 
backend_xmin     | 
query_id         | 
query            | 
backend_type     | background writer
pg_get_backtrace | #0   0x00007f48d004ff57 in epoll_wait+0x17                  +
                 | #1   0x000000000091416b in WaitEventSetWait+0x12b           +
                 | #2   0x000000000090b6a5 in WaitLatch+0x75                   +
                 | #3   0x000000000088310d in BackgroundWriterMain+0x2ed       +
                 | #4   0x000000000088434d in postmaster_child_launch+0x12d    +
                 | #5   0x000000000088d12f in StartChildProcess.lto_priv.0+0x2f+
                 | #6   0x0000000000885dfa in PostmasterMain+0xdca             +
                 | #7   0x000000000052fa80 in main+0x1d0                       +
                 | #8   0x00007f48cff612a0 in __libc_start_call_main+0x80      +
                 | #9   0x00007f48cff61359 in __libc_start_main+0x89           +
                 | #10  0x0000000000530075 in _start+0x25
-[ RECORD 3 ]----+-------------------------------------------------------------
datid            | 
datname          | 
pid              | 373779
leader_pid       | 
usesysid         | 
usename          | 
application_name | 
client_addr      | 
client_hostname  | 
client_port      | 
backend_start    | 2026-05-08 11:04:19.225799+08
xact_start       | 
query_start      | 
state_change     | 
wait_event_type  | Activity
wait_event       | CheckpointerMain
state            | 
backend_xid      | 
backend_xmin     | 
query_id         | 
query            | 
backend_type     | checkpointer
pg_get_backtrace | #0   0x00007f48d004ff57 in epoll_wait+0x17                  +
                 | #1   0x000000000091416b in WaitEventSetWait+0x12b           +
                 | #2   0x000000000090b6a5 in WaitLatch+0x75                   +
                 | #3   0x0000000000883d03 in CheckpointerMain+0x503           +
                 | #4   0x000000000088434d in postmaster_child_launch+0x12d    +
                 | #5   0x000000000088d12f in StartChildProcess.lto_priv.0+0x2f+
                 | #6   0x0000000000885e10 in PostmasterMain+0xde0             +
                 | #7   0x000000000052fa80 in main+0x1d0                       +
                 | #8   0x00007f48cff612a0 in __libc_start_call_main+0x80      +
                 | #9   0x00007f48cff61359 in __libc_start_main+0x89           +
                 | #10  0x0000000000530075 in _start+0x25

postgres=# 

pg_log_backtrace:记录stack到serverlog

代码语言:javascript
复制
2026-05-12 00:26:06.671 CST [3139292] LOG:  backtrace of PID 373776
2026-05-12 00:26:06.671 CST [3139292] DETAIL:  #0   0x00007f48d004ff57 in epoll_wait+0x17
        #1   0x000000000091416b in WaitEventSetWait+0x12b
        #2   0x000000000090b6a5 in WaitLatch+0x75
        #3   0x00000000008ed787 in IoWorkerMain+0x4c7
        #4   0x000000000088434d in postmaster_child_launch+0x12d
        #5   0x000000000088d12f in StartChildProcess.lto_priv.0+0x2f
        #6   0x000000000088d2fb in maybe_adjust_io_workers.lto_priv.0+0x8b
        #7   0x0000000000885cd8 in PostmasterMain+0xca8
        #8   0x000000000052fa80 in main+0x1d0
        #9   0x00007f48cff612a0 in __libc_start_call_main+0x80
        #10  0x00007f48cff61359 in __libc_start_main+0x89
        #11  0x0000000000530075 in _start+0x25
2026-05-12 00:26:06.671 CST [3139292] STATEMENT:  select pg_log_backtrace(373776);

更多详情请参考:https://github.com/Nickyoung0/pg_stat_backtrace.git

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2026-05-12,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 PostgreSQL运维之道 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 背景介绍
    • 需求:
    • 要求:
    • 设置定时任务,AI自行多轮验证
  • 方案效果
    • 测试报告
    • 二、PG14-19 全矩阵编译
    • 三、回归 + TAP 测试
    • 四、内存检查
    • 五、压测可靠性
    • 六、异常路径验证
    • 七、边界值(10 个用例全部正确)
    • 八、代码风格
    • 九、ABI 兼容性 / 插件冲突
    • 结果验证
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档