PostgreSQL Source Code  git master
pg_waldump.c File Reference
#include "postgres.h"
#include <dirent.h>
#include <limits.h>
#include <signal.h>
#include <sys/stat.h>
#include <unistd.h>
#include "access/transam.h"
#include "access/xlog_internal.h"
#include "access/xlogreader.h"
#include "access/xlogrecord.h"
#include "access/xlogstats.h"
#include "common/fe_memutils.h"
#include "common/file_perm.h"
#include "common/file_utils.h"
#include "common/logging.h"
#include "common/relpath.h"
#include "getopt_long.h"
#include "rmgrdesc.h"
#include "storage/bufpage.h"
Include dependency graph for pg_waldump.c:

Go to the source code of this file.

Data Structures

struct  XLogDumpPrivate
 
struct  XLogDumpConfig
 

Macros

#define FRONTEND   1
 

Typedefs

typedef struct XLogDumpPrivate XLogDumpPrivate
 
typedef struct XLogDumpConfig XLogDumpConfig
 

Functions

static void sigint_handler (SIGNAL_ARGS)
 
static void print_rmgr_list (void)
 
static bool verify_directory (const char *directory)
 
static void create_fullpage_directory (char *path)
 
static void split_path (const char *path, char **dir, char **fname)
 
static int open_file_in_directory (const char *directory, const char *fname)
 
static bool search_directory (const char *directory, const char *fname)
 
static char * identify_target_directory (char *directory, char *fname)
 
static void WALDumpOpenSegment (XLogReaderState *state, XLogSegNo nextSegNo, TimeLineID *tli_p)
 
static void WALDumpCloseSegment (XLogReaderState *state)
 
static int WALDumpReadPage (XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetPtr, char *readBuff)
 
static bool XLogRecordMatchesRelationBlock (XLogReaderState *record, RelFileLocator matchRlocator, BlockNumber matchBlock, ForkNumber matchFork)
 
static bool XLogRecordHasFPW (XLogReaderState *record)
 
static void XLogRecordSaveFPWs (XLogReaderState *record, const char *savepath)
 
static void XLogDumpDisplayRecord (XLogDumpConfig *config, XLogReaderState *record)
 
static void XLogDumpStatsRow (const char *name, uint64 n, uint64 total_count, uint64 rec_len, uint64 total_rec_len, uint64 fpi_len, uint64 total_fpi_len, uint64 tot_len, uint64 total_len)
 
static void XLogDumpDisplayStats (XLogDumpConfig *config, XLogStats *stats)
 
static void usage (void)
 
int main (int argc, char **argv)
 

Variables

static const char * progname
 
static int WalSegSz
 
static volatile sig_atomic_t time_to_stop = false
 
static const RelFileLocator emptyRelFileLocator = {0, 0, 0}
 

Macro Definition Documentation

◆ FRONTEND

#define FRONTEND   1

Definition at line 12 of file pg_waldump.c.

Typedef Documentation

◆ XLogDumpConfig

◆ XLogDumpPrivate

Function Documentation

◆ create_fullpage_directory()

static void create_fullpage_directory ( char *  path)
static

Definition at line 128 of file pg_waldump.c.

129 {
130  int ret;
131 
132  switch ((ret = pg_check_dir(path)))
133  {
134  case 0:
135  /* Does not exist, so create it */
136  if (pg_mkdir_p(path, pg_dir_create_mode) < 0)
137  pg_fatal("could not create directory \"%s\": %m", path);
138  break;
139  case 1:
140  /* Present and empty, so do nothing */
141  break;
142  case 2:
143  case 3:
144  case 4:
145  /* Exists and not empty */
146  pg_fatal("directory \"%s\" exists but is not empty", path);
147  break;
148  default:
149  /* Trouble accessing directory */
150  pg_fatal("could not access directory \"%s\": %m", path);
151  }
152 }
int pg_dir_create_mode
Definition: file_perm.c:18
#define pg_fatal(...)
int pg_mkdir_p(char *path, int omode)
Definition: pgmkdirp.c:57
int pg_check_dir(const char *dir)
Definition: pgcheckdir.c:33

References pg_check_dir(), pg_dir_create_mode, pg_fatal, and pg_mkdir_p().

Referenced by main().

◆ identify_target_directory()

static char* identify_target_directory ( char *  directory,
char *  fname 
)
static

Definition at line 288 of file pg_waldump.c.

289 {
290  char fpath[MAXPGPATH];
291 
292  if (directory != NULL)
293  {
294  if (search_directory(directory, fname))
295  return pg_strdup(directory);
296 
297  /* directory / XLOGDIR */
298  snprintf(fpath, MAXPGPATH, "%s/%s", directory, XLOGDIR);
299  if (search_directory(fpath, fname))
300  return pg_strdup(fpath);
301  }
302  else
303  {
304  const char *datadir;
305 
306  /* current directory */
307  if (search_directory(".", fname))
308  return pg_strdup(".");
309  /* XLOGDIR */
310  if (search_directory(XLOGDIR, fname))
311  return pg_strdup(XLOGDIR);
312 
313  datadir = getenv("PGDATA");
314  /* $PGDATA / XLOGDIR */
315  if (datadir != NULL)
316  {
317  snprintf(fpath, MAXPGPATH, "%s/%s", datadir, XLOGDIR);
318  if (search_directory(fpath, fname))
319  return pg_strdup(fpath);
320  }
321  }
322 
323  /* could not locate WAL file */
324  if (fname)
325  pg_fatal("could not locate WAL file \"%s\"", fname);
326  else
327  pg_fatal("could not find any WAL file");
328 
329  return NULL; /* not reached */
330 }
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
#define MAXPGPATH
char * datadir
static bool search_directory(const char *directory, const char *fname)
Definition: pg_waldump.c:210
#define snprintf
Definition: port.h:238
#define XLOGDIR
static const char * directory
Definition: zic.c:634

References datadir, directory, MAXPGPATH, pg_fatal, pg_strdup(), search_directory(), snprintf, and XLOGDIR.

Referenced by main().

◆ main()

int main ( int  argc,
char **  argv 
)

Definition at line 787 of file pg_waldump.c.

