PostgreSQL Source Code  git master
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros
pg_waldump.c
Go to the documentation of this file.
1 /*-------------------------------------------------------------------------
2  *
3  * pg_waldump.c - decode and display WAL
4  *
5  * Copyright (c) 2013-2017, PostgreSQL Global Development Group
6  *
7  * IDENTIFICATION
8  * src/bin/pg_waldump/pg_waldump.c
9  *-------------------------------------------------------------------------
10  */
11 
12 #define FRONTEND 1
13 #include "postgres.h"
14 
15 #include <dirent.h>
16 #include <unistd.h>
17 
18 #include "access/xlogreader.h"
19 #include "access/xlogrecord.h"
20 #include "access/xlog_internal.h"
21 #include "access/transam.h"
22 #include "common/fe_memutils.h"
23 #include "getopt_long.h"
24 #include "rmgrdesc.h"
25 
26 
27 static const char *progname;
28 
29 typedef struct XLogDumpPrivate
30 {
32  char *inpath;
37 
38 typedef struct XLogDumpConfig
39 {
40  /* display options */
44  bool follow;
45  bool stats;
47 
48  /* filter options */
53 
54 typedef struct Stats
55 {
56  uint64 count;
57  uint64 rec_len;
58  uint64 fpi_len;
59 } Stats;
60 
61 #define MAX_XLINFO_TYPES 16
62 
63 typedef struct XLogDumpStats
64 {
65  uint64 count;
69 
70 static void fatal_error(const char *fmt,...) pg_attribute_printf(1, 2);
71 
72 /*
73  * Big red button to push when things go horribly wrong.
74  */
75 static void
76 fatal_error(const char *fmt,...)
77 {
78  va_list args;
79 
80  fflush(stdout);
81 
82  fprintf(stderr, _("%s: FATAL: "), progname);
83  va_start(args, fmt);
84  vfprintf(stderr, _(fmt), args);
85  va_end(args);
86  fputc('\n', stderr);
87 
88  exit(EXIT_FAILURE);
89 }
90 
91 static void
93 {
94  int i;
95 
96  for (i = 0; i <= RM_MAX_ID; i++)
97  {
98  printf("%s\n", RmgrDescTable[i].rm_name);
99  }
100 }
101 
102 /*
103  * Check whether directory exists and whether we can open it. Keep errno set so
104  * that the caller can report errors somewhat more accurately.
105  */
106 static bool
108 {
109  DIR *dir = opendir(directory);
110 
111  if (dir == NULL)
112  return false;
113  closedir(dir);
114  return true;
115 }
116 
117 /*
118  * Split a pathname as dirname(1) and basename(1) would.
119  *
120  * XXX this probably doesn't do very well on Windows. We probably need to
121  * apply canonicalize_path(), at the very least.
122  */
123 static void
124 split_path(const char *path, char **dir, char **fname)
125 {
126  char *sep;
127 
128  /* split filepath into directory & filename */
129  sep = strrchr(path, '/');
130 
131  /* directory path */
132  if (sep != NULL)
133  {
134  *dir = pg_strdup(path);
135  (*dir)[(sep - path) + 1] = '\0'; /* no strndup */
136  *fname = pg_strdup(sep + 1);
137  }
138  /* local directory */
139  else
140  {
141  *dir = NULL;
142  *fname = pg_strdup(path);
143  }
144 }
145 
146 /*
147  * Try to find the file in several places:
148  * if directory == NULL:
149  * fname
150  * XLOGDIR / fname
151  * $PGDATA / XLOGDIR / fname
152  * else
153  * directory / fname
154  * directory / XLOGDIR / fname
155  *
156  * return a read only fd
157  */
158 static int
159 fuzzy_open_file(const char *directory, const char *fname)
160 {
161  int fd = -1;
162  char fpath[MAXPGPATH];
163 
164  if (directory == NULL)
165  {
166  const char *datadir;
167 
168  /* fname */
169  fd = open(fname, O_RDONLY | PG_BINARY, 0);
170  if (fd < 0 && errno != ENOENT)
171  return -1;
172  else if (fd >= 0)
173  return fd;
174 
175  /* XLOGDIR / fname */
176  snprintf(fpath, MAXPGPATH, "%s/%s",
177  XLOGDIR, fname);
178  fd = open(fpath, O_RDONLY | PG_BINARY, 0);
179  if (fd < 0 && errno != ENOENT)
180  return -1;
181  else if (fd >= 0)
182  return fd;
183 
184  datadir = getenv("PGDATA");
185  /* $PGDATA / XLOGDIR / fname */
186  if (datadir != NULL)
187  {
188  snprintf(fpath, MAXPGPATH, "%s/%s/%s",
189  datadir, XLOGDIR, fname);
190  fd = open(fpath, O_RDONLY | PG_BINARY, 0);
191  if (fd < 0 && errno != ENOENT)
192  return -1;
193  else if (fd >= 0)
194  return fd;
195  }
196  }
197  else
198  {
199  /* directory / fname */
200  snprintf(fpath, MAXPGPATH, "%s/%s",
201  directory, fname);
202  fd = open(fpath, O_RDONLY | PG_BINARY, 0);
203  if (fd < 0 && errno != ENOENT)
204  return -1;
205  else if (fd >= 0)
206  return fd;
207 
208  /* directory / XLOGDIR / fname */
209  snprintf(fpath, MAXPGPATH, "%s/%s/%s",
210  directory, XLOGDIR, fname);
211  fd = open(fpath, O_RDONLY | PG_BINARY, 0);
212  if (fd < 0 && errno != ENOENT)
213  return -1;
214  else if (fd >= 0)
215  return fd;
216  }
217  return -1;
218 }
219 
220 /*
221  * Read count bytes from a segment file in the specified directory, for the
222  * given timeline, containing the specified record pointer; store the data in
223  * the passed buffer.
224  */
225 static void
226 XLogDumpXLogRead(const char *directory, TimeLineID timeline_id,
227  XLogRecPtr startptr, char *buf, Size count)
228 {
229  char *p;
230  XLogRecPtr recptr;
231  Size nbytes;
232 
233  static int sendFile = -1;
234  static XLogSegNo sendSegNo = 0;
235  static uint32 sendOff = 0;
236 
237  p = buf;
238  recptr = startptr;
239  nbytes = count;
240 
241  while (nbytes > 0)
242  {
243  uint32 startoff;
244  int segbytes;
245  int readbytes;
246 
247  startoff = recptr % XLogSegSize;
248 
249  if (sendFile < 0 || !XLByteInSeg(recptr, sendSegNo))
250  {
251  char fname[MAXFNAMELEN];
252  int tries;
253 
254  /* Switch to another logfile segment */
255  if (sendFile >= 0)
256  close(sendFile);
257 
258  XLByteToSeg(recptr, sendSegNo);
259 
260  XLogFileName(fname, timeline_id, sendSegNo);
261 
262  /*
263  * In follow mode there is a short period of time after the server
264  * has written the end of the previous file before the new file is
265  * available. So we loop for 5 seconds looking for the file to
266  * appear before giving up.
267  */
268  for (tries = 0; tries < 10; tries++)
269  {
270  sendFile = fuzzy_open_file(directory, fname);
271  if (sendFile >= 0)
272  break;
273  if (errno == ENOENT)
274  {
275  int save_errno = errno;
276 
277  /* File not there yet, try again */
278  pg_usleep(500 * 1000);
279 
280  errno = save_errno;
281  continue;
282  }
283  /* Any other error, fall through and fail */
284  break;
285  }
286 
287  if (sendFile < 0)
288  fatal_error("could not find file \"%s\": %s",
289  fname, strerror(errno));
290  sendOff = 0;
291  }
292 
293  /* Need to seek in the file? */
294  if (sendOff != startoff)
295  {
296  if (lseek(sendFile, (off_t) startoff, SEEK_SET) < 0)
297  {
298  int err = errno;
299  char fname[MAXPGPATH];
300 
301  XLogFileName(fname, timeline_id, sendSegNo);
302 
303  fatal_error("could not seek in log segment %s to offset %u: %s",
304  fname, startoff, strerror(err));
305  }
306  sendOff = startoff;
307  }
308 
309  /* How many bytes are within this segment? */
310  if (nbytes > (XLogSegSize - startoff))
311  segbytes = XLogSegSize - startoff;
312  else
313  segbytes = nbytes;
314 
315  readbytes = read(sendFile, p, segbytes);
316  if (readbytes <= 0)
317  {
318  int err = errno;
319  char fname[MAXPGPATH];
320 
321  XLogFileName(fname, timeline_id, sendSegNo);
322 
323  fatal_error("could not read from log segment %s, offset %d, length %d: %s",
324  fname, sendOff, segbytes, strerror(err));
325  }
326 
327  /* Update state for read */
328  recptr += readbytes;
329 
330  sendOff += readbytes;
331  nbytes -= readbytes;
332  p += readbytes;
333  }
334 }
335 
336 /*
337  * XLogReader read_page callback
338  */
339 static int
340 XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
341  XLogRecPtr targetPtr, char *readBuff, TimeLineID *curFileTLI)
342 {
343  XLogDumpPrivate *private = state->private_data;
344  int count = XLOG_BLCKSZ;
345 
346  if (private->endptr != InvalidXLogRecPtr)
347  {
348  if (targetPagePtr + XLOG_BLCKSZ <= private->endptr)
349  count = XLOG_BLCKSZ;
350  else if (targetPagePtr + reqLen <= private->endptr)
351  count = private->endptr - targetPagePtr;
352  else
353  {
354  private->endptr_reached = true;
355  return -1;
356  }
357  }
358 
359  XLogDumpXLogRead(private->inpath, private->timeline, targetPagePtr,
360  readBuff, count);
361 
362  return count;
363 }
364 
365 /*
366  * Calculate the size of a record, split into !FPI and FPI parts.
367  */
368 static void
369 XLogDumpRecordLen(XLogReaderState *record, uint32 *rec_len, uint32 *fpi_len)
370 {
371  int block_id;
372 
373  /*
374  * Calculate the amount of FPI data in the record.
375  *
376  * XXX: We peek into xlogreader's private decoded backup blocks for the
377  * bimg_len indicating the length of FPI data. It doesn't seem worth it to
378  * add an accessor macro for this.
379  */
380  *fpi_len = 0;
381  for (block_id = 0; block_id <= record->max_block_id; block_id++)
382  {
383  if (XLogRecHasBlockImage(record, block_id))
384  *fpi_len += record->blocks[block_id].bimg_len;
385  }
386 
387  /*
388  * Calculate the length of the record as the total length - the length of
389  * all the block images.
390  */
391  *rec_len = XLogRecGetTotalLen(record) - *fpi_len;
392 }
393 
394 /*
395  * Store per-rmgr and per-record statistics for a given record.
396  */
397 static void
399  XLogReaderState *record)
400 {
401  RmgrId rmid;
402  uint8 recid;
403  uint32 rec_len;
404  uint32 fpi_len;
405 
406  stats->count++;
407 
408  rmid = XLogRecGetRmid(record);
409 
410  XLogDumpRecordLen(record, &rec_len, &fpi_len);
411 
412  /* Update per-rmgr statistics */
413 
414  stats->rmgr_stats[rmid].count++;
415  stats->rmgr_stats[rmid].rec_len += rec_len;
416  stats->rmgr_stats[rmid].fpi_len += fpi_len;
417 
418  /*
419  * Update per-record statistics, where the record is identified by a
420  * combination of the RmgrId and the four bits of the xl_info field that
421  * are the rmgr's domain (resulting in sixteen possible entries per
422  * RmgrId).
423  */
424 
425  recid = XLogRecGetInfo(record) >> 4;
426 
427  stats->record_stats[rmid][recid].count++;
428  stats->record_stats[rmid][recid].rec_len += rec_len;
429  stats->record_stats[rmid][recid].fpi_len += fpi_len;
430 }
431 
432 /*
433  * Print a record to stdout
434  */
435 static void
437 {
438  const char *id;
439  const RmgrDescData *desc = &RmgrDescTable[XLogRecGetRmid(record)];
440  uint32 rec_len;
441  uint32 fpi_len;
442  RelFileNode rnode;
443  ForkNumber forknum;
444  BlockNumber blk;
445  int block_id;
446  uint8 info = XLogRecGetInfo(record);
447  XLogRecPtr xl_prev = XLogRecGetPrev(record);
448 
449  XLogDumpRecordLen(record, &rec_len, &fpi_len);
450 
451  id = desc->rm_identify(info);
452  if (id == NULL)
453  id = psprintf("UNKNOWN (%x)", info & ~XLR_INFO_MASK);
454 
455  printf("rmgr: %-11s len (rec/tot): %6u/%6u, tx: %10u, lsn: %X/%08X, prev %X/%08X, ",
456  desc->rm_name,
457  rec_len, XLogRecGetTotalLen(record),
458  XLogRecGetXid(record),
459  (uint32) (record->ReadRecPtr >> 32), (uint32) record->ReadRecPtr,
460  (uint32) (xl_prev >> 32), (uint32) xl_prev);
461  printf("desc: %s ", id);
462 
463  /* the desc routine will printf the description directly to stdout */
464  desc->rm_desc(NULL, record);
465 
466  if (!config->bkp_details)
467  {
468  /* print block references (short format) */
469  for (block_id = 0; block_id <= record->max_block_id; block_id++)
470  {
471  if (!XLogRecHasBlockRef(record, block_id))
472  continue;
473 
474  XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk);
475  if (forknum != MAIN_FORKNUM)
476  printf(", blkref #%u: rel %u/%u/%u fork %s blk %u",
477  block_id,
478  rnode.spcNode, rnode.dbNode, rnode.relNode,
479  forkNames[forknum],
480  blk);
481  else
482  printf(", blkref #%u: rel %u/%u/%u blk %u",
483  block_id,
484  rnode.spcNode, rnode.dbNode, rnode.relNode,
485  blk);
486  if (XLogRecHasBlockImage(record, block_id))
487  {
488  if (XLogRecBlockImageApply(record, block_id))
489  printf(" FPW");
490  else
491  printf(" FPW for WAL verification");
492  }
493  }
494  putchar('\n');
495  }
496  else
497  {
498  /* print block references (detailed format) */
499  putchar('\n');
500  for (block_id = 0; block_id <= record->max_block_id; block_id++)
501  {
502  if (!XLogRecHasBlockRef(record, block_id))
503  continue;
504 
505  XLogRecGetBlockTag(record, block_id, &rnode, &forknum, &blk);
506  printf("\tblkref #%u: rel %u/%u/%u fork %s blk %u",
507  block_id,
508  rnode.spcNode, rnode.dbNode, rnode.relNode,
509  forkNames[forknum],
510  blk);
511  if (XLogRecHasBlockImage(record, block_id))
512  {
513  if (record->blocks[block_id].bimg_info &
515  {
516  printf(" (FPW%s); hole: offset: %u, length: %u, "
517  "compression saved: %u\n",
518  XLogRecBlockImageApply(record, block_id) ?
519  "" : " for WAL verification",
520  record->blocks[block_id].hole_offset,
521  record->blocks[block_id].hole_length,
522  BLCKSZ -
523  record->blocks[block_id].hole_length -
524  record->blocks[block_id].bimg_len);
525  }
526  else
527  {
528  printf(" (FPW%s); hole: offset: %u, length: %u\n",
529  XLogRecBlockImageApply(record, block_id) ?
530  "" : " for WAL verification",
531  record->blocks[block_id].hole_offset,
532  record->blocks[block_id].hole_length);
533  }
534  }
535  putchar('\n');
536  }
537  }
538 }
539 
540 /*
541  * Display a single row of record counts and sizes for an rmgr or record.
542  */
543 static void
544 XLogDumpStatsRow(const char *name,
545  uint64 n, uint64 total_count,
546  uint64 rec_len, uint64 total_rec_len,
547  uint64 fpi_len, uint64 total_fpi_len,
548  uint64 tot_len, uint64 total_len)
549 {
550  double n_pct,
551  rec_len_pct,
552  fpi_len_pct,
553  tot_len_pct;
554 
555  n_pct = 0;
556  if (total_count != 0)
557  n_pct = 100 * (double) n / total_count;
558 
559  rec_len_pct = 0;
560  if (total_rec_len != 0)
561  rec_len_pct = 100 * (double) rec_len / total_rec_len;
562 
563  fpi_len_pct = 0;
564  if (total_fpi_len != 0)
565  fpi_len_pct = 100 * (double) fpi_len / total_fpi_len;
566 
567  tot_len_pct = 0;
568  if (total_len != 0)
569  tot_len_pct = 100 * (double) tot_len / total_len;
570 
571  printf("%-27s "
572  "%20" INT64_MODIFIER "u (%6.02f) "
573  "%20" INT64_MODIFIER "u (%6.02f) "
574  "%20" INT64_MODIFIER "u (%6.02f) "
575  "%20" INT64_MODIFIER "u (%6.02f)\n",
576  name, n, n_pct, rec_len, rec_len_pct, fpi_len, fpi_len_pct,
577  tot_len, tot_len_pct);
578 }
579 
580 
581 /*
582  * Display summary statistics about the records seen so far.
583  */
584 static void
586 {
587  int ri,
588  rj;
589  uint64 total_count = 0;
590  uint64 total_rec_len = 0;
591  uint64 total_fpi_len = 0;
592  uint64 total_len = 0;
593  double rec_len_pct,
594  fpi_len_pct;
595 
596  /* ---
597  * Make a first pass to calculate column totals:
598  * count(*),
599  * sum(xl_len+SizeOfXLogRecord),
600  * sum(xl_tot_len-xl_len-SizeOfXLogRecord), and
601  * sum(xl_tot_len).
602  * These are used to calculate percentages for each record type.
603  * ---
604  */
605 
606  for (ri = 0; ri < RM_NEXT_ID; ri++)
607  {
608  total_count += stats->rmgr_stats[ri].count;
609  total_rec_len += stats->rmgr_stats[ri].rec_len;
610  total_fpi_len += stats->rmgr_stats[ri].fpi_len;
611  }
612  total_len = total_rec_len + total_fpi_len;
613 
614  /*
615  * 27 is strlen("Transaction/COMMIT_PREPARED"), 20 is strlen(2^64), 8 is
616  * strlen("(100.00%)")
617  */
618 
619  printf("%-27s %20s %8s %20s %8s %20s %8s %20s %8s\n"
620  "%-27s %20s %8s %20s %8s %20s %8s %20s %8s\n",
621  "Type", "N", "(%)", "Record size", "(%)", "FPI size", "(%)", "Combined size", "(%)",
622  "----", "-", "---", "-----------", "---", "--------", "---", "-------------", "---");
623 
624  for (ri = 0; ri < RM_NEXT_ID; ri++)
625  {
626  uint64 count,
627  rec_len,
628  fpi_len,
629  tot_len;
630  const RmgrDescData *desc = &RmgrDescTable[ri];
631 
632  if (!config->stats_per_record)
633  {
634  count = stats->rmgr_stats[ri].count;
635  rec_len = stats->rmgr_stats[ri].rec_len;
636  fpi_len = stats->rmgr_stats[ri].fpi_len;
637  tot_len = rec_len + fpi_len;
638 
640  count, total_count, rec_len, total_rec_len,
641  fpi_len, total_fpi_len, tot_len, total_len);
642  }
643  else
644  {
645  for (rj = 0; rj < MAX_XLINFO_TYPES; rj++)
646  {
647  const char *id;
648 
649  count = stats->record_stats[ri][rj].count;
650  rec_len = stats->record_stats[ri][rj].rec_len;
651  fpi_len = stats->record_stats[ri][rj].fpi_len;
652  tot_len = rec_len + fpi_len;
653 
654  /* Skip undefined combinations and ones that didn't occur */
655  if (count == 0)
656  continue;
657 
658  /* the upper four bits in xl_info are the rmgr's */
659  id = desc->rm_identify(rj << 4);
660  if (id == NULL)
661  id = psprintf("UNKNOWN (%x)", rj << 4);
662 
663  XLogDumpStatsRow(psprintf("%s/%s", desc->rm_name, id),
664  count, total_count, rec_len, total_rec_len,
665  fpi_len, total_fpi_len, tot_len, total_len);
666  }
667  }
668  }
669 
670  printf("%-27s %20s %8s %20s %8s %20s %8s %20s\n",
671  "", "--------", "", "--------", "", "--------", "", "--------");
672 
673  /*
674  * The percentages in earlier rows were calculated against the column
675  * total, but the ones that follow are against the row total. Note that
676  * these are displayed with a % symbol to differentiate them from the
677  * earlier ones, and are thus up to 9 characters long.
678  */
679 
680  rec_len_pct = 0;
681  if (total_len != 0)
682  rec_len_pct = 100 * (double) total_rec_len / total_len;
683 
684  fpi_len_pct = 0;
685  if (total_len != 0)
686  fpi_len_pct = 100 * (double) total_fpi_len / total_len;
687 
688  printf("%-27s "
689  "%20" INT64_MODIFIER "u %-9s"
690  "%20" INT64_MODIFIER "u %-9s"
691  "%20" INT64_MODIFIER "u %-9s"
692  "%20" INT64_MODIFIER "u %-6s\n",
693  "Total", stats->count, "",
694  total_rec_len, psprintf("[%.02f%%]", rec_len_pct),
695  total_fpi_len, psprintf("[%.02f%%]", fpi_len_pct),
696  total_len, "[100%]");
697 }
698 
699 static void
700 usage(void)
701 {
702  printf(_("%s decodes and displays PostgreSQL write-ahead logs for debugging.\n\n"),
703  progname);
704  printf(_("Usage:\n"));
705  printf(_(" %s [OPTION]... [STARTSEG [ENDSEG]]\n"), progname);
706  printf(_("\nOptions:\n"));
707  printf(_(" -b, --bkp-details output detailed information about backup blocks\n"));
708  printf(_(" -e, --end=RECPTR stop reading at WAL location RECPTR\n"));
709  printf(_(" -f, --follow keep retrying after reaching end of WAL\n"));
710  printf(_(" -n, --limit=N number of records to display\n"));
711  printf(_(" -p, --path=PATH directory in which to find log segment files or a\n"
712  " directory with a ./pg_wal that contains such files\n"
713  " (default: current directory, ./pg_wal, PGDATA/pg_wal)\n"));
714  printf(_(" -r, --rmgr=RMGR only show records generated by resource manager RMGR\n"
715  " use --rmgr=list to list valid resource manager names\n"));
716  printf(_(" -s, --start=RECPTR start reading at WAL location RECPTR\n"));
717  printf(_(" -t, --timeline=TLI timeline from which to read log records\n"
718  " (default: 1 or the value used in STARTSEG)\n"));
719  printf(_(" -V, --version output version information, then exit\n"));
720  printf(_(" -x, --xid=XID only show records with TransactionId XID\n"));
721  printf(_(" -z, --stats[=record] show statistics instead of records\n"
722  " (optionally, show per-record statistics)\n"));
723  printf(_(" -?, --help show this help, then exit\n"));
724 }
725 
726 int
727 main(int argc, char **argv)
728 {
729  uint32 xlogid;
730  uint32 xrecoff;
731  XLogReaderState *xlogreader_state;
732  XLogDumpPrivate private;
733  XLogDumpConfig config;
734  XLogDumpStats stats;
735  XLogRecord *record;
736  XLogRecPtr first_record;
737  char *errormsg;
738 
739  static struct option long_options[] = {
740  {"bkp-details", no_argument, NULL, 'b'},
741  {"end", required_argument, NULL, 'e'},
742  {"follow", no_argument, NULL, 'f'},
743  {"help", no_argument, NULL, '?'},
744  {"limit", required_argument, NULL, 'n'},
745  {"path", required_argument, NULL, 'p'},
746  {"rmgr", required_argument, NULL, 'r'},
747  {"start", required_argument, NULL, 's'},
748  {"timeline", required_argument, NULL, 't'},
749  {"xid", required_argument, NULL, 'x'},
750  {"version", no_argument, NULL, 'V'},
751  {"stats", optional_argument, NULL, 'z'},
752  {NULL, 0, NULL, 0}
753  };
754 
755  int option;
756  int optindex = 0;
757 
758  set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_waldump"));
759  progname = get_progname(argv[0]);
760 
761  memset(&private, 0, sizeof(XLogDumpPrivate));
762  memset(&config, 0, sizeof(XLogDumpConfig));
763  memset(&stats, 0, sizeof(XLogDumpStats));
764 
765  private.timeline = 1;
766  private.startptr = InvalidXLogRecPtr;
767  private.endptr = InvalidXLogRecPtr;
768  private.endptr_reached = false;
769 
770  config.bkp_details = false;
771  config.stop_after_records = -1;
772  config.already_displayed_records = 0;
773  config.follow = false;
774  config.filter_by_rmgr = -1;
776  config.filter_by_xid_enabled = false;
777  config.stats = false;
778  config.stats_per_record = false;
779 
780  if (argc <= 1)
781  {
782  fprintf(stderr, _("%s: no arguments specified\n"), progname);
783  goto bad_argument;
784  }
785 
786  while ((option = getopt_long(argc, argv, "be:?fn:p:r:s:t:Vx:z",
787  long_options, &optindex)) != -1)
788  {
789  switch (option)
790  {
791  case 'b':
792  config.bkp_details = true;
793  break;
794  case 'e':
795  if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
796  {
797  fprintf(stderr, _("%s: could not parse end WAL location \"%s\"\n"),
798  progname, optarg);
799  goto bad_argument;
800  }
801  private.endptr = (uint64) xlogid << 32 | xrecoff;
802  break;
803  case 'f':
804  config.follow = true;
805  break;
806  case '?':
807  usage();
808  exit(EXIT_SUCCESS);
809  break;
810  case 'n':
811  if (sscanf(optarg, "%d", &config.stop_after_records) != 1)
812  {
813  fprintf(stderr, _("%s: could not parse limit \"%s\"\n"),
814  progname, optarg);
815  goto bad_argument;
816  }
817  break;
818  case 'p':
819  private.inpath = pg_strdup(optarg);
820  break;
821  case 'r':
822  {
823  int i;
824 
825  if (pg_strcasecmp(optarg, "list") == 0)
826  {
827  print_rmgr_list();
828  exit(EXIT_SUCCESS);
829  }
830 
831  for (i = 0; i <= RM_MAX_ID; i++)
832  {
833  if (pg_strcasecmp(optarg, RmgrDescTable[i].rm_name) == 0)
834  {
835  config.filter_by_rmgr = i;
836  break;
837  }
838  }
839 
840  if (config.filter_by_rmgr == -1)
841  {
842  fprintf(stderr, _("%s: resource manager \"%s\" does not exist\n"),
843  progname, optarg);
844  goto bad_argument;
845  }
846  }
847  break;
848  case 's':
849  if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
850  {
851  fprintf(stderr, _("%s: could not parse start WAL location \"%s\"\n"),
852  progname, optarg);
853  goto bad_argument;
854  }
855  else
856  private.startptr = (uint64) xlogid << 32 | xrecoff;
857  break;
858  case 't':
859  if (sscanf(optarg, "%d", &private.timeline) != 1)
860  {
861  fprintf(stderr, _("%s: could not parse timeline \"%s\"\n"),
862  progname, optarg);
863  goto bad_argument;
864  }
865  break;
866  case 'V':
867  puts("pg_waldump (PostgreSQL) " PG_VERSION);
868  exit(EXIT_SUCCESS);
869  break;
870  case 'x':
871  if (sscanf(optarg, "%u", &config.filter_by_xid) != 1)
872  {
873  fprintf(stderr, _("%s: could not parse \"%s\" as a valid xid\n"),
874  progname, optarg);
875  goto bad_argument;
876  }
877  config.filter_by_xid_enabled = true;
878  break;
879  case 'z':
880  config.stats = true;
881  config.stats_per_record = false;
882  if (optarg)
883  {
884  if (strcmp(optarg, "record") == 0)
885  config.stats_per_record = true;
886  else if (strcmp(optarg, "rmgr") != 0)
887  {
888  fprintf(stderr, _("%s: unrecognized argument to --stats: %s\n"),
889  progname, optarg);
890  goto bad_argument;
891  }
892  }
893  break;
894  default:
895  goto bad_argument;
896  }
897  }
898 
899  if ((optind + 2) < argc)
900  {
901  fprintf(stderr,
902  _("%s: too many command-line arguments (first is \"%s\")\n"),
903  progname, argv[optind + 2]);
904  goto bad_argument;
905  }
906 
907  if (private.inpath != NULL)
908  {
909  /* validate path points to directory */
910  if (!verify_directory(private.inpath))
911  {
912  fprintf(stderr,
913  _("%s: path \"%s\" cannot be opened: %s\n"),
914  progname, private.inpath, strerror(errno));
915  goto bad_argument;
916  }
917  }
918 
919  /* parse files as start/end boundaries, extract path if not specified */
920  if (optind < argc)
921  {
922  char *directory = NULL;
923  char *fname = NULL;
924  int fd;
925  XLogSegNo segno;
926 
927  split_path(argv[optind], &directory, &fname);
928 
929  if (private.inpath == NULL && directory != NULL)
930  {
931  private.inpath = directory;
932 
933  if (!verify_directory(private.inpath))
934  fatal_error("cannot open directory \"%s\": %s",
935  private.inpath, strerror(errno));
936  }
937 
938  fd = fuzzy_open_file(private.inpath, fname);
939  if (fd < 0)
940  fatal_error("could not open file \"%s\"", fname);
941  close(fd);
942 
943  /* parse position from file */
944  XLogFromFileName(fname, &private.timeline, &segno);
945 
946  if (XLogRecPtrIsInvalid(private.startptr))
947  XLogSegNoOffsetToRecPtr(segno, 0, private.startptr);
948  else if (!XLByteInSeg(private.startptr, segno))
949  {
950  fprintf(stderr,
951  _("%s: start WAL location %X/%X is not inside file \"%s\"\n"),
952  progname,
953  (uint32) (private.startptr >> 32),
954  (uint32) private.startptr,
955  fname);
956  goto bad_argument;
957  }
958 
959  /* no second file specified, set end position */
960  if (!(optind + 1 < argc) && XLogRecPtrIsInvalid(private.endptr))
961  XLogSegNoOffsetToRecPtr(segno + 1, 0, private.endptr);
962 
963  /* parse ENDSEG if passed */
964  if (optind + 1 < argc)
965  {
966  XLogSegNo endsegno;
967 
968  /* ignore directory, already have that */
969  split_path(argv[optind + 1], &directory, &fname);
970 
971  fd = fuzzy_open_file(private.inpath, fname);
972  if (fd < 0)
973  fatal_error("could not open file \"%s\"", fname);
974  close(fd);
975 
976  /* parse position from file */
977  XLogFromFileName(fname, &private.timeline, &endsegno);
978 
979  if (endsegno < segno)
980  fatal_error("ENDSEG %s is before STARTSEG %s",
981  argv[optind + 1], argv[optind]);
982 
983  if (XLogRecPtrIsInvalid(private.endptr))
984  XLogSegNoOffsetToRecPtr(endsegno + 1, 0, private.endptr);
985 
986  /* set segno to endsegno for check of --end */
987  segno = endsegno;
988  }
989 
990 
991  if (!XLByteInSeg(private.endptr, segno) &&
992  private.endptr != (segno + 1) * XLogSegSize)
993  {
994  fprintf(stderr,
995  _("%s: end WAL location %X/%X is not inside file \"%s\"\n"),
996  progname,
997  (uint32) (private.endptr >> 32),
998  (uint32) private.endptr,
999  argv[argc - 1]);
1000  goto bad_argument;
1001  }
1002  }
1003 
1004  /* we don't know what to print */
1005  if (XLogRecPtrIsInvalid(private.startptr))
1006  {
1007  fprintf(stderr, _("%s: no start WAL location given\n"), progname);
1008  goto bad_argument;
1009  }
1010 
1011  /* done with argument parsing, do the actual work */
1012 
1013  /* we have everything we need, start reading */
1014  xlogreader_state = XLogReaderAllocate(XLogDumpReadPage, &private);
1015  if (!xlogreader_state)
1016  fatal_error("out of memory");
1017 
1018  /* first find a valid recptr to start from */
1019  first_record = XLogFindNextRecord(xlogreader_state, private.startptr);
1020 
1021  if (first_record == InvalidXLogRecPtr)
1022  fatal_error("could not find a valid record after %X/%X",
1023  (uint32) (private.startptr >> 32),
1024  (uint32) private.startptr);
1025 
1026  /*
1027  * Display a message that we're skipping data if `from` wasn't a pointer
1028  * to the start of a record and also wasn't a pointer to the beginning of
1029  * a segment (e.g. we were used in file mode).
1030  */
1031  if (first_record != private.startptr && (private.startptr % XLogSegSize) != 0)
1032  printf(_("first record is after %X/%X, at %X/%X, skipping over %u bytes\n"),
1033  (uint32) (private.startptr >> 32), (uint32) private.startptr,
1034  (uint32) (first_record >> 32), (uint32) first_record,
1035  (uint32) (first_record - private.startptr));
1036 
1037  for (;;)
1038  {
1039  /* try to read the next record */
1040  record = XLogReadRecord(xlogreader_state, first_record, &errormsg);
1041  if (!record)
1042  {
1043  if (!config.follow || private.endptr_reached)
1044  break;
1045  else
1046  {
1047  pg_usleep(1000000L); /* 1 second */
1048  continue;
1049  }
1050  }
1051 
1052  /* after reading the first record, continue at next one */
1053  first_record = InvalidXLogRecPtr;
1054 
1055  /* apply all specified filters */
1056  if (config.filter_by_rmgr != -1 &&
1057  config.filter_by_rmgr != record->xl_rmid)
1058  continue;
1059 
1060  if (config.filter_by_xid_enabled &&
1061  config.filter_by_xid != record->xl_xid)
1062  continue;
1063 
1064  /* process the record */
1065  if (config.stats == true)
1066  XLogDumpCountRecord(&config, &stats, xlogreader_state);
1067  else
1068  XLogDumpDisplayRecord(&config, xlogreader_state);
1069 
1070  /* check whether we printed enough */
1071  config.already_displayed_records++;
1072  if (config.stop_after_records > 0 &&
1074  break;
1075  }
1076 
1077  if (config.stats == true)
1078  XLogDumpDisplayStats(&config, &stats);
1079 
1080  if (errormsg)
1081  fatal_error("error in WAL record at %X/%X: %s",
1082  (uint32) (xlogreader_state->ReadRecPtr >> 32),
1083  (uint32) xlogreader_state->ReadRecPtr,
1084  errormsg);
1085 
1086  XLogReaderFree(xlogreader_state);
1087 
1088  return EXIT_SUCCESS;
1089 
1090 bad_argument:
1091  fprintf(stderr, _("Try \"%s --help\" for more information.\n"), progname);
1092  return EXIT_FAILURE;
1093 }
XLogReaderState * XLogReaderAllocate(XLogPageReadCB pagereadfunc, void *private_data)
Definition: xlogreader.c:67
#define XLogSegSize
Definition: xlog_internal.h:92
static void usage(void)
Definition: pg_waldump.c:700
static void XLogDumpDisplayStats(XLogDumpConfig *config, XLogDumpStats *stats)
Definition: pg_waldump.c:585
#define InvalidXLogRecPtr
Definition: xlogdefs.h:28
int already_displayed_records
Definition: pg_waldump.c:43
XLogRecPtr endptr
Definition: pg_waldump.c:34
uint32 TimeLineID
Definition: xlogdefs.h:45
#define EXIT_SUCCESS
Definition: settings.h:147
uint32 TransactionId
Definition: c.h:397
uint64 count
Definition: pg_waldump.c:56
#define XLogRecHasBlockImage(decoder, block_id)
Definition: xlogreader.h:225
int filter_by_rmgr
Definition: pg_waldump.c:49
XLogRecPtr startptr
Definition: pg_waldump.c:33
const char * get_progname(const char *argv0)
Definition: path.c:453
int getopt_long(int argc, char *const argv[], const char *optstring, const struct option *longopts, int *longindex)
Definition: getopt_long.c:57
char * psprintf(const char *fmt,...)
Definition: psprintf.c:46
static void fatal_error(const char *fmt,...) pg_attribute_printf(1
Definition: pg_waldump.c:76
static int sendFile
Definition: walsender.c:135
uint64 count
Definition: pg_waldump.c:65
uint16 hole_offset
Definition: xlogreader.h:57
struct Stats Stats
#define XLogFileName(fname, tli, logSegNo)
unsigned char uint8
Definition: c.h:266
int closedir(DIR *)
Definition: dirent.c:111
#define XLogRecHasBlockRef(decoder, block_id)
Definition: xlogreader.h:223
Stats record_stats[RM_NEXT_ID][MAX_XLINFO_TYPES]
Definition: pg_waldump.c:67
RmgrId xl_rmid
Definition: xlogrecord.h:47
int snprintf(char *str, size_t count, const char *fmt,...) pg_attribute_printf(3
uint32 BlockNumber
Definition: block.h:31
TransactionId filter_by_xid
Definition: pg_waldump.c:50
int pg_strcasecmp(const char *s1, const char *s2)
Definition: pgstrcasecmp.c:36
bool bkp_details
Definition: pg_waldump.c:41
bool endptr_reached
Definition: pg_waldump.c:35
void * private_data
Definition: xlogreader.h:108
uint16 bimg_len
Definition: xlogreader.h:59
static int fd(const char *x, int i)
Definition: preproc-init.c:105
#define PG_BINARY
Definition: c.h:1039
void(* rm_desc)(StringInfo buf, XLogReaderState *record)
Definition: rmgrdesc.h:16
#define XLogRecGetTotalLen(decoder)
Definition: xlogreader.h:214
XLogRecord * XLogReadRecord(XLogReaderState *state, XLogRecPtr RecPtr, char **errormsg)
Definition: xlogreader.c:193
#define pg_attribute_printf(f, a)
Definition: c.h:639
#define XLogSegNoOffsetToRecPtr(segno, offset, dest)
Definition: xlog_internal.h:95
Stats rmgr_stats[RM_NEXT_ID]
Definition: pg_waldump.c:66
bool filter_by_xid_enabled
Definition: pg_waldump.c:51
void pg_usleep(long microsec)
Definition: signal.c:53
#define required_argument
Definition: getopt_long.h:25
int optind
Definition: getopt.c:51
Definition: dirent.c:25
uint16 hole_length
Definition: xlogreader.h:58
TimeLineID timeline
Definition: pg_waldump.c:31
#define MAXPGPATH
const RmgrDescData RmgrDescTable[RM_MAX_ID+1]
Definition: rmgrdesc.c:38
DIR * opendir(const char *)
Definition: dirent.c:33
#define XLogRecGetPrev(decoder)
Definition: xlogreader.h:215
static void XLogDumpCountRecord(XLogDumpConfig *config, XLogDumpStats *stats, XLogReaderState *record)
Definition: pg_waldump.c:398
static char * buf
Definition: pg_test_fsync.c:66
static int XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, XLogRecPtr targetPtr, char *readBuff, TimeLineID *curFileTLI)
Definition: pg_waldump.c:340
uint64 XLogSegNo
Definition: xlogdefs.h:34
static void XLogDumpDisplayRecord(XLogDumpConfig *config, XLogReaderState *record)
Definition: pg_waldump.c:436
XLogRecPtr ReadRecPtr
Definition: xlogreader.h:114
char * datadir
char * pg_strdup(const char *in)
Definition: fe_memutils.c:85
#define InvalidTransactionId
Definition: transam.h:31
static void split_path(const char *path, char **dir, char **fname)
Definition: pg_waldump.c:124
unsigned int uint32
Definition: c.h:268
struct XLogDumpConfig XLogDumpConfig
#define XLogRecGetInfo(decoder)
Definition: xlogreader.h:216
ForkNumber
Definition: relpath.h:24
const char * rm_name
Definition: rmgrdesc.h:15
#define XLogRecPtrIsInvalid(r)
Definition: xlogdefs.h:29
void XLogReaderFree(XLogReaderState *state)
Definition: xlogreader.c:125
#define MAXFNAMELEN
#define RM_MAX_ID
Definition: rmgr.h:33
bool XLogRecGetBlockTag(XLogReaderState *record, uint8 block_id, RelFileNode *rnode, ForkNumber *forknum, BlockNumber *blknum)
Definition: xlogreader.c:1307
#define XLogRecGetXid(decoder)
Definition: xlogreader.h:218
#define no_argument
Definition: getopt_long.h:24
#define PG_TEXTDOMAIN(domain)
Definition: c.h:1013
#define BKPIMAGE_IS_COMPRESSED
Definition: xlogrecord.h:148
#define XLOGDIR
uint8 RmgrId
Definition: rmgr.h:11
#define XLByteToSeg(xlrp, logSegNo)
#define NULL
Definition: c.h:229
uint64 XLogRecPtr
Definition: xlogdefs.h:21
#define XLR_INFO_MASK
Definition: xlogrecord.h:62
#define MAX_XLINFO_TYPES
Definition: pg_waldump.c:61
Definition: regguts.h:298
size_t Size
Definition: c.h:356
static const char * directory
Definition: zic.c:561
#define optional_argument
Definition: getopt_long.h:26
static int fuzzy_open_file(const char *directory, const char *fname)
Definition: pg_waldump.c:159
static bool verify_directory(const char *directory)
Definition: pg_waldump.c:107
struct XLogDumpPrivate XLogDumpPrivate
static XLogSegNo sendSegNo
Definition: walsender.c:136
const char * name
Definition: encode.c:521
uint64 fpi_len
Definition: pg_waldump.c:58
uint64 rec_len
Definition: pg_waldump.c:57
TransactionId xl_xid
Definition: xlogrecord.h:44
static TimeLineID curFileTLI
Definition: xlog.c:319
void set_pglocale_pgservice(const char *argv0, const char *app)
Definition: exec.c:550
static void XLogDumpXLogRead(const char *directory, TimeLineID timeline_id, XLogRecPtr startptr, char *buf, Size count)
Definition: pg_waldump.c:226
static const char * progname
Definition: pg_waldump.c:27
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:544
char * optarg
Definition: getopt.c:53
int i
const char * strerror(int errnum)
Definition: strerror.c:19
int stop_after_records
Definition: pg_waldump.c:42
#define EXIT_FAILURE
Definition: settings.h:151
struct XLogDumpStats XLogDumpStats
static uint32 sendOff
Definition: walsender.c:137
#define XLogRecBlockImageApply(decoder, block_id)
Definition: xlogreader.h:227
static void print_rmgr_list(void)
Definition: pg_waldump.c:92
bool stats_per_record
Definition: pg_waldump.c:46
#define close(a)
Definition: win32.h:12
const char *(* rm_identify)(uint8 info)
Definition: rmgrdesc.h:17
const char *const forkNames[]
Definition: relpath.c:34
#define XLByteInSeg(xlrp, logSegNo)
#define XLogFromFileName(fname, tli, logSegNo)
#define _(x)
Definition: elog.c:84
int main(int argc, char **argv)
Definition: pg_waldump.c:727
#define read(a, b, c)
Definition: win32.h:13
DecodedBkpBlock blocks[XLR_MAX_BLOCK_ID+1]
Definition: xlogreader.h:134
#define XLogRecGetRmid(decoder)
Definition: xlogreader.h:217
static void XLogDumpRecordLen(XLogReaderState *record, uint32 *rec_len, uint32 *fpi_len)
Definition: pg_waldump.c:369