From 2a7d5d6a660bdbb04b50515e9b09bee4e8f694d8 Mon Sep 17 00:00:00 2001
From: Michael Paquier <michael@paquier.xyz>
Date: Sun, 3 Sep 2023 14:38:27 +0900
Subject: [PATCH v4 3/3] Adjust few things on top of Thomas' patch series

---
 src/backend/access/transam/xlogreader.c |  10 +-
 src/test/perl/PostgreSQL/Test/Utils.pm  |  43 +++
 src/test/recovery/t/038_end_of_wal.pl   | 386 +++++++++++++-----------
 3 files changed, 253 insertions(+), 186 deletions(-)

diff --git a/src/backend/access/transam/xlogreader.c b/src/backend/access/transam/xlogreader.c
index d5ed139131..5bc7639a00 100644
--- a/src/backend/access/transam/xlogreader.c
+++ b/src/backend/access/transam/xlogreader.c
@@ -192,6 +192,9 @@ XLogReaderFree(XLogReaderState *state)
  * XLOG_BLCKSZ, and make sure it's at least 5*Max(BLCKSZ, XLOG_BLCKSZ) to start
  * with.  (That is enough for all "normal" records, but very large commit or
  * abort records might need more space.)
+ *
+ * Note: This routine should *never* be called for xl_tot_len until the header
+ * of the record has been fully validated.
  */
 static bool
 allocate_recordbuf(XLogReaderState *state, uint32 reclength)
@@ -201,13 +204,6 @@ allocate_recordbuf(XLogReaderState *state, uint32 reclength)
 	newSize += XLOG_BLCKSZ - (newSize % XLOG_BLCKSZ);
 	newSize = Max(newSize, 5 * Max(BLCKSZ, XLOG_BLCKSZ));
 
-#ifndef FRONTEND
-
-	if (!AllocSizeIsValid(newSize))
-		return false;
-
-#endif
-
 	if (state->readRecordBuf)
 		pfree(state->readRecordBuf);
 	state->readRecordBuf =
