PostgreSQL中使用pstack打印fork子進程所有線程堆棧信息

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中的輸出爲空, 不知何故???

可能原因:

  1. bgworker pallell 子進程運行時間太短, 來不及打印
  2. system(str)的輸出沒有定向到log中
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章