PostgreSQL中使用pstack打印fork子進程所有線程堆棧信息
PostgreSQL數據庫在並行查詢中, 出現如下"stack depth limit exceeded"的錯誤, 因此想使用pstack來打印其堆棧信息, 依次來排查錯誤.
void
check_stack_depth(void)
{
if (stack_is_too_deep())
{
ereport(ERROR,
(errcode(ERRCODE_STATEMENT_TOO_COMPLEX),
errmsg("stack depth limit exceeded"),
errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), "
"after ensuring the platform's stack depth limit is adequate.",
max_stack_depth)));
}
}
使用pstack pid 打印進程堆棧信息, 有2種方式, 一是在進程中 "pstack pid"來打印, 二是 “sudo pstack pid” 在外部打印, 外部打印需要sudo權限.
進程外部打印
下面select.sh腳本是想打印select count(*) 時, fork出來的並行查詢子進程(即: bgworker parallel )的堆棧信息, 構造了下面的腳本可以進行抓取.
create table t1(id int, info text);
select.sh
# 1. 要觸發並行查詢, 首先要確保t1中的數據量足夠的多, 如: 1千萬條, 否則不會觸發並行查詢
# 2. 並行查詢的開關要打開
# 3. 至少要2個客戶端同時查詢
# 下面在select.sh 腳本中, 通過2條select count(*)的後臺執行, 來模擬客戶端的並行查詢
./psql -p 5432 -c "select count(*) from t1;" &
echo "main wait 5432 1"
./psql -p 5432 -c "select count(*) from t1" &
echo "main wait 5432 2"
# 抓取並行子進程堆棧信息構造方法如下
# 這裏需要等待一點時間, 因爲前面的select查詢時異步執行的, 如果不等待,
# 執行到下面 ps -ef 獲取其進程pid時, 並行查詢子進程可能還沒fork出來,
# list的結果會爲空
sleep 0.1
# 打印出所有並行查詢子進程, 存放在list中
ps -ef|grep -v color|grep para
list=`ps -ef|grep -v grep|grep para|awk '{print $2}'`
echo "list is " $list
# 啓動一個循環, 逐次打印各個進程的堆棧信息
# 在具體實踐中, 這裏有2個注意事項
# 1 因爲是sudo運行, 所以在執行select.sh時, 需要輸入root密碼,
# 雖然多次循環, 但實際只需要輸入一次root密碼就夠了, 我是在centos
# 下, centos中, sudo 輸入一次root密碼, 後續短時間內如果再次sudo,
# 默認是不需要root密碼的
#
# 這裏輸入密碼的速度要快, 否則 並行子進程可能會很快執行完並結束掉,
# pstack 來不及打印
#
# 個人解決方法: 使用xshell, 在xshell中將輸入root密碼的操作添加到"
# 快速命令集"中, 然後關聯一個"Ctrl+F1"之類的快捷鍵, select.sh執行
# 時, 按下"Ctrl+F1"即可快速輸入密碼.
#
# 這樣就可以打印出其進程堆棧信息了
for pid in $list
do
echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> pid = " $pid
sudo pstack $pid
done
wait
進程內部打印
在進程內部打印, 即在程序代碼中調用 “pstack getpid()”, 這種方式不需要sudo執行.
#include <sys/types.h>
#include <unistd.h>
bool
stack_is_too_deep(void)
{
char stack_top_loc;
long stack_depth;
/*
* Compute distance from reference point to my local variables
*/
stack_depth = (long) (stack_base_ptr - &stack_top_loc);
/*
* Take abs value, since stacks grow up on some machines, down on others
*/
if (stack_depth < 0)
stack_depth = -stack_depth;
/*
* Trouble?
*
* The test on stack_base_ptr prevents us from erroring out if called
* during process setup or in a non-backend process. Logically it should
* be done first, but putting it here avoids wasting cycles during normal
* cases.
*/
if (stack_depth > max_stack_depth_bytes &&
stack_base_ptr != NULL)
{
char str[128] = {0};
sprintf(str, "pstack %d", getpid());
system(str);
return true;
}
}
備註 在上述進程內, system(str)有時候在log中的輸出爲空, 不知何故???
可能原因:
- bgworker pallell 子進程運行時間太短, 來不及打印
- system(str)的輸出沒有定向到log中