diff --git a/src/test/perl/PostgreSQL/Test/Utils.pm b/src/test/perl/PostgreSQL/Test/Utils.pm
index 617caa022f..9de34a2260 100644
--- a/src/test/perl/PostgreSQL/Test/Utils.pm
+++ b/src/test/perl/PostgreSQL/Test/Utils.pm
@@ -71,6 +71,7 @@ our @EXPORT = qw(
   chmod_recursive
   check_pg_config
   dir_symlink
+  scan_server_header
   system_or_bail
   system_log
   run_log
@@ -702,6 +703,48 @@ sub chmod_recursive
 
 =pod
 
+=item scan_server_header(header_path, regexp)
+
+Returns an array that stores all the matches of the given regular expression
+within the PostgreSQL installation's C<header_path>.  This can be used to
+retrieve specific value patterns from the installation's header files.
+
+=cut
+
+sub scan_server_header
+{
+	my ($header_path, $regexp) = @_;
+
+	my ($stdout, $stderr);
+	my $result = IPC::Run::run [ 'pg_config', '--includedir-server' ], '>',
+	  \$stdout, '2>', \$stderr
+	  or die "could not execute pg_config";
+	chomp($stdout);
+	$stdout =~ s/\r$//;
+
+	open my $header_h, '<', "$stdout/$header_path" or die "$!";
+
+	my @match = undef;
+	while (<$header_h>)
+	{
+		my $line = $_;
+
+		if ($line =~ /^$regexp/)
+		{
+			# Found match, so store all the results.
+			@match = @{^CAPTURE};
+			last;
+		}
+	}
+
+	close $header_h;
+	die "could not find match in header $header_path\n"
+	  unless @match;
+	return @match;
+}
+
+=pod
+
 =item check_pg_config(regexp)
 
 Return the number of matches of the given regular expression
diff --git a/src/test/recovery/t/038_end_of_wal.pl b/src/test/recovery/t/038_end_of_wal.pl
index fefb5b6034..aa5eec7c03 100644
--- a/src/test/recovery/t/038_end_of_wal.pl
+++ b/src/test/recovery/t/038_end_of_wal.pl
@@ -1,6 +1,7 @@
 # Copyright (c) 2023, PostgreSQL Global Development Group
 #
-# Test ways of detecting end of WAL.
+# Test detecting end of WAL replay.  This test suite generates fake
+# WAL records able to trigger various failure scenarios at replay.
 
 use strict;
 use warnings;
@@ -9,42 +10,44 @@ use PostgreSQL::Test::Utils;
 use Test::More;
 use Fcntl qw(SEEK_SET);
 
-use integer; # causes / operator to use integer math
+use integer;    # causes / operator to use integer math
 
-# Values that could be extracted from the source tree
+# Header size of record header.
 my $RECORD_HEADER_SIZE = 24;
-my $XLP_PAGE_MAGIC = 0xd113;
-my $XLP_FIRST_IS_CONTRECORD = 0x1;
+
+# Fields retrieved from code headers.
+my @scan_result = scan_server_header('access/xlog_internal.h',
+	'#define\s+XLOG_PAGE_MAGIC\s+(\w+)');
+my $XLP_PAGE_MAGIC = hex($scan_result[0]);
+@scan_result = scan_server_header('access/xlog_internal.h',
+	'#define\s+XLP_FIRST_IS_CONTRECORD\s+(\w+)');
+my $XLP_FIRST_IS_CONTRECORD = hex($scan_result[0]);
 
 # Values queried from the server
 my $WAL_SEGMENT_SIZE;
 my $WAL_BLOCK_SIZE;
+my $TLI;
 
+# Build path of a WAL segment.
 sub wal_segment_path
 {
 	my $node = shift;
 	my $tli = shift;
 	my $segment = shift;
-	my $wal_path = sprintf("%s/pg_wal/%08X%08X%08X",
-						   $node->data_dir,
-						   $tli,
-						   0,
-						   $segment);
+	my $wal_path =
+	  sprintf("%s/pg_wal/%08X%08X%08X", $node->data_dir, $tli, 0, $segment);
 	return $wal_path;
 }
 
+# Calculate from a LSN (in bytes) its segment number and its offset.
 sub lsn_to_segment_and_offset
 {
 	my $lsn = shift;
 	return ($lsn / $WAL_SEGMENT_SIZE, $lsn % $WAL_SEGMENT_SIZE);
 }
 
-sub get_log_size
-{
-	my $node = shift;
-	return (stat $node->logfile)[7];
-}
-
+# Write some arbitrary data in WAL for the given segment at offset.
+# This should be called while the cluster is offline.
 sub write_wal
 {
 	my $node = shift;
@@ -61,30 +64,37 @@ sub write_wal
 	close $fh;
 }
 
+# Emit a WAL record of arbitrary size.  Returns the end LSN of the
+# record inserted, in bytes.
 sub emit_message
 {
 	my $node = shift;
 	my $size = shift;
-	return int($node->safe_psql('postgres', "select pg_logical_emit_message(true, '', repeat('a', $size)) - '0/0'"));
+	return int(
+		$node->safe_psql(
+			'postgres',
+			"SELECT pg_logical_emit_message(true, '', repeat('a', $size)) - '0/0'"
+		));
 }
 
+# Get the current insert LSN of a node, in bytes.
 sub get_insert_lsn
 {
 	my $node = shift;
-	return int($node->safe_psql('postgres', "select pg_current_wal_insert_lsn() - '0/0'"));
+	return int(
+		$node->safe_psql(
+			'postgres', "SELECT pg_current_wal_insert_lsn() - '0/0'"));
 }
 
+# Get GUC value, converted to an int.
 sub get_int_setting
 {
 	my $node = shift;
 	my $name = shift;
-	return int($node->safe_psql('postgres', "select setting from pg_settings where name = '$name'"));
-}
-
-sub format_lsn
-{
-	my $lsn = shift;
-	return sprintf("%X/%X", $lsn >> 32, $lsn & 0xffffffff);
+	return int(
+		$node->safe_psql(
+			'postgres',
+			"SELECT setting FROM pg_settings WHERE name = '$name'"));
 }
 
 sub start_of_page
@@ -99,7 +109,10 @@ sub start_of_next_page
 	return start_of_page($lsn) + $WAL_BLOCK_SIZE;
 }
 