788 {
789  uint32 xlogid;
790  uint32 xrecoff;
791  XLogReaderState *xlogreader_state;
792  XLogDumpPrivate private;
793  XLogDumpConfig config;
794  XLogStats stats;
795  XLogRecord *record;
796  XLogRecPtr first_record;
797  char *waldir = NULL;
798  char *errormsg;
799 
800  static struct option long_options[] = {
801  {"bkp-details", no_argument, NULL, 'b'},
802  {"block", required_argument, NULL, 'B'},
803  {"end", required_argument, NULL, 'e'},
804  {"follow", no_argument, NULL, 'f'},
805  {"fork", required_argument, NULL, 'F'},
806  {"fullpage", no_argument, NULL, 'w'},
807  {"help", no_argument, NULL, '?'},
808  {"limit", required_argument, NULL, 'n'},
809  {"path", required_argument, NULL, 'p'},
810  {"quiet", no_argument, NULL, 'q'},
811  {"relation", required_argument, NULL, 'R'},
812  {"rmgr", required_argument, NULL, 'r'},
813  {"start", required_argument, NULL, 's'},
814  {"timeline", required_argument, NULL, 't'},
815  {"xid", required_argument, NULL, 'x'},
816  {"version", no_argument, NULL, 'V'},
817  {"stats", optional_argument, NULL, 'z'},
818  {"save-fullpage", required_argument, NULL, 1},
819  {NULL, 0, NULL, 0}
820  };
821 
822  int option;
823  int optindex = 0;
824 
825 #ifndef WIN32
826  pqsignal(SIGINT, sigint_handler);
827 #endif
828 
829  pg_logging_init(argv[0]);
830  set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_waldump"));
831  progname = get_progname(argv[0]);
832 
833  if (argc > 1)
834  {
835  if (strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") == 0)
836  {
837  usage();
838  exit(0);
839  }
840  if (strcmp(argv[1], "--version") == 0 || strcmp(argv[1], "-V") == 0)
841  {
842  puts("pg_waldump (PostgreSQL) " PG_VERSION);
843  exit(0);
844  }
845  }
846 
847  memset(&private, 0, sizeof(XLogDumpPrivate));
848  memset(&config, 0, sizeof(XLogDumpConfig));
849  memset(&stats, 0, sizeof(XLogStats));
850 
851  private.timeline = 1;
852  private.startptr = InvalidXLogRecPtr;
853  private.endptr = InvalidXLogRecPtr;
854  private.endptr_reached = false;
855 
856  config.quiet = false;
857  config.bkp_details = false;
858  config.stop_after_records = -1;
859  config.already_displayed_records = 0;
860  config.follow = false;
861  /* filter_by_rmgr array was zeroed by memset above */
862  config.filter_by_rmgr_enabled = false;
864  config.filter_by_xid_enabled = false;
865  config.filter_by_extended = false;
866  config.filter_by_relation_enabled = false;
867  config.filter_by_relation_block_enabled = false;
869  config.filter_by_fpw = false;
870  config.save_fullpage_path = NULL;
871  config.stats = false;
872  config.stats_per_record = false;
873 
874  stats.startptr = InvalidXLogRecPtr;
875  stats.endptr = InvalidXLogRecPtr;
876 
877  if (argc <= 1)
878  {
879  pg_log_error("no arguments specified");
880  goto bad_argument;
881  }
882 
883  while ((option = getopt_long(argc, argv, "bB:e:fF:n:p:qr:R:s:t:wx:z",
884  long_options, &optindex)) != -1)
885  {
886  switch (option)
887  {
888  case 'b':
889  config.bkp_details = true;
890  break;
891  case 'B':
892  if (sscanf(optarg, "%u", &config.filter_by_relation_block) != 1 ||
894  {
895  pg_log_error("invalid block number: \"%s\"", optarg);
896  goto bad_argument;
897  }
898  config.filter_by_relation_block_enabled = true;
899  config.filter_by_extended = true;
900  break;
901  case 'e':
902  if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
903  {
904  pg_log_error("invalid WAL location: \"%s\"",
905  optarg);
906  goto bad_argument;
907  }
908  private.endptr = (uint64) xlogid << 32 | xrecoff;
909  break;
910  case 'f':
911  config.follow = true;
912  break;
913  case 'F':
916  {
917  pg_log_error("invalid fork name: \"%s\"", optarg);
918  goto bad_argument;
919  }
920  config.filter_by_extended = true;
921  break;
922  case 'n':
923  if (sscanf(optarg, "%d", &config.stop_after_records) != 1)
924  {
925  pg_log_error("invalid value \"%s\" for option %s", optarg, "-n/--limit");
926  goto bad_argument;
927  }
928  break;
929  case 'p':
930  waldir = pg_strdup(optarg);
931  break;
932  case 'q':
933  config.quiet = true;
934  break;
935  case 'r':
936  {
937  int rmid;
938 
939  if (pg_strcasecmp(optarg, "list") == 0)
940  {
941  print_rmgr_list();
943  }
944 
945  /*
946  * First look for the generated name of a custom rmgr, of
947  * the form "custom###". We accept this form, because the
948  * custom rmgr module is not loaded, so there's no way to
949  * know the real name. This convention should be
950  * consistent with that in rmgrdesc.c.
951  */
952  if (sscanf(optarg, "custom%03d", &rmid) == 1)
953  {
954  if (!RmgrIdIsCustom(rmid))
955  {
956  pg_log_error("custom resource manager \"%s\" does not exist",
957  optarg);
958  goto bad_argument;
959  }
960  config.filter_by_rmgr[rmid] = true;
961  config.filter_by_rmgr_enabled = true;
962  }
963  else
964  {
965  /* then look for builtin rmgrs */
966  for (rmid = 0; rmid <= RM_MAX_BUILTIN_ID; rmid++)
967  {
968  if (pg_strcasecmp(optarg, GetRmgrDesc(rmid)->rm_name) == 0)
969  {
970  config.filter_by_rmgr[rmid] = true;
971  config.filter_by_rmgr_enabled = true;
972  break;
973  }
974  }
975  if (rmid > RM_MAX_BUILTIN_ID)
976  {
977  pg_log_error("resource manager \"%s\" does not exist",
978  optarg);
979  goto bad_argument;
980  }
981  }
982  }
983  break;
984  case 'R':
985  if (sscanf(optarg, "%u/%u/%u",
986  &config.filter_by_relation.spcOid,
987  &config.filter_by_relation.dbOid,
988  &config.filter_by_relation.relNumber) != 3 ||
991  {
992  pg_log_error("invalid relation specification: \"%s\"", optarg);
993  pg_log_error_detail("Expecting \"tablespace OID/database OID/relation filenode\".");
994  goto bad_argument;
995  }
996  config.filter_by_relation_enabled = true;
997  config.filter_by_extended = true;
998  break;
999  case 's':
1000  if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
1001  {
1002  pg_log_error("invalid WAL location: \"%s\"",
1003  optarg);
1004  goto bad_argument;
1005  }
1006  else
1007  private.startptr = (uint64) xlogid << 32 | xrecoff;
1008  break;
1009  case 't':
1010 
1011  /*
1012  * This is like option_parse_int() but needs to handle
1013  * unsigned 32-bit int. Also, we accept both decimal and
1014  * hexadecimal specifications here.
1015  */
1016  {
1017  char *endptr;
1018  unsigned long val;
1019 
1020  errno = 0;
1021  val = strtoul(optarg, &endptr, 0);
1022 
1023  while (*endptr != '\0' && isspace((unsigned char) *endptr))
1024  endptr++;
1025 
1026  if (*endptr != '\0')
1027  {
1028  pg_log_error("invalid value \"%s\" for option %s",
1029  optarg, "-t/--timeline");
1030  goto bad_argument;
1031  }
1032 
1033  if (errno == ERANGE || val < 1 || val > UINT_MAX)
1034  {
1035  pg_log_error("%s must be in range %u..%u",
1036  "-t/--timeline", 1, UINT_MAX);
1037  goto bad_argument;
1038  }
1039 
1040  private.timeline = val;
1041 
1042  break;
1043  }
1044  case 'w':
1045  config.filter_by_fpw = true;
1046  break;
1047  case 'x':
1048  if (sscanf(optarg, "%u", &config.filter_by_xid) != 1)
1049  {
1050  pg_log_error("invalid transaction ID specification: \"%s\"",
1051  optarg);
1052  goto bad_argument;
1053  }
1054  config.filter_by_xid_enabled = true;
1055  break;
1056  case 'z':
1057  config.stats = true;
1058  config.stats_per_record = false;
1059  if (optarg)
1060  {
1061  if (strcmp(optarg, "record") == 0)
1062  config.stats_per_record = true;
1063  else if (strcmp(optarg, "rmgr") != 0)
1064  {
1065  pg_log_error("unrecognized value for option %s: %s",
1066  "--stats", optarg);
1067  goto bad_argument;
1068  }
1069  }
1070  break;
1071  case 1:
1073  break;
1074  default:
1075  goto bad_argument;
1076  }
1077  }
1078 
1079  if (config.filter_by_relation_block_enabled &&
1081  {
1082  pg_log_error("option %s requires option %s to be specified",
1083  "-B/--block", "-R/--relation");
1084  goto bad_argument;
1085  }
1086 
1087  if ((optind + 2) < argc)
1088  {
1089  pg_log_error("too many command-line arguments (first is \"%s\")",
1090  argv[optind + 2]);
1091  goto bad_argument;
1092  }
1093 
1094  if (waldir != NULL)
1095  {
1096  /* validate path points to directory */
1097  if (!verify_directory(waldir))
1098  {
1099  pg_log_error("could not open directory \"%s\": %m", waldir);
1100  goto bad_argument;
1101  }
1102  }
1103 
1104  if (config.save_fullpage_path != NULL)
1106 
1107  /* parse files as start/end boundaries, extract path if not specified */
1108  if (optind < argc)
1109  {
1110  char *directory = NULL;
1111  char *fname = NULL;
1112  int fd;
1113  XLogSegNo segno;
1114 
1115  split_path(argv[optind], &directory, &fname);
1116 
1117  if (waldir == NULL && directory != NULL)
1118  {
1119  waldir = directory;
1120 
1121  if (!verify_directory(waldir))
1122  pg_fatal("could not open directory \"%s\": %m", waldir);
1123  }
1124 
1125  waldir = identify_target_directory(waldir, fname);
1126  fd = open_file_in_directory(waldir, fname);
1127  if (fd < 0)
1128  pg_fatal("could not open file \"%s\"", fname);
1129  close(fd);
1130 
1131  /* parse position from file */
1132  XLogFromFileName(fname, &private.timeline, &segno, WalSegSz);
1133 
1134  if (XLogRecPtrIsInvalid(private.startptr))
1135  XLogSegNoOffsetToRecPtr(segno, 0, WalSegSz, private.startptr);
1136  else if (!XLByteInSeg(private.startptr, segno, WalSegSz))
1137  {
1138  pg_log_error("start WAL location %X/%X is not inside file \"%s\"",
1139  LSN_FORMAT_ARGS(private.startptr),
1140  fname);
1141  goto bad_argument;
1142  }
1143 
1144  /* no second file specified, set end position */
1145  if (!(optind + 1 < argc) && XLogRecPtrIsInvalid(private.endptr))
1146  XLogSegNoOffsetToRecPtr(segno + 1, 0, WalSegSz, private.endptr);
1147 
1148  /* parse ENDSEG if passed */
1149  if (optind + 1 < argc)
1150  {
1151  XLogSegNo endsegno;
1152 
1153  /* ignore directory, already have that */
1154  split_path(argv[optind + 1], &directory, &fname);
1155 
1156  fd = open_file_in_directory(waldir, fname);
1157  if (fd < 0)
1158  pg_fatal("could not open file \"%s\"", fname);
1159  close(fd);
1160 
1161  /* parse position from file */
1162  XLogFromFileName(fname, &private.timeline, &endsegno, WalSegSz);
1163 
1164  if (endsegno < segno)
1165  pg_fatal("ENDSEG %s is before STARTSEG %s",
1166  argv[optind + 1], argv[optind]);
1167 
1168  if (XLogRecPtrIsInvalid(private.endptr))
1169  XLogSegNoOffsetToRecPtr(endsegno + 1, 0, WalSegSz,
1170  private.endptr);
1171 
1172  /* set segno to endsegno for check of --end */
1173  segno = endsegno;
1174  }
1175 
1176 
1177  if (!XLByteInSeg(private.endptr, segno, WalSegSz) &&
1178  private.endptr != (segno + 1) * WalSegSz)
1179  {
1180  pg_log_error("end WAL location %X/%X is not inside file \"%s\"",
1181  LSN_FORMAT_ARGS(private.endptr),
1182  argv[argc - 1]);
1183  goto bad_argument;
1184  }
1185  }
1186  else
1187  waldir = identify_target_directory(waldir, NULL);
1188 
1189  /* we don't know what to print */
1190  if (XLogRecPtrIsInvalid(private.startptr))
1191  {
1192  pg_log_error("no start WAL location given");
1193  goto bad_argument;
1194  }
1195 
1196  /* done with argument parsing, do the actual work */
1197 
1198  /* we have everything we need, start reading */
1199  xlogreader_state =
1200  XLogReaderAllocate(WalSegSz, waldir,
1201  XL_ROUTINE(.page_read = WALDumpReadPage,
1202  .segment_open = WALDumpOpenSegment,
1203  .segment_close = WALDumpCloseSegment),
1204  &private);
1205  if (!xlogreader_state)
1206  pg_fatal("out of memory while allocating a WAL reading processor");
1207 
1208  /* first find a valid recptr to start from */
1209  first_record = XLogFindNextRecord(xlogreader_state, private.startptr);
1210 
1211  if (first_record == InvalidXLogRecPtr)
1212  pg_fatal("could not find a valid record after %X/%X",
1213  LSN_FORMAT_ARGS(private.startptr));
1214 
1215  /*
1216  * Display a message that we're skipping data if `from` wasn't a pointer
1217  * to the start of a record and also wasn't a pointer to the beginning of
1218  * a segment (e.g. we were used in file mode).
1219  */
1220  if (first_record != private.startptr &&
1221  XLogSegmentOffset(private.startptr, WalSegSz) != 0)
1222  printf(ngettext("first record is after %X/%X, at %X/%X, skipping over %u byte\n",
1223  "first record is after %X/%X, at %X/%X, skipping over %u bytes\n",
1224  (first_record - private.startptr)),
1225  LSN_FORMAT_ARGS(private.startptr),
1226  LSN_FORMAT_ARGS(first_record),
1227  (uint32) (first_record - private.startptr));
1228 
1229  if (config.stats == true && !config.quiet)
1230  stats.startptr = first_record;
1231 
1232  for (;;)
1233  {
1234  if (time_to_stop)
1235  {
1236  /* We've been Ctrl-C'ed, so leave */
1237  break;
1238  }
1239 
1240  /* try to read the next record */
1241  record = XLogReadRecord(xlogreader_state, &errormsg);
1242  if (!record)
1243  {
1244  if (!config.follow || private.endptr_reached)
1245  break;
1246  else
1247  {
1248  pg_usleep(1000000L); /* 1 second */
1249  continue;
1250  }
1251  }
1252 
1253  /* apply all specified filters */
1254  if (config.filter_by_rmgr_enabled &&
1255  !config.filter_by_rmgr[record->xl_rmid])
1256  continue;
1257 
1258  if (config.filter_by_xid_enabled &&
1259  config.filter_by_xid != record->xl_xid)
1260  continue;
1261 
1262  /* check for extended filtering */
1263  if (config.filter_by_extended &&
1264  !XLogRecordMatchesRelationBlock(xlogreader_state,
1266  config.filter_by_relation :
1269  config.filter_by_relation_block :
1272  continue;
1273 
1274  if (config.filter_by_fpw && !XLogRecordHasFPW(xlogreader_state))
1275  continue;
1276 
1277  /* perform any per-record work */
1278  if (!config.quiet)
1279  {
1280  if (config.stats == true)
1281  {
1282  XLogRecStoreStats(&stats, xlogreader_state);
1283  stats.endptr = xlogreader_state->EndRecPtr;
1284  }
1285  else
1286  XLogDumpDisplayRecord(&config, xlogreader_state);
1287  }
1288 
1289  /* save full pages if requested */
1290  if (config.save_fullpage_path != NULL)
1291  XLogRecordSaveFPWs(xlogreader_state, config.save_fullpage_path);
1292 
1293  /* check whether we printed enough */
1294  config.already_displayed_records++;
1295  if (config.stop_after_records > 0 &&
1297  break;
1298  }
1299 
1300  if (config.stats == true && !config.quiet)
1301  XLogDumpDisplayStats(&config, &stats);
1302 
1303  if (time_to_stop)
1304  exit(0);
1305 
1306  if (errormsg)
1307  pg_fatal("error in WAL record at %X/%X: %s",
1308  LSN_FORMAT_ARGS(xlogreader_state->ReadRecPtr),
1309  errormsg);
1310 
1311  XLogReaderFree(xlogreader_state);
1312 
1313  return EXIT_SUCCESS;
1314 
1315 bad_argument:
1316  pg_log_error_hint("Try \"%s --help\" for more information.", progname);
1317  return EXIT_FAILURE;
1318 }
#define InvalidBlockNumber
Definition: block.h:33
static bool BlockNumberIsValid(BlockNumber blockNumber)
Definition: block.h:71
unsigned int uint32
Definition: c.h:490
#define ngettext(s, p, n)
Definition: c.h:1189
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1222
#define OidIsValid(objectId)
Definition: c.h:759
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:436
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition: getopt_long.c:57
#define no_argument
Definition: getopt_long.h:24
#define required_argument
Definition: getopt_long.h:25
#define optional_argument
Definition: getopt_long.h:26
long val
Definition: informix.c:664
#define close(a)
Definition: win32.h:12
exit(1)
void pg_logging_init(const char *argv0)
Definition: logging.c:83
#define pg_log_error(...)
Definition: logging.h:106
#define pg_log_error_hint(...)
Definition: logging.h:112
#define pg_log_error_detail(...)
Definition: logging.h:109
PGDLLIMPORT int optind
Definition: getopt.c:50
PGDLLIMPORT char * optarg
Definition: getopt.c:52
static void XLogDumpDisplayStats(XLogDumpConfig *config, XLogStats *stats)
Definition: pg_waldump.c:621
static int WalSegSz
Definition: pg_waldump.c:42
static void WALDumpCloseSegment(XLogReaderState *state)
Definition: pg_waldump.c:376
static volatile sig_atomic_t time_to_stop
Definition: pg_waldump.c:43
static void split_path(const char *path, char **dir, char **fname)
Definition: pg_waldump.c:161
static int open_file_in_directory(const char *directory, const char *fname)
Definition: pg_waldump.c:188
static void XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
Definition: pg_waldump.c:541
static void create_fullpage_directory(char *path)
Definition: pg_waldump.c:128
static bool verify_directory(const char *directory)
Definition: pg_waldump.c:113
static const RelFileLocator emptyRelFileLocator
Definition: pg_waldump.c:45
static void print_rmgr_list(void)
Definition: pg_waldump.c:98
static void WALDumpOpenSegment(XLogReaderState *state, XLogSegNo nextSegNo, TimeLineID *tli_p)
Definition: pg_waldump.c:334
static const char * progname
Definition: pg_waldump.c:40
static void usage(void)
Definition: pg_waldump.c:751
static int WALDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetPtr, char *readBuff)
Definition: pg_waldump.c:385
static void XLogRecordSaveFPWs(XLogReaderState *record, const char *savepath)
Definition: pg_waldump.c:486
static char * identify_target_directory(char *directory, char *fname)
Definition: pg_waldump.c:288
static bool XLogRecordHasFPW(XLogReaderState *record)
Definition: pg_waldump.c:465
static void sigint_handler(SIGNAL_ARGS)
Definition: pg_waldump.c:91
static bool XLogRecordMatchesRelationBlock(XLogReaderState *record, RelFileLocator matchRlocator, BlockNumber matchBlock, ForkNumber matchFork)
Definition: pg_waldump.c:434
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
const char * get_progname(const char *argv0)
Definition: path.c:574
pqsigfunc pqsignal(int signo, pqsigfunc func)
#define printf(...)
Definition: port.h:244
static int fd(const char *x, int i)
Definition: preproc-init.c:105
ForkNumber forkname_to_number(const char *forkName)
Definition: relpath.c:50
@ InvalidForkNumber
Definition: relpath.h:49
#define RelFileNumberIsValid(relnumber)
Definition: relpath.h:27
#define RM_MAX_BUILTIN_ID
Definition: rmgr.h:34
static bool RmgrIdIsCustom(int rmid)
Definition: rmgr.h:48
const RmgrDescData * GetRmgrDesc(RmgrId rmid)
Definition: rmgrdesc.c:87
#define EXIT_SUCCESS
Definition: settings.h:163
#define EXIT_FAILURE
Definition: settings.h:167
void pg_usleep(long microsec)
Definition: signal.c:53
RelFileNumber relNumber
bool filter_by_xid_enabled
Definition: pg_waldump.c:70
RelFileLocator filter_by_relation
Definition: pg_waldump.c:71
char * save_fullpage_path
Definition: pg_waldump.c:80
bool bkp_details
Definition: pg_waldump.c:59
bool filter_by_rmgr[RM_MAX_ID+1]
Definition: pg_waldump.c:67
bool filter_by_extended
Definition: pg_waldump.c:72
bool stats_per_record
Definition: pg_waldump.c:64
int already_displayed_records
Definition: pg_waldump.c:61
int stop_after_records
Definition: pg_waldump.c:60
bool filter_by_fpw
Definition: pg_waldump.c:77
bool filter_by_relation_enabled
Definition: pg_waldump.c:73
BlockNumber filter_by_relation_block
Definition: pg_waldump.c:74
bool filter_by_rmgr_enabled
Definition: pg_waldump.c:68
TransactionId filter_by_xid
Definition: pg_waldump.c:69
ForkNumber filter_by_relation_forknum
Definition: pg_waldump.c:76
bool filter_by_relation_block_enabled
Definition: pg_waldump.c:75
XLogRecPtr EndRecPtr
Definition: xlogreader.h:207
XLogRecPtr ReadRecPtr
Definition: xlogreader.h:206
TransactionId xl_xid
Definition: xlogrecord.h:44
RmgrId xl_rmid
Definition: xlogrecord.h:47
#define InvalidTransactionId
Definition: transam.h:31
#define XLogSegmentOffset(xlogptr, wal_segsz_bytes)
static void XLogFromFileName(const char *fname, TimeLineID *tli, XLogSegNo *logSegNo, int wal_segsz_bytes)
#define XLogSegNoOffsetToRecPtr(segno, offset, wal_segsz_bytes, dest)
#define XLByteInSeg(xlrp, logSegNo, wal_segsz_bytes)
#define LSN_FORMAT_ARGS(lsn)
Definition: xlogdefs.h:43
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
uint64 XLogSegNo
Definition: xlogdefs.h:48
XLogRecord * XLogReadRecord(XLogReaderState *state, char **errormsg)
Definition: xlogreader.c:422
void XLogReaderFree(XLogReaderState *state)
Definition: xlogreader.c:170
XLogReaderState * XLogReaderAllocate(int wal_segment_size, const char *waldir, XLogReaderRoutine *routine, void *private_data)
Definition: xlogreader.c:108
XLogRecPtr XLogFindNextRecord(XLogReaderState *state, XLogRecPtr RecPtr)
Definition: xlogreader.c:1370
#define XL_ROUTINE(...)
Definition: xlogreader.h:117
void XLogRecStoreStats(XLogStats *stats, XLogReaderState *record)
Definition: xlogstats.c:54

References XLogDumpConfig::already_displayed_records, XLogDumpConfig::bkp_details, BlockNumberIsValid(), close, create_fullpage_directory(), RelFileLocator::dbOid, directory, emptyRelFileLocator, XLogReaderState::EndRecPtr, exit(), EXIT_FAILURE, EXIT_SUCCESS, fd(), XLogDumpConfig::filter_by_extended, XLogDumpConfig::filter_by_fpw, XLogDumpConfig::filter_by_relation, XLogDumpConfig::filter_by_relation_block, XLogDumpConfig::filter_by_relation_block_enabled, XLogDumpConfig::filter_by_relation_enabled, XLogDumpConfig::filter_by_relation_forknum, XLogDumpConfig::filter_by_rmgr, XLogDumpConfig::filter_by_rmgr_enabled, XLogDumpConfig::filter_by_xid, XLogDumpConfig::filter_by_xid_enabled, XLogDumpConfig::follow, forkname_to_number(), get_progname(), getopt_long(), GetRmgrDesc(), identify_target_directory(), InvalidBlockNumber, InvalidForkNumber, InvalidTransactionId, InvalidXLogRecPtr, LSN_FORMAT_ARGS, ngettext, no_argument, OidIsValid, open_file_in_directory(), optarg, optind, optional_argument, pg_fatal, pg_log_error, pg_log_error_detail, pg_log_error_hint, pg_logging_init(), pg_strcasecmp(), pg_strdup(), PG_TEXTDOMAIN, pg_usleep(), pqsignal(), print_rmgr_list(), printf, progname, XLogDumpConfig::quiet, XLogReaderState::ReadRecPtr, RelFileNumberIsValid, RelFileLocator::relNumber, required_argument, RM_MAX_BUILTIN_ID, RmgrIdIsCustom(), XLogDumpConfig::save_fullpage_path, set_pglocale_pgservice(), sigint_handler(), RelFileLocator::spcOid, split_path(), XLogDumpConfig::stats, XLogDumpConfig::stats_per_record, XLogDumpConfig::stop_after_records, time_to_stop, usage(), val, verify_directory(), WALDumpCloseSegment(), WALDumpOpenSegment(), WALDumpReadPage(), WalSegSz, XLogRecord::xl_rmid, XL_ROUTINE, XLogRecord::xl_xid, XLByteInSeg, XLogDumpDisplayRecord(), XLogDumpDisplayStats(), XLogFindNextRecord(), XLogFromFileName(), XLogReaderAllocate(), XLogReaderFree(), XLogReadRecord(), XLogRecordHasFPW(), XLogRecordMatchesRelationBlock(), XLogRecordSaveFPWs(), XLogRecPtrIsInvalid, XLogRecStoreStats(), XLogSegmentOffset, and XLogSegNoOffsetToRecPtr.

◆ open_file_in_directory()

static int open_file_in_directory ( const char *  directory,
const char *  fname 
)
static

Definition at line 188 of file pg_waldump.c.

189 {
190  int fd = -1;
191  char fpath[MAXPGPATH];
192 
193  Assert(directory != NULL);
194 
195  snprintf(fpath, MAXPGPATH, "%s/%s", directory, fname);
196  fd = open(fpath, O_RDONLY | PG_BINARY, 0);
197 
198  if (fd < 0 && errno != ENOENT)
199  pg_fatal("could not open file \"%s\": %m", fname);
200  return fd;
201 }
#define PG_BINARY
Definition: c.h:1278
Assert(fmt[strlen(fmt) - 1] !='\n')

References Assert(), directory, fd(), MAXPGPATH, PG_BINARY, pg_fatal, and snprintf.

Referenced by main(), search_directory(), and WALDumpOpenSegment().

◆ print_rmgr_list()

static void print_rmgr_list ( void  )
static

Definition at line 98 of file pg_waldump.c.

99 {
100  int i;
101 
102  for (i = 0; i <= RM_MAX_BUILTIN_ID; i++)
103  {
104  printf("%s\n", GetRmgrDesc(i)->rm_name);
105  }
106 }
int i
Definition: isn.c:73

References GetRmgrDesc(), i, printf, and RM_MAX_BUILTIN_ID.

Referenced by main().

◆ search_directory()

static bool search_directory ( const char *  directory,
const char *  fname 
)
static

Definition at line 210 of file pg_waldump.c.

211 {
212  int fd = -1;
213  DIR *xldir;
214 
215  /* open file if valid filename is provided */
216  if (fname != NULL)
218 
219  /*
220  * A valid file name is not passed, so search the complete directory. If
221  * we find any file whose name is a valid WAL file name then try to open
222  * it. If we cannot open it, bail out.
223  */
224  else if ((xldir = opendir(directory)) != NULL)
225  {
226  struct dirent *xlde;
227 
228  while ((xlde = readdir(xldir)) != NULL)
229  {
230  if (IsXLogFileName(xlde->d_name))
231  {
233  fname = pg_strdup(xlde->d_name);
234  break;
235  }
236  }
237 
238  closedir(xldir);
239  }
240 
241  /* set WalSegSz if file is successfully opened */
242  if (fd >= 0)
243  {
245  int r;
246 
247  r = read(fd, buf.data, XLOG_BLCKSZ);
248  if (r == XLOG_BLCKSZ)
249  {
250  XLogLongPageHeader longhdr = (XLogLongPageHeader) buf.data;
251 
252  WalSegSz = longhdr->xlp_seg_size;
253 
255  pg_fatal(ngettext("WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file \"%s\" header specifies %d byte",
256  "WAL segment size must be a power of two between 1 MB and 1 GB, but the WAL file \"%s\" header specifies %d bytes",
257  WalSegSz),
258  fname, WalSegSz);
259  }
260  else if (r < 0)
261  pg_fatal("could not read file \"%s\": %m",
262  fname);
263  else
264  pg_fatal("could not read file \"%s\": read %d of %d",
265  fname, r, XLOG_BLCKSZ);
266  close(fd);
267  return true;
268  }
269 
270  return false;
271 }
int closedir(DIR *)
Definition: dirent.c:127
struct dirent * readdir(DIR *)
Definition: dirent.c:78
DIR * opendir(const char *)
Definition: dirent.c:33
#define read(a, b, c)
Definition: win32.h:13
if(TABLE==NULL||TABLE_index==NULL)
Definition: isn.c:77
static char * buf
Definition: pg_test_fsync.c:67
Definition: dirent.c:26
Definition: dirent.h:10
char d_name[MAX_PATH]
Definition: dirent.h:15
#define IsValidWalSegSize(size)
Definition: xlog_internal.h:96
XLogLongPageHeaderData * XLogLongPageHeader
Definition: xlog_internal.h:71
static bool IsXLogFileName(const char *fname)

References buf, close, closedir(), dirent::d_name, directory, fd(), if(), IsValidWalSegSize, IsXLogFileName(), ngettext, open_file_in_directory(), opendir(), pg_fatal, pg_strdup(), read, readdir(), WalSegSz, and XLogLongPageHeaderData::xlp_seg_size.

Referenced by identify_target_directory().

◆ sigint_handler()

static void sigint_handler ( SIGNAL_ARGS  )
static

Definition at line 91 of file pg_waldump.c.

92 {
93  time_to_stop = true;
94 }

References time_to_stop.

Referenced by main().

◆ split_path()

static void split_path ( const char *  path,
char **  dir,
char **  fname 
)
static

Definition at line 161 of file pg_waldump.c.

162 {
163  char *sep;
164 
165  /* split filepath into directory & filename */
166  sep = strrchr(path, '/');
167 
168  /* directory path */
169  if (sep != NULL)
170  {
171  *dir = pnstrdup(path, sep - path);
172  *fname = pg_strdup(sep + 1);
173  }
174  /* local directory */
175  else
176  {
177  *dir = NULL;
178  *fname = pg_strdup(path);
179  }
180 }
char * pnstrdup(const char *in, Size len)
Definition: mcxt.c:1655

References pg_strdup(), and pnstrdup().

Referenced by main().

◆ usage()

static void usage ( void  )
static

Definition at line 751 of file pg_waldump.c.

752 {
753  printf(_("%s decodes and displays PostgreSQL write-ahead logs for debugging.\n\n"),
754  progname);
755  printf(_("Usage:\n"));
756  printf(_(" %s [OPTION]... [STARTSEG [ENDSEG]]\n"), progname);
757  printf(_("\nOptions:\n"));
758  printf(_(" -b, --bkp-details output detailed information about backup blocks\n"));
759  printf(_(" -B, --block=N with --relation, only show records that modify block N\n"));
760  printf(_(" -e, --end=RECPTR stop reading at WAL location RECPTR\n"));
761  printf(_(" -f, --follow keep retrying after reaching end of WAL\n"));
762  printf(_(" -F, --fork=FORK only show records that modify blocks in fork FORK;\n"
763  " valid names are main, fsm, vm, init\n"));
764  printf(_(" -n, --limit=N number of records to display\n"));
765  printf(_(" -p, --path=PATH directory in which to find WAL segment files or a\n"
766  " directory with a ./pg_wal that contains such files\n"
767  " (default: current directory, ./pg_wal, $PGDATA/pg_wal)\n"));
768  printf(_(" -q, --quiet do not print any output, except for errors\n"));
769  printf(_(" -r, --rmgr=RMGR only show records generated by resource manager RMGR;\n"
770  " use --rmgr=list to list valid resource manager names\n"));
771  printf(_(" -R, --relation=T/D/R only show records that modify blocks in relation T/D/R\n"));
772  printf(_(" -s, --start=RECPTR start reading at WAL location RECPTR\n"));
773  printf(_(" -t, --timeline=TLI timeline from which to read WAL records\n"
774  " (default: 1 or the value used in STARTSEG)\n"));
775  printf(_(" -V, --version output version information, then exit\n"));
776  printf(_(" -w, --fullpage only show records with a full page write\n"));
777  printf(_(" -x, --xid=XID only show records with transaction ID XID\n"));
778  printf(_(" -z, --stats[=record] show statistics instead of records\n"
779  " (optionally, show per-record statistics)\n"));
780  printf(_(" --save-fullpage=DIR save full page images to DIR\n"));
781  printf(_(" -?, --help show this help, then exit\n"));
782  printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
783  printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
784 }
#define _(x)
Definition: elog.c:91

References _, printf, and progname.

Referenced by main().

◆ verify_directory()

static bool verify_directory ( const char *  directory)
static

Definition at line 113 of file pg_waldump.c.

114 {
115  DIR *dir = opendir(directory);
116 
117  if (dir == NULL)
118  return false;
119  closedir(dir);
120  return true;
121 }

References closedir(), directory, and opendir().

Referenced by main().

◆ WALDumpCloseSegment()

static void WALDumpCloseSegment ( XLogReaderState state)
static

Definition at line 376 of file pg_waldump.c.

377 {
378  close(state->seg.ws_file);
379  /* need to check errno? */
380  state->seg.ws_file = -1;
381 }
Definition: regguts.h:323

References close.

Referenced by main().

◆ WALDumpOpenSegment()

static void WALDumpOpenSegment ( XLogReaderState state,
XLogSegNo  nextSegNo,
TimeLineID tli_p 
)
static

Definition at line 334 of file pg_waldump.c.

336 {
337  TimeLineID tli = *tli_p;
338  char fname[MAXPGPATH];
339  int tries;
340 
341  XLogFileName(fname, tli, nextSegNo, state->segcxt.ws_segsize);
342 
343  /*
344  * In follow mode there is a short period of time after the server has
345  * written the end of the previous file before the new file is available.
346  * So we loop for 5 seconds looking for the file to appear before giving
347  * up.
348  */
349  for (tries = 0; tries < 10; tries++)
350  {
351  state->seg.ws_file = open_file_in_directory(state->segcxt.ws_dir, fname);
352  if (state->seg.ws_file >= 0)
353  return;
354  if (errno == ENOENT)
355  {
356  int save_errno = errno;
357 
358  /* File not there yet, try again */
359  pg_usleep(500 * 1000);
360 
361  errno = save_errno;
362  continue;
363  }
364  /* Any other error, fall through and fail */
365  break;
366  }
367 
368  pg_fatal("could not find file \"%s\": %m", fname);
369 }
static void XLogFileName(char *fname, TimeLineID tli, XLogSegNo logSegNo, int wal_segsz_bytes)
uint32 TimeLineID
Definition: xlogdefs.h:59

References MAXPGPATH, open_file_in_directory(), pg_fatal, pg_usleep(), and XLogFileName().

Referenced by main().

◆ WALDumpReadPage()

static int WALDumpReadPage ( XLogReaderState state,
XLogRecPtr  targetPagePtr,
int  reqLen,
XLogRecPtr  targetPtr,
char *  readBuff 
)
static

Definition at line 385 of file pg_waldump.c.

387 {
388  XLogDumpPrivate *private = state->private_data;
389  int count = XLOG_BLCKSZ;
390  WALReadError errinfo;
391 
392  if (private->endptr != InvalidXLogRecPtr)
393  {
394  if (targetPagePtr + XLOG_BLCKSZ <= private->endptr)
395  count = XLOG_BLCKSZ;
396  else if (targetPagePtr + reqLen <= private->endptr)
397  count = private->endptr - targetPagePtr;
398  else
399  {
400  private->endptr_reached = true;
401  return -1;
402  }
403  }
404 
405  if (!WALRead(state, readBuff, targetPagePtr, count, private->timeline,
406  &errinfo))
407  {
408  WALOpenSegment *seg = &errinfo.wre_seg;
409  char fname[MAXPGPATH];
410 
411  XLogFileName(fname, seg->ws_tli, seg->ws_segno,
412  state->segcxt.ws_segsize);
413 
414  if (errinfo.wre_errno != 0)
415  {
416  errno = errinfo.wre_errno;
417  pg_fatal("could not read from file %s, offset %d: %m",
418  fname, errinfo.wre_off);
419  }
420  else
421  pg_fatal("could not read from file %s, offset %d: read %d of %d",
422  fname, errinfo.wre_off, errinfo.wre_read,
423  errinfo.wre_req);
424  }
425 
426  return count;
427 }
XLogSegNo ws_segno
Definition: xlogreader.h:48
TimeLineID ws_tli
Definition: xlogreader.h:49
WALOpenSegment wre_seg
Definition: xlogreader.h:388
bool WALRead(XLogReaderState *state, char *buf, XLogRecPtr startptr, Size count, TimeLineID tli, WALReadError *errinfo)
Definition: xlogreader.c:1493

References InvalidXLogRecPtr, MAXPGPATH, pg_fatal, WALRead(), WALReadError::wre_errno, WALReadError::wre_off, WALReadError::wre_read, WALReadError::wre_req, WALReadError::wre_seg, WALOpenSegment::ws_segno, WALOpenSegment::ws_tli, and XLogFileName().

Referenced by main().

◆ XLogDumpDisplayRecord()

static void XLogDumpDisplayRecord ( XLogDumpConfig config,
XLogReaderState record 
)
static

Definition at line 541 of file pg_waldump.c.

542 {
543  const char *id;
544  const RmgrDescData *desc = GetRmgrDesc(XLogRecGetRmid(record));
545  uint32 rec_len;
546  uint32 fpi_len;
547  uint8 info = XLogRecGetInfo(record);
548  XLogRecPtr xl_prev = XLogRecGetPrev(record);
549  StringInfoData s;
550 
551  XLogRecGetLen(record, &rec_len, &fpi_len);
552 
553  printf("rmgr: %-11s len (rec/tot): %6u/%6u, tx: %10u, lsn: %X/%08X, prev %X/%08X, ",
554  desc->rm_name,
555  rec_len, XLogRecGetTotalLen(record),
556  XLogRecGetXid(record),
557  LSN_FORMAT_ARGS(record->ReadRecPtr),
558  LSN_FORMAT_ARGS(xl_prev));
559 
560  id = desc->rm_identify(info);
561  if (id == NULL)
562  printf("desc: UNKNOWN (%x) ", info & ~XLR_INFO_MASK);
563  else
564  printf("desc: %s ", id);
565 
566  initStringInfo(&s);
567  desc->rm_desc(&s, record);
568  printf("%s", s.data);
569 
570  resetStringInfo(&s);
571  XLogRecGetBlockRefInfo(record, true, config->bkp_details, &s, NULL);
572  printf("%s", s.data);
573  pfree(s.data);
574 }
unsigned char uint8
Definition: c.h:488
void pfree(void *pointer)
Definition: mcxt.c:1456
void resetStringInfo(StringInfo str)
Definition: stringinfo.c:75
void initStringInfo(StringInfo str)
Definition: stringinfo.c:59
const char *(* rm_identify)(uint8 info)
Definition: rmgrdesc.h:18
const char * rm_name
Definition: rmgrdesc.h:16
void(* rm_desc)(StringInfo buf, XLogReaderState *record)
Definition: rmgrdesc.h:17
void XLogRecGetBlockRefInfo(XLogReaderState *record, bool pretty, bool detailed_format, StringInfo buf, uint32 *fpi_len)
Definition: xlogdesc.c:209
#define XLogRecGetInfo(decoder)
Definition: xlogreader.h:410
#define XLogRecGetRmid(decoder)
Definition: xlogreader.h:411
#define XLogRecGetTotalLen(decoder)
Definition: xlogreader.h:408
#define XLogRecGetXid(decoder)
Definition: xlogreader.h:412
#define XLogRecGetPrev(decoder)
Definition: xlogreader.h:409
#define XLR_INFO_MASK
Definition: xlogrecord.h:62
void XLogRecGetLen(XLogReaderState *record, uint32 *rec_len, uint32 *fpi_len)
Definition: xlogstats.c:22

References XLogDumpConfig::bkp_details, StringInfoData::data, GetRmgrDesc(), initStringInfo(), LSN_FORMAT_ARGS, pfree(), printf, XLogReaderState::ReadRecPtr, resetStringInfo(), RmgrDescData::rm_desc, RmgrDescData::rm_identify, RmgrDescData::rm_name, XLogRecGetBlockRefInfo(), XLogRecGetInfo, XLogRecGetLen(), XLogRecGetPrev, XLogRecGetRmid, XLogRecGetTotalLen, XLogRecGetXid, and XLR_INFO_MASK.

Referenced by main().

◆ XLogDumpDisplayStats()

static void XLogDumpDisplayStats ( XLogDumpConfig config,
XLogStats stats 
)
static

Definition at line 621 of file pg_waldump.c.

622 {
623  int ri,
624  rj;
625  uint64 total_count = 0;
626  uint64 total_rec_len = 0;
627  uint64 total_fpi_len = 0;
628  uint64 total_len = 0;
629  double rec_len_pct,
630  fpi_len_pct;
631 
632  /*
633  * Leave if no stats have been computed yet, as tracked by the end LSN.
634  */
635  if (XLogRecPtrIsInvalid(stats->endptr))
636  return;
637 
638  /*
639  * Each row shows its percentages of the total, so make a first pass to
640  * calculate column totals.
641  */
642 
643  for (ri = 0; ri <= RM_MAX_ID; ri++)
644  {
645  if (!RmgrIdIsValid(ri))
646  continue;
647 
648  total_count += stats->rmgr_stats[ri].count;
649  total_rec_len += stats->rmgr_stats[ri].rec_len;
650  total_fpi_len += stats->rmgr_stats[ri].fpi_len;
651  }
652  total_len = total_rec_len + total_fpi_len;
653 
654  printf("WAL statistics between %X/%X and %X/%X:\n",
655  LSN_FORMAT_ARGS(stats->startptr), LSN_FORMAT_ARGS(stats->endptr));
656 
657  /*
658  * 27 is strlen("Transaction/COMMIT_PREPARED"), 20 is strlen(2^64), 8 is
659  * strlen("(100.00%)")
660  */
661 
662  printf("%-27s %20s %8s %20s %8s %20s %8s %20s %8s\n"
663  "%-27s %20s %8s %20s %8s %20s %8s %20s %8s\n",
664  "Type", "N", "(%)", "Record size", "(%)", "FPI size", "(%)", "Combined size", "(%)",
665  "----", "-", "---", "-----------", "---", "--------", "---", "-------------", "---");
666 
667  for (ri = 0; ri <= RM_MAX_ID; ri++)
668  {
669  uint64 count,
670  rec_len,
671  fpi_len,
672  tot_len;
673  const RmgrDescData *desc;
674 
675  if (!RmgrIdIsValid(ri))
676  continue;
677 
678  desc = GetRmgrDesc(ri);
679 
680  if (!config->stats_per_record)
681  {
682  count = stats->rmgr_stats[ri].count;
683  rec_len = stats->rmgr_stats[ri].rec_len;
684  fpi_len = stats->rmgr_stats[ri].fpi_len;
685  tot_len = rec_len + fpi_len;
686 
687  if (RmgrIdIsCustom(ri) && count == 0)
688  continue;
689 
691  count, total_count, rec_len, total_rec_len,
692  fpi_len, total_fpi_len, tot_len, total_len);
693  }
694  else
695  {
696  for (rj = 0; rj < MAX_XLINFO_TYPES; rj++)
697  {
698  const char *id;
699 
700  count = stats->record_stats[ri][rj].count;
701  rec_len = stats->record_stats[ri][rj].rec_len;
702  fpi_len = stats->record_stats[ri][rj].fpi_len;
703  tot_len = rec_len + fpi_len;
704 
705  /* Skip undefined combinations and ones that didn't occur */
706  if (count == 0)
707  continue;
708 
709  /* the upper four bits in xl_info are the rmgr's */
710  id = desc->rm_identify(rj << 4);
711  if (id == NULL)
712  id = psprintf("UNKNOWN (%x)", rj << 4);
713 
714  XLogDumpStatsRow(psprintf("%s/%s", desc->rm_name, id),
715  count, total_count, rec_len, total_rec_len,
716  fpi_len, total_fpi_len, tot_len, total_len);
717  }
718  }
719  }
720 
721  printf("%-27s %20s %8s %20s %8s %20s %8s %20s\n",
722  "", "--------", "", "--------", "", "--------", "", "--------");
723 
724  /*
725  * The percentages in earlier rows were calculated against the column
726  * total, but the ones that follow are against the row total. Note that
727  * these are displayed with a % symbol to differentiate them from the
728  * earlier ones, and are thus up to 9 characters long.
729  */
730 
731  rec_len_pct = 0;
732  if (total_len != 0)
733  rec_len_pct = 100 * (double) total_rec_len / total_len;
734 
735  fpi_len_pct = 0;
736  if (total_len != 0)
737  fpi_len_pct = 100 * (double) total_fpi_len / total_len;
738 
739  printf("%-27s "
740  "%20" INT64_MODIFIER "u %-9s"
741  "%20" INT64_MODIFIER "u %-9s"
742  "%20" INT64_MODIFIER "u %-9s"
743  "%20" INT64_MODIFIER "u %-6s\n",
744  "Total", stats->count, "",
745  total_rec_len, psprintf("[%.02f%%]", rec_len_pct),
746  total_fpi_len, psprintf("[%.02f%%]", fpi_len_pct),
747  total_len, "[100%]");
748 }
static void XLogDumpStatsRow(const char *name, uint64 n, uint64 total_count, uint64 rec_len, uint64 total_rec_len, uint64 fpi_len, uint64 total_fpi_len, uint64 tot_len, uint64 total_len)
Definition: pg_waldump.c:580
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
#define RmgrIdIsValid(rmid)
Definition: rmgr.h:53
#define RM_MAX_ID
Definition: rmgr.h:33
uint64 count
Definition: xlogstats.h:23
uint64 fpi_len
Definition: xlogstats.h:25
uint64 rec_len
Definition: xlogstats.h:24
XLogRecStats record_stats[RM_MAX_ID+1][MAX_XLINFO_TYPES]
Definition: xlogstats.h:36
uint64 count
Definition: xlogstats.h:30
XLogRecStats rmgr_stats[RM_MAX_ID+1]
Definition: xlogstats.h:35
#define MAX_XLINFO_TYPES
Definition: xlogstats.h:19

References XLogRecStats::count, XLogStats::count, XLogRecStats::fpi_len, GetRmgrDesc(), LSN_FORMAT_ARGS, MAX_XLINFO_TYPES, printf, psprintf(), XLogRecStats::rec_len, XLogStats::record_stats, RmgrDescData::rm_identify, RM_MAX_ID, RmgrDescData::rm_name, XLogStats::rmgr_stats, RmgrIdIsCustom(), RmgrIdIsValid, XLogDumpConfig::stats_per_record, XLogDumpStatsRow(), and XLogRecPtrIsInvalid.

Referenced by main().

◆ XLogDumpStatsRow()

static void XLogDumpStatsRow ( const char *  name,
uint64  n,
uint64  total_count,
uint64  rec_len,
uint64  total_rec_len,
uint64  fpi_len,
uint64  total_fpi_len,
uint64  tot_len,
uint64  total_len 
)
static

Definition at line 580 of file pg_waldump.c.

585 {
586  double n_pct,
587  rec_len_pct,
588  fpi_len_pct,
589  tot_len_pct;
590 
591  n_pct = 0;
592  if (total_count != 0)
593  n_pct = 100 * (double) n / total_count;
594 
595  rec_len_pct = 0;
596  if (total_rec_len != 0)
597  rec_len_pct = 100 * (double) rec_len / total_rec_len;
598 
599  fpi_len_pct = 0;
600  if (total_fpi_len != 0)
601  fpi_len_pct = 100 * (double) fpi_len / total_fpi_len;
602 
603  tot_len_pct = 0;
604  if (total_len != 0)
605  tot_len_pct = 100 * (double) tot_len / total_len;
606 
607  printf("%-27s "
608  "%20" INT64_MODIFIER "u (%6.02f) "
609  "%20" INT64_MODIFIER "u (%6.02f) "
610  "%20" INT64_MODIFIER "u (%6.02f) "
611  "%20" INT64_MODIFIER "u (%6.02f)\n",
612  name, n, n_pct, rec_len, rec_len_pct, fpi_len, fpi_len_pct,
613  tot_len, tot_len_pct);
614 }
const char * name
Definition: encode.c:571

References name, and printf.

Referenced by XLogDumpDisplayStats().

◆ XLogRecordHasFPW()

static bool XLogRecordHasFPW ( XLogReaderState record)
static

Definition at line 465 of file pg_waldump.c.

466 {
467  int block_id;
468 
469  for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
470  {
471  if (!XLogRecHasBlockRef(record, block_id))
472  continue;
473 
474  if (XLogRecHasBlockImage(record, block_id))
475  return true;
476  }
477 
478  return false;
479 }
#define XLogRecMaxBlockId(decoder)
Definition: xlogreader.h:418
#define XLogRecHasBlockImage(decoder, block_id)
Definition: xlogreader.h:423
#define XLogRecHasBlockRef(decoder, block_id)
Definition: xlogreader.h:420

References XLogRecHasBlockImage, XLogRecHasBlockRef, and XLogRecMaxBlockId.

Referenced by main().

◆ XLogRecordMatchesRelationBlock()

static bool XLogRecordMatchesRelationBlock ( XLogReaderState record,
RelFileLocator  matchRlocator,
BlockNumber  matchBlock,
ForkNumber  matchFork 
)
static

Definition at line 434 of file pg_waldump.c.

438 {
439  int block_id;
440 
441  for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
442  {
443  RelFileLocator rlocator;
444  ForkNumber forknum;
445  BlockNumber blk;
446 
447  if (!XLogRecGetBlockTagExtended(record, block_id,
448  &rlocator, &forknum, &blk, NULL))
449  continue;
450 
451  if ((matchFork == InvalidForkNumber || matchFork == forknum) &&
452  (RelFileLocatorEquals(matchRlocator, emptyRelFileLocator) ||
453  RelFileLocatorEquals(matchRlocator, rlocator)) &&
454  (matchBlock == InvalidBlockNumber || matchBlock == blk))
455  return true;
456  }
457 
458  return false;
459 }
uint32 BlockNumber
Definition: block.h:31
#define RelFileLocatorEquals(locator1, locator2)
ForkNumber
Definition: relpath.h:48
bool XLogRecGetBlockTagExtended(XLogReaderState *record, uint8 block_id, RelFileLocator *rlocator, ForkNumber *forknum, BlockNumber *blknum, Buffer *prefetch_buffer)
Definition: xlogreader.c:1987

References emptyRelFileLocator, InvalidBlockNumber, InvalidForkNumber, RelFileLocatorEquals, XLogRecGetBlockTagExtended(), and XLogRecMaxBlockId.

Referenced by main().

◆ XLogRecordSaveFPWs()

static void XLogRecordSaveFPWs ( XLogReaderState record,
const char *  savepath 
)
static

Definition at line 486 of file pg_waldump.c.

487 {
488  int block_id;
489 
490  for (block_id = 0; block_id <= XLogRecMaxBlockId(record); block_id++)
491  {
493  Page page;
494  char filename[MAXPGPATH];
495  char forkname[FORKNAMECHARS + 2]; /* _ + terminating zero */
496  FILE *file;
497  BlockNumber blk;
498  RelFileLocator rnode;
499  ForkNumber fork;
500 
501  if (!XLogRecHasBlockRef(record, block_id))
502  continue;
503 
504  if (!XLogRecHasBlockImage(record, block_id))
505  continue;
506 
507  page = (Page) buf.data;
508 
509  /* Full page exists, so let's save it */
510  if (!RestoreBlockImage(record, block_id, page))
511  pg_fatal("%s", record->errormsg_buf);
512 
513  (void) XLogRecGetBlockTagExtended(record, block_id,
514  &rnode, &fork, &blk, NULL);
515 
516  if (fork >= 0 && fork <= MAX_FORKNUM)
517  sprintf(forkname, "_%s", forkNames[fork]);
518  else
519  pg_fatal("invalid fork number: %u", fork);
520 
521  snprintf(filename, MAXPGPATH, "%s/%08X-%08X.%u.%u.%u.%u%s", savepath,
522  LSN_FORMAT_ARGS(record->ReadRecPtr),
523  rnode.spcOid, rnode.dbOid, rnode.relNumber, blk, forkname);
524 
525  file = fopen(filename, PG_BINARY_W);
526  if (!file)
527  pg_fatal("could not open file \"%s\": %m", filename);
528 
529  if (fwrite(page, BLCKSZ, 1, file) != 1)
530  pg_fatal("could not write file \"%s\": %m", filename);
531 
532  if (fclose(file) != 0)
533  pg_fatal("could not close file \"%s\": %m", filename);
534  }
535 }
Pointer Page
Definition: bufpage.h:78
#define PG_BINARY_W
Definition: c.h:1281
static char * filename
Definition: pg_dumpall.c:119
#define sprintf
Definition: port.h:240
const char *const forkNames[]
Definition: relpath.c:33
#define MAX_FORKNUM
Definition: relpath.h:62
#define FORKNAMECHARS
Definition: relpath.h:64
char * errormsg_buf
Definition: xlogreader.h:311
bool RestoreBlockImage(XLogReaderState *record, uint8 block_id, char *page)
Definition: xlogreader.c:2046

References buf, RelFileLocator::dbOid, XLogReaderState::errormsg_buf, filename, FORKNAMECHARS, forkNames, if(), LSN_FORMAT_ARGS, MAX_FORKNUM, MAXPGPATH, PG_BINARY_W, pg_fatal, XLogReaderState::ReadRecPtr, RelFileLocator::relNumber, RestoreBlockImage(), snprintf, RelFileLocator::spcOid, sprintf, XLogRecGetBlockTagExtended(), XLogRecHasBlockImage, XLogRecHasBlockRef, and XLogRecMaxBlockId.

Referenced by main().

Variable Documentation

◆ emptyRelFileLocator

const RelFileLocator emptyRelFileLocator = {0, 0, 0}
static

Definition at line 45 of file pg_waldump.c.

Referenced by main(), and XLogRecordMatchesRelationBlock().

◆ progname

const char* progname
static

Definition at line 40 of file pg_waldump.c.

Referenced by main(), and usage().

◆ time_to_stop

volatile sig_atomic_t time_to_stop = false
static

Definition at line 43 of file pg_waldump.c.

Referenced by main(), and sigint_handler().

◆ WalSegSz

int WalSegSz
static

Definition at line 42 of file pg_waldump.c.

Referenced by main(), and search_directory().