00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012 #define FRONTEND 1
00013 #include "postgres.h"
00014
00015 #include <dirent.h>
00016 #include <unistd.h>
00017
00018 #include "access/xlog.h"
00019 #include "access/xlogreader.h"
00020 #include "access/transam.h"
00021 #include "common/fe_memutils.h"
00022 #include "common/relpath.h"
00023 #include "getopt_long.h"
00024 #include "rmgrdesc.h"
00025
00026
00027 static const char *progname;
00028
00029 typedef struct XLogDumpPrivate
00030 {
00031 TimeLineID timeline;
00032 char *inpath;
00033 XLogRecPtr startptr;
00034 XLogRecPtr endptr;
00035 } XLogDumpPrivate;
00036
00037 typedef struct XLogDumpConfig
00038 {
00039
00040 bool bkp_details;
00041 int stop_after_records;
00042 int already_displayed_records;
00043
00044
00045 int filter_by_rmgr;
00046 TransactionId filter_by_xid;
00047 bool filter_by_xid_enabled;
00048 } XLogDumpConfig;
00049
00050 static void
00051 fatal_error(const char *fmt,...)
00052 __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2)));
00053
00054
00055
00056
00057 static void
00058 fatal_error(const char *fmt,...)
00059 {
00060 va_list args;
00061
00062 fflush(stdout);
00063
00064 fprintf(stderr, "%s: FATAL: ", progname);
00065 va_start(args, fmt);
00066 vfprintf(stderr, fmt, args);
00067 va_end(args);
00068 fputc('\n', stderr);
00069
00070 exit(EXIT_FAILURE);
00071 }
00072
00073 static void
00074 print_rmgr_list(void)
00075 {
00076 int i;
00077
00078 for (i = 0; i < RM_MAX_ID + 1; i++)
00079 {
00080 printf("%s\n", RmgrDescTable[i].rm_name);
00081 }
00082 }
00083
00084
00085
00086
00087
00088 static bool
00089 verify_directory(const char *directory)
00090 {
00091 DIR *dir = opendir(directory);
00092 if (dir == NULL)
00093 return false;
00094 closedir(dir);
00095 return true;
00096 }
00097
00098
00099
00100
00101
00102
00103
00104 static void
00105 split_path(const char *path, char **dir, char **fname)
00106 {
00107 char *sep;
00108
00109
00110 sep = strrchr(path, '/');
00111
00112
00113 if (sep != NULL)
00114 {
00115 *dir = pg_strdup(path);
00116 (*dir)[(sep - path) + 1] = '\0';
00117 *fname = pg_strdup(sep + 1);
00118 }
00119
00120 else
00121 {
00122 *dir = NULL;
00123 *fname = pg_strdup(path);
00124 }
00125 }
00126
00127
00128
00129
00130
00131
00132
00133
00134
00135
00136
00137
00138
00139 static int
00140 fuzzy_open_file(const char *directory, const char *fname)
00141 {
00142 int fd = -1;
00143 char fpath[MAXPGPATH];
00144
00145 if (directory == NULL)
00146 {
00147 const char *datadir;
00148
00149
00150 fd = open(fname, O_RDONLY | PG_BINARY, 0);
00151 if (fd < 0 && errno != ENOENT)
00152 return -1;
00153 else if (fd > 0)
00154 return fd;
00155
00156
00157 snprintf(fpath, MAXPGPATH, "%s/%s",
00158 XLOGDIR, fname);
00159 fd = open(fpath, O_RDONLY | PG_BINARY, 0);
00160 if (fd < 0 && errno != ENOENT)
00161 return -1;
00162 else if (fd > 0)
00163 return fd;
00164
00165 datadir = getenv("PGDATA");
00166
00167 if (datadir != NULL)
00168 {
00169 snprintf(fpath, MAXPGPATH, "%s/%s/%s",
00170 datadir, XLOGDIR, fname);
00171 fd = open(fpath, O_RDONLY | PG_BINARY, 0);
00172 if (fd < 0 && errno != ENOENT)
00173 return -1;
00174 else if (fd > 0)
00175 return fd;
00176 }
00177 }
00178 else
00179 {
00180
00181 snprintf(fpath, MAXPGPATH, "%s/%s",
00182 directory, fname);
00183 fd = open(fpath, O_RDONLY | PG_BINARY, 0);
00184 if (fd < 0 && errno != ENOENT)
00185 return -1;
00186 else if (fd > 0)
00187 return fd;
00188
00189
00190 snprintf(fpath, MAXPGPATH, "%s/%s/%s",
00191 directory, XLOGDIR, fname);
00192 fd = open(fpath, O_RDONLY | PG_BINARY, 0);
00193 if (fd < 0 && errno != ENOENT)
00194 return -1;
00195 else if (fd > 0)
00196 return fd;
00197 }
00198 return -1;
00199 }
00200
00201
00202
00203
00204
00205
00206 static void
00207 XLogDumpXLogRead(const char *directory, TimeLineID timeline_id,
00208 XLogRecPtr startptr, char *buf, Size count)
00209 {
00210 char *p;
00211 XLogRecPtr recptr;
00212 Size nbytes;
00213
00214 static int sendFile = -1;
00215 static XLogSegNo sendSegNo = 0;
00216 static uint32 sendOff = 0;
00217
00218 p = buf;
00219 recptr = startptr;
00220 nbytes = count;
00221
00222 while (nbytes > 0)
00223 {
00224 uint32 startoff;
00225 int segbytes;
00226 int readbytes;
00227
00228 startoff = recptr % XLogSegSize;
00229
00230 if (sendFile < 0 || !XLByteInSeg(recptr, sendSegNo))
00231 {
00232 char fname[MAXFNAMELEN];
00233
00234
00235 if (sendFile >= 0)
00236 close(sendFile);
00237
00238 XLByteToSeg(recptr, sendSegNo);
00239
00240 XLogFileName(fname, timeline_id, sendSegNo);
00241
00242 sendFile = fuzzy_open_file(directory, fname);
00243
00244 if (sendFile < 0)
00245 fatal_error("could not find file \"%s\": %s",
00246 fname, strerror(errno));
00247 sendOff = 0;
00248 }
00249
00250
00251 if (sendOff != startoff)
00252 {
00253 if (lseek(sendFile, (off_t) startoff, SEEK_SET) < 0)
00254 {
00255 int err = errno;
00256 char fname[MAXPGPATH];
00257
00258 XLogFileName(fname, timeline_id, sendSegNo);
00259
00260 fatal_error("could not seek in log segment %s to offset %u: %s",
00261 fname, startoff, strerror(err));
00262 }
00263 sendOff = startoff;
00264 }
00265
00266
00267 if (nbytes > (XLogSegSize - startoff))
00268 segbytes = XLogSegSize - startoff;
00269 else
00270 segbytes = nbytes;
00271
00272 readbytes = read(sendFile, p, segbytes);
00273 if (readbytes <= 0)
00274 {
00275 int err = errno;
00276 char fname[MAXPGPATH];
00277
00278 XLogFileName(fname, timeline_id, sendSegNo);
00279
00280 fatal_error("could not read from log segment %s, offset %d, length %d: %s",
00281 fname, sendOff, segbytes, strerror(err));
00282 }
00283
00284
00285 recptr += readbytes;
00286
00287 sendOff += readbytes;
00288 nbytes -= readbytes;
00289 p += readbytes;
00290 }
00291 }
00292
00293
00294
00295
00296 static int
00297 XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen,
00298 XLogRecPtr targetPtr, char *readBuff, TimeLineID *curFileTLI)
00299 {
00300 XLogDumpPrivate *private = state->private_data;
00301 int count = XLOG_BLCKSZ;
00302
00303 if (private->endptr != InvalidXLogRecPtr)
00304 {
00305 if (targetPagePtr + XLOG_BLCKSZ <= private->endptr)
00306 count = XLOG_BLCKSZ;
00307 else if (targetPagePtr + reqLen <= private->endptr)
00308 count = private->endptr - targetPagePtr;
00309 else
00310 return -1;
00311 }
00312
00313 XLogDumpXLogRead(private->inpath, private->timeline, targetPagePtr,
00314 readBuff, count);
00315
00316 return count;
00317 }
00318
00319
00320
00321
00322 static void
00323 XLogDumpDisplayRecord(XLogDumpConfig *config, XLogRecPtr ReadRecPtr, XLogRecord *record)
00324 {
00325 const RmgrDescData *desc = &RmgrDescTable[record->xl_rmid];
00326
00327 if (config->filter_by_rmgr != -1 &&
00328 config->filter_by_rmgr != record->xl_rmid)
00329 return;
00330
00331 if (config->filter_by_xid_enabled &&
00332 config->filter_by_xid != record->xl_xid)
00333 return;
00334
00335 config->already_displayed_records++;
00336
00337 printf("rmgr: %-11s len (rec/tot): %6u/%6u, tx: %10u, lsn: %X/%08X, prev %X/%08X, bkp: %u%u%u%u, desc: ",
00338 desc->rm_name,
00339 record->xl_len, record->xl_tot_len,
00340 record->xl_xid,
00341 (uint32) (ReadRecPtr >> 32), (uint32) ReadRecPtr,
00342 (uint32) (record->xl_prev >> 32), (uint32) record->xl_prev,
00343 !!(XLR_BKP_BLOCK(0) & record->xl_info),
00344 !!(XLR_BKP_BLOCK(1) & record->xl_info),
00345 !!(XLR_BKP_BLOCK(2) & record->xl_info),
00346 !!(XLR_BKP_BLOCK(3) & record->xl_info));
00347
00348
00349 desc->rm_desc(NULL, record->xl_info, XLogRecGetData(record));
00350
00351 putchar('\n');
00352
00353 if (config->bkp_details)
00354 {
00355 int bkpnum;
00356 char *blk = (char *) XLogRecGetData(record) + record->xl_len;
00357
00358 for (bkpnum = 0; bkpnum < XLR_MAX_BKP_BLOCKS; bkpnum++)
00359 {
00360 BkpBlock bkpb;
00361
00362 if (!(XLR_BKP_BLOCK(bkpnum) & record->xl_info))
00363 continue;
00364
00365 memcpy(&bkpb, blk, sizeof(BkpBlock));
00366 blk += sizeof(BkpBlock);
00367 blk += BLCKSZ - bkpb.hole_length;
00368
00369 printf("\tbackup bkp #%u; rel %u/%u/%u; fork: %s; block: %u; hole: offset: %u, length: %u\n",
00370 bkpnum,
00371 bkpb.node.spcNode, bkpb.node.dbNode, bkpb.node.relNode,
00372 forkNames[bkpb.fork],
00373 bkpb.block, bkpb.hole_offset, bkpb.hole_length);
00374 }
00375 }
00376 }
00377
00378 static void
00379 usage(void)
00380 {
00381 printf("%s decodes and displays PostgreSQL transaction logs for debugging.\n\n",
00382 progname);
00383 printf("Usage:\n");
00384 printf(" %s [OPTION] [STARTSEG [ENDSEG]] \n", progname);
00385 printf("\nGeneral options:\n");
00386 printf(" -V, --version output version information, then exit\n");
00387 printf(" -?, --help show this help, then exit\n");
00388 printf("\nContent options:\n");
00389 printf(" -b, --bkp-details output detailed information about backup blocks\n");
00390 printf(" -e, --end=RECPTR stop reading at log position RECPTR\n");
00391 printf(" -n, --limit=N number of records to display\n");
00392 printf(" -p, --path=PATH directory in which to find log segment files\n");
00393 printf(" (default: ./pg_xlog)\n");
00394 printf(" -r, --rmgr=RMGR only show records generated by resource manager RMGR\n");
00395 printf(" use --rmgr=list to list valid resource manager names\n");
00396 printf(" -s, --start=RECPTR start reading at log position RECPTR\n");
00397 printf(" -t, --timeline=TLI timeline from which to read log records\n");
00398 printf(" (default: 1 or the value used in STARTSEG)\n");
00399 printf(" -x, --xid=XID only show records with TransactionId XID\n");
00400 }
00401
00402 int
00403 main(int argc, char **argv)
00404 {
00405 uint32 xlogid;
00406 uint32 xrecoff;
00407 XLogReaderState *xlogreader_state;
00408 XLogDumpPrivate private;
00409 XLogDumpConfig config;
00410 XLogRecord *record;
00411 XLogRecPtr first_record;
00412 char *errormsg;
00413
00414 static struct option long_options[] = {
00415 {"bkp-details", no_argument, NULL, 'b'},
00416 {"end", required_argument, NULL, 'e'},
00417 {"help", no_argument, NULL, '?'},
00418 {"limit", required_argument, NULL, 'n'},
00419 {"path", required_argument, NULL, 'p'},
00420 {"rmgr", required_argument, NULL, 'r'},
00421 {"start", required_argument, NULL, 's'},
00422 {"timeline", required_argument, NULL, 't'},
00423 {"xid", required_argument, NULL, 'x'},
00424 {"version", no_argument, NULL, 'V'},
00425 {NULL, 0, NULL, 0}
00426 };
00427
00428 int option;
00429 int optindex = 0;
00430
00431 progname = get_progname(argv[0]);
00432
00433 memset(&private, 0, sizeof(XLogDumpPrivate));
00434 memset(&config, 0, sizeof(XLogDumpConfig));
00435
00436 private.timeline = 1;
00437 private.startptr = InvalidXLogRecPtr;
00438 private.endptr = InvalidXLogRecPtr;
00439
00440 config.bkp_details = false;
00441 config.stop_after_records = -1;
00442 config.already_displayed_records = 0;
00443 config.filter_by_rmgr = -1;
00444 config.filter_by_xid = InvalidTransactionId;
00445 config.filter_by_xid_enabled = false;
00446
00447 if (argc <= 1)
00448 {
00449 fprintf(stderr, "%s: no arguments specified\n", progname);
00450 goto bad_argument;
00451 }
00452
00453 while ((option = getopt_long(argc, argv, "be:?n:p:r:s:t:Vx:",
00454 long_options, &optindex)) != -1)
00455 {
00456 switch (option)
00457 {
00458 case 'b':
00459 config.bkp_details = true;
00460 break;
00461 case 'e':
00462 if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
00463 {
00464 fprintf(stderr, "%s: could not parse end log position \"%s\"\n",
00465 progname, optarg);
00466 goto bad_argument;
00467 }
00468 private.endptr = (uint64) xlogid << 32 | xrecoff;
00469 break;
00470 case '?':
00471 usage();
00472 exit(EXIT_SUCCESS);
00473 break;
00474 case 'n':
00475 if (sscanf(optarg, "%d", &config.stop_after_records) != 1)
00476 {
00477 fprintf(stderr, "%s: could not parse limit \"%s\"\n",
00478 progname, optarg);
00479 goto bad_argument;
00480 }
00481 break;
00482 case 'p':
00483 private.inpath = pg_strdup(optarg);
00484 break;
00485 case 'r':
00486 {
00487 int i;
00488
00489 if (pg_strcasecmp(optarg, "list") == 0)
00490 {
00491 print_rmgr_list();
00492 exit(EXIT_SUCCESS);
00493 }
00494
00495 for (i = 0; i < RM_MAX_ID; i++)
00496 {
00497 if (pg_strcasecmp(optarg, RmgrDescTable[i].rm_name) == 0)
00498 {
00499 config.filter_by_rmgr = i;
00500 break;
00501 }
00502 }
00503
00504 if (config.filter_by_rmgr == -1)
00505 {
00506 fprintf(stderr, "%s: resource manager \"%s\" does not exist\n",
00507 progname, optarg);
00508 goto bad_argument;
00509 }
00510 }
00511 break;
00512 case 's':
00513 if (sscanf(optarg, "%X/%X", &xlogid, &xrecoff) != 2)
00514 {
00515 fprintf(stderr, "%s: could not parse start log position \"%s\"\n",
00516 progname, optarg);
00517 goto bad_argument;
00518 }
00519 else
00520 private.startptr = (uint64) xlogid << 32 | xrecoff;
00521 break;
00522 case 't':
00523 if (sscanf(optarg, "%d", &private.timeline) != 1)
00524 {
00525 fprintf(stderr, "%s: could not parse timeline \"%s\"\n",
00526 progname, optarg);
00527 goto bad_argument;
00528 }
00529 break;
00530 case 'V':
00531 puts("pg_xlogdump (PostgreSQL) " PG_VERSION);
00532 exit(EXIT_SUCCESS);
00533 break;
00534 case 'x':
00535 if (sscanf(optarg, "%u", &config.filter_by_xid) != 1)
00536 {
00537 fprintf(stderr, "%s: could not parse \"%s\" as a valid xid\n",
00538 progname, optarg);
00539 goto bad_argument;
00540 }
00541 config.filter_by_xid_enabled = true;
00542 break;
00543 default:
00544 goto bad_argument;
00545 }
00546 }
00547
00548 if ((optind + 2) < argc)
00549 {
00550 fprintf(stderr,
00551 "%s: too many command-line arguments (first is \"%s\")\n",
00552 progname, argv[optind + 2]);
00553 goto bad_argument;
00554 }
00555
00556 if (private.inpath != NULL)
00557 {
00558
00559 if (!verify_directory(private.inpath))
00560 {
00561 fprintf(stderr,
00562 "%s: path \"%s\" cannot be opened: %s",
00563 progname, private.inpath, strerror(errno));
00564 goto bad_argument;
00565 }
00566 }
00567
00568
00569 if (optind < argc)
00570 {
00571 char *directory = NULL;
00572 char *fname = NULL;
00573 int fd;
00574 XLogSegNo segno;
00575
00576 split_path(argv[optind], &directory, &fname);
00577
00578 if (private.inpath == NULL && directory != NULL)
00579 {
00580 private.inpath = directory;
00581
00582 if (!verify_directory(private.inpath))
00583 fatal_error("cannot open directory \"%s\": %s",
00584 private.inpath, strerror(errno));
00585 }
00586
00587 fd = fuzzy_open_file(private.inpath, fname);
00588 if (fd < 0)
00589 fatal_error("could not open file \"%s\"", fname);
00590 close(fd);
00591
00592
00593 XLogFromFileName(fname, &private.timeline, &segno);
00594
00595 if (XLogRecPtrIsInvalid(private.startptr))
00596 XLogSegNoOffsetToRecPtr(segno, 0, private.startptr);
00597 else if (!XLByteInSeg(private.startptr, segno))
00598 {
00599 fprintf(stderr,
00600 "%s: start log position %X/%X is not inside file \"%s\"\n",
00601 progname,
00602 (uint32) (private.startptr >> 32),
00603 (uint32) private.startptr,
00604 fname);
00605 goto bad_argument;
00606 }
00607
00608
00609 if (!(optind + 1 < argc) && XLogRecPtrIsInvalid(private.endptr))
00610 XLogSegNoOffsetToRecPtr(segno + 1, 0, private.endptr);
00611
00612
00613 if (optind + 1 < argc)
00614 {
00615 XLogSegNo endsegno;
00616
00617
00618 split_path(argv[optind + 1], &directory, &fname);
00619
00620 fd = fuzzy_open_file(private.inpath, fname);
00621 if (fd < 0)
00622 fatal_error("could not open file \"%s\"", fname);
00623 close(fd);
00624
00625
00626 XLogFromFileName(fname, &private.timeline, &endsegno);
00627
00628 if (endsegno < segno)
00629 fatal_error("ENDSEG %s is before STARTSEG %s",
00630 argv[optind + 1], argv[optind]);
00631
00632 if (XLogRecPtrIsInvalid(private.endptr))
00633 XLogSegNoOffsetToRecPtr(endsegno + 1, 0, private.endptr);
00634
00635
00636 segno = endsegno;
00637 }
00638
00639
00640 if (!XLByteInSeg(private.endptr, segno) &&
00641 private.endptr != (segno + 1) * XLogSegSize)
00642 {
00643 fprintf(stderr,
00644 "%s: end log position %X/%X is not inside file \"%s\"\n",
00645 progname,
00646 (uint32) (private.endptr >> 32),
00647 (uint32) private.endptr,
00648 argv[argc - 1]);
00649 goto bad_argument;
00650 }
00651 }
00652
00653
00654 if (XLogRecPtrIsInvalid(private.startptr))
00655 {
00656 fprintf(stderr, "%s: no start log position given in range mode.\n", progname);
00657 goto bad_argument;
00658 }
00659
00660
00661
00662
00663 xlogreader_state = XLogReaderAllocate(XLogDumpReadPage, &private);
00664 if (!xlogreader_state)
00665 fatal_error("out of memory");
00666
00667
00668 first_record = XLogFindNextRecord(xlogreader_state, private.startptr);
00669
00670 if (first_record == InvalidXLogRecPtr)
00671 fatal_error("could not find a valid record after %X/%X",
00672 (uint32) (private.startptr >> 32),
00673 (uint32) private.startptr);
00674
00675
00676
00677
00678
00679
00680 if (first_record != private.startptr && (private.startptr % XLogSegSize) != 0)
00681 printf("first record is after %X/%X, at %X/%X, skipping over %u bytes\n",
00682 (uint32) (private.startptr >> 32), (uint32) private.startptr,
00683 (uint32) (first_record >> 32), (uint32) first_record,
00684 (uint32) (first_record - private.startptr));
00685
00686 while ((record = XLogReadRecord(xlogreader_state, first_record, &errormsg)))
00687 {
00688
00689 first_record = InvalidXLogRecPtr;
00690 XLogDumpDisplayRecord(&config, xlogreader_state->ReadRecPtr, record);
00691
00692
00693 if (config.stop_after_records > 0 &&
00694 config.already_displayed_records >= config.stop_after_records)
00695 break;
00696 }
00697
00698 if (errormsg)
00699 fatal_error("error in WAL record at %X/%X: %s\n",
00700 (uint32) (xlogreader_state->ReadRecPtr >> 32),
00701 (uint32) xlogreader_state->ReadRecPtr,
00702 errormsg);
00703
00704 XLogReaderFree(xlogreader_state);
00705
00706 return EXIT_SUCCESS;
00707
00708 bad_argument:
00709 fprintf(stderr, "Try \"%s --help\" for more information.\n", progname);
00710 return EXIT_FAILURE;
00711 }