-sub record_header
+# Build a fake WAL record header based on the data given by the caller.
+# This needs to follow the format of the C structure XLogRecord.  To
+# be inserted with write_wal().
+sub build_record_header
 {
 	my $xl_tot_len = shift;
 	my $xl_xid = shift || 0;
@@ -108,19 +121,22 @@ sub record_header
 	my $xl_rmid = shift || 0;
 	my $xl_crc = shift || 0;
 
-	# struct XLogRecord
+	# This needs to follow the structure XLogRecord:
+	# I for xl_tot_len
+	# I for xl_xid
+	# Q for xl_prev
+	# C for xl_info
+	# C for xl_rmid
+	# BB for two bytes of padding
+	# I for xl_crc
 	return pack("IIQCCBBI",
-				$xl_tot_len,
-				$xl_xid,
-				$xl_prev,
-				$xl_info,
-				$xl_rmid,
-				0,
-				0,
-				$xl_crc);
+		$xl_tot_len, $xl_xid, $xl_prev, $xl_info, $xl_rmid, 0, 0, $xl_crc);
 }
 
-sub page_header
+# Build a fake WAL page header, based on the data given by the caller
+# This needs to follow the format of the C structure XLogPageHeaderData.
+# To be inserted with write_wal().
+sub build_page_header
 {
 	my $xlp_magic = shift;
 	my $xlp_info = shift || 0;
@@ -128,32 +144,37 @@ sub page_header
 	my $xlp_pageaddr = shift || 0;
 	my $xlp_rem_len = shift || 0;
 
-	# struct XLogPageHeaderData
+	# This needs to follow the structure XLogPageHeaderData:
+	# S for xlp_magic
+	# S for xlp_info
+	# I for xlp_tli
+	# Q for xlp_pageaddr
+	# I for xlp_rem_len
 	return pack("SSIQI",
-				$xlp_magic,
-				$xlp_info,
-				$xlp_tli,
-				$xlp_pageaddr,
-				$xlp_rem_len);
+		$xlp_magic, $xlp_info, $xlp_tli, $xlp_pageaddr, $xlp_rem_len);
 }
 
 # Make sure we are far away enough from the end of a page that we could insert
-# a couple of small records.
+# a couple of small records.  This inserts a few records of a fixed size, until
+# the threshold gets close enough to the end of the WAL page inserting records
+# to.
 sub advance_out_of_record_splitting_zone
 {
 	my $node = shift;
 
+	my $page_threshold = $WAL_BLOCK_SIZE / 4;
 	my $end_lsn = get_insert_lsn($node);
 	my $page_offset = $end_lsn % $WAL_BLOCK_SIZE;
-	while ($page_offset >= $WAL_BLOCK_SIZE - 2000)
+	while ($page_offset >= $WAL_BLOCK_SIZE - $page_threshold)
 	{
-		$end_lsn = emit_message($node, 2000);
+		$end_lsn = emit_message($node, $page_threshold);
 		$page_offset = $end_lsn % $WAL_BLOCK_SIZE;
 	}
 	return $end_lsn;
 }
 
