Postgres 오류를 스택 트레이스로 분석하기: backtrace_functions 활용

작성자: Phil Eaton
작성일: 2025년 7월 31일

Postgres가 크래시(crash)하면 gdb를 사용해 스택 트레이스를 확인할 수 있습니다.
그렇다면 Postgres가 크래시하지 않는 일반적인 오류는 어떻게 디버깅할 수 있을까요?


1. Postgres 17 소스 코드 빌드

먼저 Postgres 17 소스를 가져와 디버그 심볼을 포함해 빌드해 보겠습니다.

$ git clone https://github.com/postgres/postgres
$ cd postgres
$ git checkout REL_17_STABLE
$ ./configure --enable-debug --without-icu \
--prefix=$(pwd)/build \
--libdir=$(pwd)/build/lib
$ make -j16 && make install


2. 데이터베이스 생성 및 시작

$ ./build/bin/initdb testdb
$ ./build/bin/pg_ctl -D $(pwd)/testdb -l logfile start


3. psql로 접속

$ ./build/bin/psql postgres
psql (17.5)
Type "help" for help.
postgres=#


4. 간단한 오류 발생시키기

존재하지 않는 테이블을 조회해 오류를 발생시킵니다.

postgres=# SELECT * FROM nothere;
ERROR: relation "nothere" does not exist
LINE 1: SELECT * FROM nothere;
^
postgres=#

이 오류 자체는 디버깅할 필요가 없는 단순한 ERROR이지만, 여기서는 과정을 설명하기 위해 사용하겠습니다.


5. 예전 방식: 문자열 검색으로 원인 찾기

예전에는 이런 오류 메시지가 나오면, 메시지의 고정된 문자열 부분을 추출해 git grep으로 검색하곤 했습니다.

$ git grep '"relation' | grep 'does not exist"' | grep '.c:'
src/backend/catalog/aclchk.c: errmsg("relation with OID %u does not exist",
src/backend/catalog/aclchk.c: errmsg("relation with OID %u does not exist",
src/backend/catalog/aclchk.c: errmsg("relation with OID %u does not exist",
src/backend/catalog/namespace.c: errmsg("relation \"%s.%s\" does not exist",
src/backend/catalog/namespace.c: errmsg("relation \"%s\" does not exist",
src/backend/parser/parse_relation.c: errmsg("relation \"%s.%s\" does not exist",
src/backend/parser/parse_relation.c: errmsg("relation \"%s\" does not exist",
src/backend/parser/parse_relation.c: errmsg("relation \"%s\" does not exist",
src/backend/utils/adt/regproc.c: errmsg("relation \"%s\" does not exist",
src/pl/plpgsql/src/pl_comp.c: errmsg("relation \"%s\" does not exist", ident)));


이렇게 문자열 처리와 grep을 잘 조합하면 원인을 찾을 수도 있지만, 찾지 못할 수도 있습니다.


6. VERBOSITY 옵션 활용

보다 직접적인 방법은 psql에서 VERBOSITYverbose로 설정하는 것입니다.
이렇게 하면 오류가 발생한 함수명과 파일 위치를 알려주며, 이 정보만으로도 상당 부분 문제를 해결할 수 있습니다.

postgres=# \set VERBOSITY verbose
postgres=# SELECT * FROM nothere;
ERROR: 42P01: relation "nothere" does not exist
LINE 1: SELECT * FROM nothere;
^
LOCATION: parserOpenTable, parse_relation.c:1452


7. backtrace_functions GUC로 스택 트레이스 출력

최근 온콜(on-call) 중에 backtrace_functions라는 GUC를 알게 되었습니다.
이 기능을 사용하면 Postgres가 오류 발생 시 스택 트레이스를 로그로 남길 수 있습니다. 정말 유용하죠!

단, 로그를 발생시키는 모든 함수에 대해 무차별적으로 스택 트레이스를 출력하면 로그가 스팸처럼 쌓일 수 있으므로, 원하는 함수만 지정해야 합니다.

앞서 VERBOSITY verbose 설정 덕분에 오류가 발생한 정확한 함수(parserOpenTable)를 알았으니, 이를 backtrace_functions에 지정하고 Postgres 설정을 재로드합니다.

postgres=# ALTER SYSTEM SET backtrace_functions='parserOpenTable';
ALTER SYSTEM
postgres=# SELECT pg_reload_conf();
pg_reload_conf
----------------
t
(1 row)


8. 다시 오류 실행 후 로그 확인

postgres=# SELECT * FROM nothere;
ERROR: 42P01: relation "nothere" does not exist
LINE 1: SELECT * FROM nothere;
^
LOCATION: parserOpenTable, parse_relation.c:1452

이제 로그 파일에서 해당 오류의 스택 트레이스를 확인할 수 있습니다.

2025-07-31 13:46:42.657 EDT [84985] ERROR: relation "nothere" does not exist at character 15
2025-07-31 13:46:42.657 EDT [84985] BACKTRACE:
2 postgres 0x000000010499a0d0 parserOpenTable.cold.1 + 156
3 postgres 0x00000001045d64cc parserOpenTable + 124
4 postgres 0x00000001045d666c addRangeTableEntry + 272
5 postgres 0x00000001045c2744 transformFromClauseItem + 256
6 postgres 0x00000001045c25a8 transformFromClause + 132
7 postgres 0x00000001045ad3fc transformSelectStmt + 128
8 postgres 0x00000001045ac858 transformStmt + 4316
9 postgres 0x00000001045ab344 parse_analyze_fixedparams + 192
10 postgres 0x00000001048003a8 exec_simple_query + 1024
11 postgres 0x00000001047fe2a8 PostgresMain + 3108
12 postgres 0x00000001047fa08c BackendInitialize + 0
13 postgres 0x000000010476f22c PgArchShmemSize + 0
14 postgres 0x0000000104773308 ServerLoop + 6772
15 postgres 0x0000000104770d38 PostmasterMain + 3436
16 postgres 0x00000001046a5844 main + 800
17 dyld 0x0000000196993154 start + 2476


9. 향후 개선 가능성

현재는 특정 함수를 지정해야만 스택 트레이스를 출력할 수 있지만, 모든 오류에 대해 자동으로 스택 트레이스를 출력하는 패치도 제안되어 있습니다.
이는 오류 발생 빈도가 낮고 디버깅이 번거로운 자동화 테스트 환경에서 특히 유용할 것입니다.
앞으로 이 기능이 Postgres에 공식 반영되기를 기대합니다.

메일: salesinquiry@enterprisedb.com