From 35d716f87611439d56f233a26f29a3b582ed3fc0 Mon Sep 17 00:00:00 2001
From: Bharath Rupireddy <bharath.rupireddyforpostgres@gmail.com>
Date: Mon, 12 Feb 2024 15:22:05 +0000
Subject: [PATCH v1] Add test module for backtrace functionality

---
 src/test/modules/Makefile                     |   1 +
 src/test/modules/meson.build                  |   1 +
 src/test/modules/test_backtrace/.gitignore    |   4 +
 src/test/modules/test_backtrace/Makefile      |  14 +++
 src/test/modules/test_backtrace/README        |   4 +
 src/test/modules/test_backtrace/meson.build   |  12 ++
 .../modules/test_backtrace/t/001_backtrace.pl | 103 ++++++++++++++++++
 7 files changed, 139 insertions(+)
 create mode 100644 src/test/modules/test_backtrace/.gitignore
 create mode 100644 src/test/modules/test_backtrace/Makefile
 create mode 100644 src/test/modules/test_backtrace/README
 create mode 100644 src/test/modules/test_backtrace/meson.build
 create mode 100644 src/test/modules/test_backtrace/t/001_backtrace.pl

diff --git a/src/test/modules/Makefile b/src/test/modules/Makefile
index 89aa41b5e3..3967311048 100644
--- a/src/test/modules/Makefile
+++ b/src/test/modules/Makefile
@@ -13,6 +13,7 @@ SUBDIRS = \
 		  libpq_pipeline \
 		  plsample \
 		  spgist_name_ops \
+		  test_backtrace \
 		  test_bloomfilter \
 		  test_copy_callbacks \
 		  test_custom_rmgrs \
diff --git a/src/test/modules/meson.build b/src/test/modules/meson.build
index 8fbe742d38..3b21b389af 100644
--- a/src/test/modules/meson.build
+++ b/src/test/modules/meson.build
@@ -12,6 +12,7 @@ subdir('libpq_pipeline')
 subdir('plsample')
 subdir('spgist_name_ops')
 subdir('ssl_passphrase_callback')
+subdir('test_backtrace')
 subdir('test_bloomfilter')
 subdir('test_copy_callbacks')
 subdir('test_custom_rmgrs')