-# Advance so close to the end of a page that an XLogRecordHeader wouldn't fit.
+# Advance so close to the end of a page that an XLogRecordHeader would not
+# fit on it.
 sub advance_to_record_splitting_zone
 {
 	my $node = shift;
@@ -161,15 +182,15 @@ sub advance_to_record_splitting_zone
 	my $end_lsn = get_insert_lsn($node);
 	my $page_offset = $end_lsn % $WAL_BLOCK_SIZE;
 
-	# get fairly close to the end of a page in big steps
+	# Get fairly close to the end of a page in big steps
 	while ($page_offset <= $WAL_BLOCK_SIZE - 512)
 	{
 		$end_lsn = emit_message($node, $WAL_BLOCK_SIZE - $page_offset - 256);
 		$page_offset = $end_lsn % $WAL_BLOCK_SIZE;
 	}
 
-	# calibrate our message size so that we can get closer 8 bytes at
-	# a time (XXX something better is surely possible)
+	# Calibrate our message size so that we can get closer 8 bytes at
+	# a time.
 	my $message_size = $WAL_BLOCK_SIZE - 80;
 	while ($page_offset <= $WAL_BLOCK_SIZE - $RECORD_HEADER_SIZE)
 	{
@@ -178,8 +199,8 @@ sub advance_to_record_splitting_zone
 		my $old_offset = $page_offset;
 		$page_offset = $end_lsn % $WAL_BLOCK_SIZE;
 
-		# adjust the message size until it causes 8 bytes changes in
-		# offset
+		# Adjust the message size until it causes 8 bytes changes in
+		# offset, enough to be able to split a record header.
 		my $delta = $page_offset - $old_offset;
 		if ($delta > 8)
 		{
@@ -193,223 +214,230 @@ sub advance_to_record_splitting_zone
 	return $end_lsn;
 }
 
-
+# Setup a new node.  The configuration chosen here minimizes the number
+# of arbitrary records that could get generated in a cluster.  Enlarging
+# checkpoint_timeout avoids noise with checkpoint activity.  wal_level
+# set to "minimal" avoids random standby snapshot records.  Autovacuum
+# could also trigger randomly, generating random WAL activity of its own.
 my $node = PostgreSQL::Test::Cluster->new("node");
 $node->init;
-$node->append_conf('postgresql.conf',
-				   q[wal_level = minimal
+$node->append_conf(
+	'postgresql.conf',
+	q[wal_level = minimal
 					 autovacuum = off
 					 checkpoint_timeout = '30min'
 ]);
 $node->start;
-$node->safe_psql('postgres', "create table t as select 42");
+$node->safe_psql('postgres', "CREATE TABLE t AS SELECT 42");
 
 $WAL_SEGMENT_SIZE = get_int_setting($node, 'wal_segment_size');
 $WAL_BLOCK_SIZE = get_int_setting($node, 'wal_block_size');
-
+$TLI = $node->safe_psql('postgres',
+	"SELECT timeline_id FROM pg_control_checkpoint();");
 
 my $end_lsn;
 my $prev_lsn;
-my $log_size;
 
 ###########################################################################
 note "Single-page end-of-WAL detection";
 ###########################################################################
 
-# xl_tot_len is 0 (a common case, we hit trailing zeroes)
+# xl_tot_len is 0 (a common case, we hit trailing zeroes).
 $end_lsn = advance_out_of_record_splitting_zone($node);
 $node->stop('immediate');
-$log_size = get_log_size($node);
+my $log_size = -s $node->logfile;
 $node->start;
-ok($node->log_contains("invalid record length at .*: expected at least 24, got 0",
-					   $log_size),
-   "xl_tot_len zero");
+ok( $node->log_contains(
+		"invalid record length at .*: expected at least 24, got 0", $log_size
+	),
+	"xl_tot_len zero");
 
-# xl_tot_len is < 24 (presumably recycled garbage)
+# xl_tot_len is < 24 (presumably recycled garbage).
 $end_lsn = advance_out_of_record_splitting_zone($node);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn, record_header(23));
-$log_size = get_log_size($node);
+write_wal($node, $TLI, $end_lsn, build_record_header(23));
+$log_size = -s $node->logfile;
 $node->start;
-ok($node->log_contains("invalid record length at .*: expected at least 24, got 23",
-					   $log_size),
-   "xl_tot_len short");
+ok( $node->log_contains(
+		"invalid record length at .*: expected at least 24, got 23",
+		$log_size),
+	"xl_tot_len short");
 
-# need more pages, but xl_prev check fails first
+# Need more pages, but xl_prev check fails first.
 $end_lsn = advance_out_of_record_splitting_zone($node);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024,
-											0,
-											0xdeadbeef));
-$log_size = get_log_size($node);
+write_wal($node, $TLI, $end_lsn,
+	build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef));
+$log_size = -s $node->logfile;
 $node->start;
-ok($node->log_contains("record with incorrect prev-link 0/DEADBEEF at .*",
-					   $log_size),
-   "xl_prev bad");
+ok( $node->log_contains(
+		"record with incorrect prev-link 0/DEADBEEF at .*", $log_size),
+	"xl_prev bad");
 
-# xl_crc check fails
+# xl_crc check fails.
 advance_out_of_record_splitting_zone($node);
 $end_lsn = emit_message($node, 10);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn - 8, '!'); # corrupt a byte in that record
-$log_size = get_log_size($node);
+# Corrupt a byte in that record, breaking its CRC.
+write_wal($node, $TLI, $end_lsn - 8, '!');
+$log_size = -s $node->logfile;
 $node->start;
-ok($node->log_contains("incorrect resource manager data checksum in record at .*",
-					   $log_size),
-   "xl_crc bad");
+ok( $node->log_contains(
+		"incorrect resource manager data checksum in record at .*", $log_size
+	),
+	"xl_crc bad");
 
 
 ###########################################################################
 note "Multi-page end-of-WAL detection, header is not split";
 ###########################################################################
 
-# good xl_prev, we hit zero page next (zero magic)
+# This series of tests requires a valid xl_prev set in the record header
+# written to WAL.
+
+# Good xl_prev, we hit zero page next (zero magic).
 $prev_lsn = advance_out_of_record_splitting_zone($node);
-#my $prev_lsn = emit_message($node, 0);
 $end_lsn = emit_message($node, 0);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024,
-											0,
-											$prev_lsn));
-$log_size = get_log_size($node);
+write_wal($node, $TLI, $end_lsn,
+	build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn));
+$log_size = -s $node->logfile;
 $node->start;
 ok($node->log_contains("invalid magic number 0000 .* LSN .*", $log_size),
-  "xlp_magic zero");
+	"xlp_magic zero");
 
-# good xl_prev, we hit garbage page next (bad magic)
+# Good xl_prev, we hit garbage page next (bad magic).
 $prev_lsn = advance_out_of_record_splitting_zone($node);
 $end_lsn = emit_message($node, 0);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024,
-											0,
-											$prev_lsn));
-write_wal($node, 1, start_of_next_page($end_lsn),
-		  page_header(0xcafe,
-					  0,
-					  1,
-					  0));
-$log_size = get_log_size($node);
+write_wal($node, $TLI, $end_lsn,
+	build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn));
+write_wal(
+	$node, $TLI,
+	start_of_next_page($end_lsn),
+	build_page_header(0xcafe, 0, 1, 0));
+$log_size = -s $node->logfile;
 $node->start;
 ok($node->log_contains("invalid magic number CAFE .* LSN .*", $log_size),
-  "xlp_magic bad");
+	"xlp_magic bad");
 
-# good xl_prev, we hit typical recycled page (good xlp_magic, bad xlp_pageaddr)
+# Good xl_prev, we hit typical recycled page (good xlp_magic, bad
+# xlp_pageaddr).
 $prev_lsn = advance_out_of_record_splitting_zone($node);
 $end_lsn = emit_message($node, 0);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024,
-											0,
-											$prev_lsn));
-write_wal($node, 1, start_of_next_page($end_lsn),
-		  page_header($XLP_PAGE_MAGIC,
-					  0,
-					  1,
-					  0xbaaaaaad));
-$log_size = get_log_size($node);
+write_wal($node, $TLI, $end_lsn,
+	build_record_header(2 * 1024 * 1024 * 1024, 0, $prev_lsn));
+write_wal(
+	$node, $TLI,
+	start_of_next_page($end_lsn),
+	build_page_header($XLP_PAGE_MAGIC, 0, 1, 0xbaaaaaad));
+$log_size = -s $node->logfile;
 $node->start;