diff --git a/src/test/modules/test_backtrace/.gitignore b/src/test/modules/test_backtrace/.gitignore
new file mode 100644
index 0000000000..5dcb3ff972
--- /dev/null
+++ b/src/test/modules/test_backtrace/.gitignore
@@ -0,0 +1,4 @@
+# Generated subdirectories
+/log/
+/results/
+/tmp_check/
diff --git a/src/test/modules/test_backtrace/Makefile b/src/test/modules/test_backtrace/Makefile
new file mode 100644
index 0000000000..b908864e89
--- /dev/null
+++ b/src/test/modules/test_backtrace/Makefile
@@ -0,0 +1,14 @@
+# src/test/modules/test_backtrace/Makefile
+
+TAP_TESTS = 1
+
+ifdef USE_PGXS
+PG_CONFIG = pg_config
+PGXS := $(shell $(PG_CONFIG) --pgxs)
+include $(PGXS)
+else
+subdir = src/test/modules/test_backtrace
+top_builddir = ../../../..
+include $(top_builddir)/src/Makefile.global
+include $(top_srcdir)/contrib/contrib-global.mk
+endif
diff --git a/src/test/modules/test_backtrace/README b/src/test/modules/test_backtrace/README
new file mode 100644
index 0000000000..ae1366bd58
--- /dev/null
+++ b/src/test/modules/test_backtrace/README
@@ -0,0 +1,4 @@
+This directory doesn't actually contain any extension module.
+
+Instead it is a home for backtrace tests that we want to run using TAP
+infrastructure.
diff --git a/src/test/modules/test_backtrace/meson.build b/src/test/modules/test_backtrace/meson.build
new file mode 100644
index 0000000000..4adffcc914
--- /dev/null
+++ b/src/test/modules/test_backtrace/meson.build
@@ -0,0 +1,12 @@
+# Copyright (c) 2024, PostgreSQL Global Development Group
+
+tests += {
+  'name': 'test_backtrace',
+  'sd': meson.current_source_dir(),
+  'bd': meson.current_build_dir(),
+  'tap': {
+    'tests': [
+      't/001_backtrace.pl',
+    ],
+  },
+}
diff --git a/src/test/modules/test_backtrace/t/001_backtrace.pl b/src/test/modules/test_backtrace/t/001_backtrace.pl
new file mode 100644
index 0000000000..ecd53dbe7b
--- /dev/null
+++ b/src/test/modules/test_backtrace/t/001_backtrace.pl
@@ -0,0 +1,103 @@
+# Copyright (c) 2024, PostgreSQL Global Development Group
+
+# Test PostgreSQL backtrace related code.
+use strict;
+use warnings FATAL => 'all';
+use PostgreSQL::Test::Cluster;
+use PostgreSQL::Test::Utils;
+use Test::More;
+
+my $node = PostgreSQL::Test::Cluster->new('node');
+$node->init;
+
+# Turn off query statement logging so that we get to see the functions in only
+# backtraces.
+$node->append_conf(
+	'postgresql.conf', qq{
+backtrace_functions = 'pg_terminate_backend, pg_create_restore_point'
+wal_level = replica
+log_statement = none
+log_min_error_statement = fatal
+});
+$node->start;
+
+# Check if backtrace generation is supported on this installation.
+my ($result, $stdout, $stderr);
+my $log_offset = -s $node->logfile;
+
+# Generate an error with negative timeout.
+($result, $stdout, $stderr) = $node->psql(
+	'postgres', q(
+	SELECT pg_terminate_backend(123, -1);
+));
+
+if ($stderr =~ m/"timeout" must not be negative/ &&
+    $node->log_contains(
+		qr/backtrace generation is not supported by this installation/,
+		$log_offset))
+{
+	plan skip_all => 'backtrace generation is not supported by this installation';
+}
+
+# Start verifying for the results only after skip_all check.
+ok( $stderr =~ m/"timeout" must not be negative/,
+	'error from terminating backend is logged');
+
+my $backtrace_text = qr/BACKTRACE:  /;
+
+ok( $node->log_contains(
+	    $backtrace_text, $log_offset),
+	"backtrace pg_terminate_backend start is found");
+
+ok( $node->log_contains(
+		"pg_terminate_backend", $log_offset),
+	"backtrace pg_terminate_backend is found");
+
+$log_offset = -s $node->logfile;
+
+# Generate an error with a long restore point name.
+($result, $stdout, $stderr) = $node->psql(
+	'postgres', q(
+	SELECT pg_create_restore_point(repeat('A', 1024));
+));
+ok( $stderr =~ m/value too long for restore point \(maximum .* characters\)/,
+	'error from restore point function is logged');
+
+ok( $node->log_contains(
+	    $backtrace_text, $log_offset),
+	"backtrace pg_create_restore_point start is found");
+
+ok( $node->log_contains(
+		"pg_create_restore_point", $log_offset),
+	"backtrace pg_create_restore_point is found");
+
+# Test if backtrace gets generated on internal errors when
+# backtrace_on_internal_error is set. Note that we use a function (i.e.
+# pg_replication_slot_advance) that generates an error with ereport without
+# setting errcode explicitly, in which case elog.c assumes it as an internal
+# error (see edata->sqlerrcode = ERRCODE_INTERNAL_ERROR; in errstart() in
+# elog.c).
+$node->append_conf('postgresql.conf', "backtrace_on_internal_error = on");
+$node->reload;
+
+$node->safe_psql('postgres',
+	"SELECT pg_create_physical_replication_slot('myslot', true);");
+
+$log_offset = -s $node->logfile;
+
+($result, $stdout, $stderr) = $node->psql(
+	'postgres', q(
+	SELECT pg_replication_slot_advance('myslot', '0/0'::pg_lsn);
+));
+ok( $stderr =~ m/invalid target WAL LSN/,
+	'error from replication slot advance is logged');
+
+ok( $node->log_contains(
+	    $backtrace_text, $log_offset),
+	"backtrace pg_replication_slot_advance start is found");
+
+ok( $node->log_contains(
+		"pg_replication_slot_advance", $log_offset),
+	"backtrace pg_replication_slot_advance is found");
+
+done_testing();
-- 
2.34.1