-ok($node->log_contains("unexpected pageaddr 0/BAAAAAAD in .*, LSN .*,", $log_size),
-   "xlp_pageaddr bad");
+ok( $node->log_contains(
+		"unexpected pageaddr 0/BAAAAAAD in .*, LSN .*,", $log_size),
+	"xlp_pageaddr bad");
 
-# good xl_prev, xlp_magic, xlp_pageaddr, but bogus xlp_info
+# Good xl_prev, xlp_magic, xlp_pageaddr, but bogus xlp_info.
 $prev_lsn = advance_out_of_record_splitting_zone($node);
 $end_lsn = emit_message($node, 0);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024,
-											42,
-											$prev_lsn));
-write_wal($node, 1, start_of_next_page($end_lsn),
-		  page_header($XLP_PAGE_MAGIC,
-					  0x1234,
-					  1,
-					  start_of_next_page($end_lsn)));
-$log_size = get_log_size($node);
+write_wal($node, $TLI, $end_lsn,
+	build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn));
+write_wal(
+	$node, $TLI,
+	start_of_next_page($end_lsn),
+	build_page_header(
+		$XLP_PAGE_MAGIC, 0x1234, 1, start_of_next_page($end_lsn)));
+$log_size = -s $node->logfile;
 $node->start;
 ok($node->log_contains("invalid info bits 1234 in .*, LSN .*,", $log_size),
-   "xlp_info bad");
+	"xlp_info bad");
 
-# good xl_prev, xlp_magic, xlp_pageaddr, but xlp_info doesn't mention cont record
+# Good xl_prev, xlp_magic, xlp_pageaddr, but xlp_info doesn't mention
+# continuation record.
 $prev_lsn = advance_out_of_record_splitting_zone($node);
 $end_lsn = emit_message($node, 0);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024,
-											42,
-											$prev_lsn));
-write_wal($node, 1, start_of_next_page($end_lsn),
-		  page_header($XLP_PAGE_MAGIC,
-					  0,
-					  1,
-					  start_of_next_page($end_lsn)));
-$log_size = get_log_size($node);
+write_wal($node, $TLI, $end_lsn,
+	build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn));
+write_wal(
+	$node, $TLI,
+	start_of_next_page($end_lsn),
+	build_page_header($XLP_PAGE_MAGIC, 0, 1, start_of_next_page($end_lsn)));
+$log_size = -s $node->logfile;
 $node->start;
 ok($node->log_contains("there is no contrecord flag at .*", $log_size),
-  "xlp_info lacks XLP_FIRST_IS_CONTRECORD");
+	"xlp_info lacks XLP_FIRST_IS_CONTRECORD");
 
-# good xl_prev, xlp_magic, xlp_pageaddr, xlp_info but xlp_rem_len doesn't add up
+# Good xl_prev, xlp_magic, xlp_pageaddr, xlp_info but xlp_rem_len doesn't add
+# up.
 $prev_lsn = advance_out_of_record_splitting_zone($node);
 $end_lsn = emit_message($node, 0);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024,
-											42,
-											$prev_lsn));
-write_wal($node, 1, start_of_next_page($end_lsn),
-		  page_header($XLP_PAGE_MAGIC,
-					  $XLP_FIRST_IS_CONTRECORD,
-					  1,
-					  start_of_next_page($end_lsn),
-					  123456));
-$log_size = get_log_size($node);
+write_wal($node, $TLI, $end_lsn,
+	build_record_header(2 * 1024 * 1024 * 1024, 42, $prev_lsn));
+write_wal(
+	$node, $TLI,
+	start_of_next_page($end_lsn),
+	build_page_header(
+		$XLP_PAGE_MAGIC, $XLP_FIRST_IS_CONTRECORD,
+		1, start_of_next_page($end_lsn),
+		123456));
+$log_size = -s $node->logfile;
 $node->start;
-ok($node->log_contains("invalid contrecord length 123456 .* at .*", $log_size),
-   "xlp_rem_len bad");
+ok( $node->log_contains(
+		"invalid contrecord length 123456 .* at .*", $log_size),
+	"xlp_rem_len bad");
 
 
 ###########################################################################
 note "Multi-page, but header is split, so page checks are done first";
 ###########################################################################
 
-# xlp_prev is bad and xl_tot_len is too big, but we'll check xlp_magic first
+# xlp_prev is bad and xl_tot_len is too big, but we'll check xlp_magic first.
 $end_lsn = advance_to_record_splitting_zone($node);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024,
-											0,
-											0xdeadbeef));
-$log_size = get_log_size($node);
+write_wal($node, $TLI, $end_lsn,
+	build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef));
+$log_size = -s $node->logfile;
 $node->start;
 ok($node->log_contains("invalid magic number 0000 .* LSN .*", $log_size),
-   "xlp_magic zero (split record header)");
+	"xlp_magic zero (split record header)");
 
-# and we'll also check xlp_pageaddr before any header checks
+# And we'll also check xlp_pageaddr before any header checks.
 $end_lsn = advance_to_record_splitting_zone($node);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024,
-											0,
-											0xdeadbeef));
-write_wal($node, 1, start_of_next_page($end_lsn),
-		  page_header($XLP_PAGE_MAGIC,
-					  $XLP_FIRST_IS_CONTRECORD,
-					  1,
-					  0xbaaaaaad));
-$log_size = get_log_size($node);
+write_wal($node, $TLI, $end_lsn,
+	build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef));
+write_wal(
+	$node, $TLI,
+	start_of_next_page($end_lsn),
+	build_page_header(
+		$XLP_PAGE_MAGIC, $XLP_FIRST_IS_CONTRECORD, 1, 0xbaaaaaad));
+$log_size = -s $node->logfile;
 $node->start;
-ok($node->log_contains("unexpected pageaddr 0/BAAAAAAD in .*, LSN .*,", $log_size),
-   "xlp_pageaddr bad (split record header)");
+ok( $node->log_contains(
+		"unexpected pageaddr 0/BAAAAAAD in .*, LSN .*,", $log_size),
+	"xlp_pageaddr bad (split record header)");
 
-# we'll also discover that xlp_rem_len doesn't add up before any
-# header checks
+# We'll also discover that xlp_rem_len doesn't add up before any
+# header checks,
 $end_lsn = advance_to_record_splitting_zone($node);
 $node->stop('immediate');
-write_wal($node, 1, $end_lsn, record_header(2 * 1024 * 1024 * 1024,
-											0,
-											0xdeadbeef));
-write_wal($node, 1, start_of_next_page($end_lsn),
-		  page_header($XLP_PAGE_MAGIC,
-					  $XLP_FIRST_IS_CONTRECORD,
-					  1,
-					  start_of_next_page($end_lsn),
-					  123456));
-$log_size = get_log_size($node);
+write_wal($node, $TLI, $end_lsn,
+	build_record_header(2 * 1024 * 1024 * 1024, 0, 0xdeadbeef));
+write_wal(
+	$node, $TLI,
+	start_of_next_page($end_lsn),
+	build_page_header(
+		$XLP_PAGE_MAGIC, $XLP_FIRST_IS_CONTRECORD,
+		1, start_of_next_page($end_lsn),
+		123456));
+$log_size = -s $node->logfile;
 $node->start;
-ok($node->log_contains("invalid contrecord length 123456 .* at .*", $log_size),
-   "xlp_rem_len bad (split record header)");
+ok( $node->log_contains(
+		"invalid contrecord length 123456 .* at .*", $log_size),
+	"xlp_rem_len bad (split record header)");
 
 done_testing();
-- 
2.40.1

