Hi. Here's a patch to make pg_xlogdump print summary statistics instead of individual records.
By default, for each rmgr it shows the number of records, the size of rmgr-specific records, the size of full-page images, and the combined size. With --stats=record it shows these figures for each rmgr/xl_info combination (omitting those with zero counts for readability). Here's an example of the output after a few pgbench runs with the default checkpoint settings. I raised wal_keep_segments, resulting in 3.5GB of WAL data in pg_xlog. As you can see in the "Total" line, 96.83% of this is full-page images. As Andres observed, this is a good demonstration of why one should not use the default checkpoint_segments in production. $ ../bin/pg_xlogdump --stats=record 000000010000000000000001 0000000100000000000000DE Type N (%) Record size (%) FPI size (%) Combined size (%) ---- - --- ----------- --- -------- --- ------------- --- XLOG/CHECKPOINT_SHUTDOWN 16 ( 0.00) 1152 ( 0.00) 0 ( 0.00) 1152 ( 0.00) XLOG/CHECKPOINT_ONLINE 80 ( 0.00) 5760 ( 0.01) 0 ( 0.00) 5760 ( 0.00) XLOG/NEXTOID 12 ( 0.00) 48 ( 0.00) 0 ( 0.00) 48 ( 0.00) Transaction/COMMIT 71 ( 0.00) 4708 ( 0.00) 0 ( 0.00) 4708 ( 0.00) Transaction/COMMIT_COMPACT 413956 ( 14.82) 4967472 ( 4.35) 0 ( 0.00) 4967472 ( 0.14) Storage/CREATE 231 ( 0.01) 3696 ( 0.00) 0 ( 0.00) 3696 ( 0.00) Storage/TRUNCATE 1 ( 0.00) 16 ( 0.00) 0 ( 0.00) 16 ( 0.00) CLOG/ZEROPAGE 13 ( 0.00) 52 ( 0.00) 0 ( 0.00) 52 ( 0.00) Database/CREATE 3 ( 0.00) 48 ( 0.00) 0 ( 0.00) 48 ( 0.00) RelMap/UPDATE 14 ( 0.00) 7336 ( 0.01) 0 ( 0.00) 7336 ( 0.00) Heap2/CLEAN 369312 ( 13.22) 10769122 ( 9.43) 2698910088 ( 77.33) 2709679210 ( 75.17) Heap2/FREEZE_PAGE 53 ( 0.00) 3276 ( 0.00) 327732 ( 0.01) 331008 ( 0.01) Heap2/VISIBLE 58160 ( 2.08) 1163200 ( 1.02) 599768 ( 0.02) 1762968 ( 0.05) Heap2/MULTI_INSERT 1 ( 0.00) 59 ( 0.00) 0 ( 0.00) 59 ( 0.00) Heap2/MULTI_INSERT+INIT 7 ( 0.00) 38874 ( 0.03) 0 ( 0.00) 38874 ( 0.00) Heap/INSERT 425472 ( 15.23) 22392664 ( 19.61) 6081712 ( 0.17) 28474376 ( 0.79) Heap/DELETE 1638 ( 0.06) 42588 ( 0.04) 19800 ( 0.00) 62388 ( 0.00) Heap/UPDATE 53912 ( 1.93) 7145531 ( 6.26) 390264760 ( 11.18) 397410291 ( 11.03) Heap/HOT_UPDATE 1185607 ( 42.43) 59538947 ( 52.13) 48724168 ( 1.40) 108263115 ( 3.00) Heap/LOCK 199320 ( 7.13) 4983000 ( 4.36) 1656812 ( 0.05) 6639812 ( 0.18) Heap/INPLACE 469 ( 0.02) 66676 ( 0.06) 558604 ( 0.02) 625280 ( 0.02) Heap/INSERT+INIT 2992 ( 0.11) 272895 ( 0.24) 0 ( 0.00) 272895 ( 0.01) Heap/UPDATE+INIT 1184 ( 0.04) 146420 ( 0.13) 6611352 ( 0.19) 6757772 ( 0.19) Btree/INSERT_LEAF 81058 ( 2.90) 2224916 ( 1.95) 336444372 ( 9.64) 338669288 ( 9.40) Btree/INSERT_UPPER 128 ( 0.00) 5272 ( 0.00) 16368 ( 0.00) 21640 ( 0.00) Btree/SPLIT_L 48 ( 0.00) 171384 ( 0.15) 46712 ( 0.00) 218096 ( 0.01) Btree/SPLIT_R 80 ( 0.00) 233736 ( 0.20) 39116 ( 0.00) 272852 ( 0.01) Btree/SPLIT_L_ROOT 7 ( 0.00) 5488 ( 0.00) 0 ( 0.00) 5488 ( 0.00) Btree/SPLIT_R_ROOT 4 ( 0.00) 2880 ( 0.00) 0 ( 0.00) 2880 ( 0.00) Btree/DELETE 3 ( 0.00) 454 ( 0.00) 0 ( 0.00) 454 ( 0.00) Btree/UNLINK_PAGE 4 ( 0.00) 176 ( 0.00) 0 ( 0.00) 176 ( 0.00) Btree/NEWROOT 33 ( 0.00) 980 ( 0.00) 0 ( 0.00) 980 ( 0.00) Btree/MARK_PAGE_HALFDEAD 4 ( 0.00) 144 ( 0.00) 0 ( 0.00) 144 ( 0.00) Btree/VACUUM 66 ( 0.00) 7890 ( 0.01) 18400 ( 0.00) 26290 ( 0.00) -------- -------- -------- -------- Total 2793959 114206860 [3.17%] 3490319764 [96.83%] 3604526624 [100%] pg_xlogdump: FATAL: error in WAL record at 0/DEA52150: record with zero length at 0/DEA521B8 (Note: the FATAL error above is just the normal end of WAL.) In each row, - Type is rmgr/record - N is the number of records of that type - % is the percentage of total records - Record size is sum(xl_len+SizeOfXLogRecord) - % is the percentage of the total record size - FPI size is the size of full-page images - % is the percentage of the total FPI size - Combined size is sum(xl_tot_len) - % is the percentage of the total combined size The last line ("Total") shows the total number of records of all types, the total record size (and what percentage that is of the total size), the total full-page image size (and ditto), and the total combined size (which is 100% by definition). The patch is quite straightforward, but became quite long due to the many switch/cases needed to name each meaningful xl_rmid/xl_info combination. I'll add it to the CF. -- Abhijit
commit 1d687aeea278e0313071c238e6c3dc04e3e8b798 Author: Abhijit Menon-Sen <a...@2ndquadrant.com> Date: Wed Jun 4 14:22:33 2014 +0530 Make 'pg_xlogdump --stats[=record]' display summary statistics diff --git a/contrib/pg_xlogdump/pg_xlogdump.c b/contrib/pg_xlogdump/pg_xlogdump.c index 824b8c3..ed9dd31 100644 --- a/contrib/pg_xlogdump/pg_xlogdump.c +++ b/contrib/pg_xlogdump/pg_xlogdump.c @@ -15,12 +15,28 @@ #include <dirent.h> #include <unistd.h> +#include "access/clog.h" +#include "access/gin_private.h" +#include "access/gist_private.h" +#include "access/heapam_xlog.h" +#include "access/heapam_xlog.h" +#include "access/multixact.h" +#include "access/nbtree.h" +#include "access/spgist_private.h" +#include "access/xact.h" #include "access/xlog.h" #include "access/xlogreader.h" #include "access/transam.h" +#include "catalog/pg_control.h" +#include "catalog/storage_xlog.h" +#include "commands/dbcommands.h" +#include "commands/sequence.h" +#include "commands/tablespace.h" #include "common/fe_memutils.h" #include "getopt_long.h" #include "rmgrdesc.h" +#include "storage/standby.h" +#include "utils/relmapper.h" static const char *progname; @@ -41,6 +57,8 @@ typedef struct XLogDumpConfig int stop_after_records; int already_displayed_records; bool follow; + bool stats; + bool stats_per_record; /* filter options */ int filter_by_rmgr; @@ -48,6 +66,20 @@ typedef struct XLogDumpConfig bool filter_by_xid_enabled; } XLogDumpConfig; +typedef struct Stats +{ + uint64 count; + uint64 rec_len; + uint64 fpi_len; +} Stats; + +typedef struct XLogDumpStats +{ + uint64 count; + Stats rmgr_stats[RM_NEXT_ID]; + Stats record_stats[RM_NEXT_ID][16]; +} XLogDumpStats; + static void fatal_error(const char *fmt,...) __attribute__((format(PG_PRINTF_ATTRIBUTE, 1, 2))); @@ -322,6 +354,50 @@ XLogDumpReadPage(XLogReaderState *state, XLogRecPtr targetPagePtr, int reqLen, } /* + * Store per-rmgr and per-record statistics for a given record. + */ +static void +XLogDumpCountRecord(XLogDumpConfig *config, XLogDumpStats *stats, XLogRecPtr ReadRecPtr, XLogRecord *record) +{ + RmgrId rmid; + uint8 recid; + + if (config->filter_by_rmgr != -1 && + config->filter_by_rmgr != record->xl_rmid) + return; + + if (config->filter_by_xid_enabled && + config->filter_by_xid != record->xl_xid) + return; + + stats->count++; + + /* Update per-rmgr statistics */ + + rmid = record->xl_rmid; + + stats->rmgr_stats[rmid].count++; + stats->rmgr_stats[rmid].rec_len += + record->xl_len + SizeOfXLogRecord; + stats->rmgr_stats[rmid].fpi_len += + record->xl_tot_len - (record->xl_len + SizeOfXLogRecord); + + /* + * Update per-record statistics, where the record is identified by a + * combination of the RmgrId and the upper four bits of the xl_info + * field (to give sixteen possible entries per RmgrId). + */ + + recid = (record->xl_info & ~XLR_INFO_MASK) >> 4; + + stats->record_stats[rmid][recid].count++; + stats->record_stats[rmid][recid].rec_len += + record->xl_len + SizeOfXLogRecord; + stats->record_stats[rmid][recid].fpi_len += + record->xl_tot_len - (record->xl_len + SizeOfXLogRecord); +} + +/* * Print a record to stdout */ static void @@ -380,6 +456,498 @@ XLogDumpDisplayRecord(XLogDumpConfig *config, XLogRecPtr ReadRecPtr, XLogRecord } } +/* + * Given an xl_rmid and the high bits of xl_info, returns a string + * describing the record type. + */ +static const char * +identify_record(RmgrId rmid, uint8 info) +{ + const RmgrDescData *desc = &RmgrDescTable[rmid]; + const char *rec; + + rec = psprintf("0x%x", info); + + switch (rmid) + { + case RM_XLOG_ID: + switch (info) + { + case XLOG_CHECKPOINT_SHUTDOWN: + rec = "CHECKPOINT_SHUTDOWN"; + break; + case XLOG_CHECKPOINT_ONLINE: + rec = "CHECKPOINT_ONLINE"; + break; + case XLOG_NOOP: + rec = "NOOP"; + break; + case XLOG_NEXTOID: + rec = "NEXTOID"; + break; + case XLOG_SWITCH: + rec = "SWITCH"; + break; + case XLOG_BACKUP_END: + rec = "BACKUP_END"; + break; + case XLOG_PARAMETER_CHANGE: + rec = "PARAMETER_CHANGE"; + break; + case XLOG_RESTORE_POINT: + rec = "RESTORE_POINT"; + break; + case XLOG_FPW_CHANGE: + rec = "FPW_CHANGE"; + break; + case XLOG_END_OF_RECOVERY: + rec = "END_OF_RECOVERY"; + break; + case XLOG_FPI: + rec = "FPI"; + break; + } + break; + + case RM_XACT_ID: + switch (info) + { + case XLOG_XACT_COMMIT: + rec = "COMMIT"; + break; + case XLOG_XACT_PREPARE: + rec = "PREPARE"; + break; + case XLOG_XACT_ABORT: + rec = "ABORT"; + break; + case XLOG_XACT_COMMIT_PREPARED: + rec = "COMMIT_PREPARED"; + break; + case XLOG_XACT_ABORT_PREPARED: + rec = "ABORT_PREPARED"; + break; + case XLOG_XACT_ASSIGNMENT: + rec = "ASSIGNMENT"; + break; + case XLOG_XACT_COMMIT_COMPACT: + rec = "COMMIT_COMPACT"; + break; + } + break; + + case RM_SMGR_ID: + switch (info) + { + case XLOG_SMGR_CREATE: + rec = "CREATE"; + break; + case XLOG_SMGR_TRUNCATE: + rec = "TRUNCATE"; + break; + } + break; + + case RM_CLOG_ID: + switch (info) + { + case CLOG_ZEROPAGE: + rec = "ZEROPAGE"; + break; + case CLOG_TRUNCATE: + rec = "TRUNCATE"; + break; + } + break; + + case RM_DBASE_ID: + switch (info) + { + case XLOG_DBASE_CREATE: + rec = "CREATE"; + break; + case XLOG_DBASE_DROP: + rec = "DROP"; + break; + } + break; + + case RM_TBLSPC_ID: + switch (info) + { + case XLOG_TBLSPC_CREATE: + rec = "CREATE"; + break; + case XLOG_TBLSPC_DROP: + rec = "DROP"; + break; + } + break; + + case RM_MULTIXACT_ID: + switch (info) + { + case XLOG_MULTIXACT_ZERO_OFF_PAGE: + rec = "ZERO_OFF_PAGE"; + break; + case XLOG_MULTIXACT_ZERO_MEM_PAGE: + rec = "ZERO_MEM_PAGE"; + break; + case XLOG_MULTIXACT_CREATE_ID: + rec = "CREATE_ID"; + break; + } + break; + + case RM_RELMAP_ID: + switch (info) + { + case XLOG_RELMAP_UPDATE: + rec = "UPDATE"; + break; + } + break; + + case RM_STANDBY_ID: + switch (info) + { + case XLOG_STANDBY_LOCK: + rec = "LOCK"; + break; + case XLOG_RUNNING_XACTS: + rec = "RUNNING_XACTS"; + break; + } + break; + + case RM_HEAP2_ID: + switch (info & XLOG_HEAP_OPMASK) + { + case XLOG_HEAP2_CLEAN: + rec = "CLEAN"; + break; + case XLOG_HEAP2_FREEZE_PAGE: + rec = "FREEZE_PAGE"; + break; + case XLOG_HEAP2_CLEANUP_INFO: + rec = "CLEANUP_INFO"; + break; + case XLOG_HEAP2_VISIBLE: + rec = "VISIBLE"; + break; + case XLOG_HEAP2_MULTI_INSERT: + rec = "MULTI_INSERT"; + break; + case XLOG_HEAP2_LOCK_UPDATED: + rec = "LOCK_UPDATED"; + break; + case XLOG_HEAP2_NEW_CID: + rec = "NEW_CID"; + break; + case XLOG_HEAP2_REWRITE: + rec = "REWRITE"; + break; + } + + if (info & XLOG_HEAP_INIT_PAGE) + rec = psprintf("%s+INIT", rec); + + break; + + case RM_HEAP_ID: + switch (info & XLOG_HEAP_OPMASK) + { + case XLOG_HEAP_INSERT: + rec = "INSERT"; + break; + case XLOG_HEAP_DELETE: + rec = "DELETE"; + break; + case XLOG_HEAP_UPDATE: + rec = "UPDATE"; + break; + case XLOG_HEAP_HOT_UPDATE: + rec = "HOT_UPDATE"; + break; + case XLOG_HEAP_NEWPAGE: + rec = "NEWPAGE"; + break; + case XLOG_HEAP_LOCK: + rec = "LOCK"; + break; + case XLOG_HEAP_INPLACE: + rec = "INPLACE"; + break; + } + + if (info & XLOG_HEAP_INIT_PAGE) + rec = psprintf("%s+INIT", rec); + + break; + + case RM_BTREE_ID: + switch (info) + { + case XLOG_BTREE_INSERT_LEAF: + rec = "INSERT_LEAF"; + break; + case XLOG_BTREE_INSERT_UPPER: + rec = "INSERT_UPPER"; + break; + case XLOG_BTREE_INSERT_META: + rec = "INSERT_META"; + break; + case XLOG_BTREE_SPLIT_L: + rec = "SPLIT_L"; + break; + case XLOG_BTREE_SPLIT_R: + rec = "SPLIT_R"; + break; + case XLOG_BTREE_SPLIT_L_ROOT: + rec = "SPLIT_L_ROOT"; + break; + case XLOG_BTREE_SPLIT_R_ROOT: + rec = "SPLIT_R_ROOT"; + break; + case XLOG_BTREE_VACUUM: + rec = "VACUUM"; + break; + case XLOG_BTREE_DELETE: + rec = "DELETE"; + break; + case XLOG_BTREE_MARK_PAGE_HALFDEAD: + rec = "MARK_PAGE_HALFDEAD"; + break; + case XLOG_BTREE_UNLINK_PAGE: + rec = "UNLINK_PAGE"; + break; + case XLOG_BTREE_UNLINK_PAGE_META: + rec = "UNLINK_PAGE_META"; + break; + case XLOG_BTREE_NEWROOT: + rec = "NEWROOT"; + break; + case XLOG_BTREE_REUSE_PAGE: + rec = "REUSE_PAGE"; + break; + } + break; + + case RM_HASH_ID: + break; + + case RM_GIN_ID: + switch (info) + { + case XLOG_GIN_CREATE_INDEX: + rec = "CREATE_INDEX"; + break; + case XLOG_GIN_CREATE_PTREE: + rec = "CREATE_PTREE"; + break; + case XLOG_GIN_INSERT: + rec = "INSERT"; + break; + case XLOG_GIN_SPLIT: + rec = "SPLIT"; + break; + case XLOG_GIN_VACUUM_PAGE: + rec = "VACUUM_PAGE"; + break; + case XLOG_GIN_VACUUM_DATA_LEAF_PAGE: + rec = "VACUUM_DATA_LEAF_PAGE"; + break; + case XLOG_GIN_DELETE_PAGE: + rec = "DELETE_PAGE"; + break; + case XLOG_GIN_UPDATE_META_PAGE: + rec = "UPDATE_META_PAGE"; + break; + case XLOG_GIN_INSERT_LISTPAGE: + rec = "INSERT_LISTPAGE"; + break; + case XLOG_GIN_DELETE_LISTPAGE: + rec = "DELETE_LISTPAGE"; + break; + } + break; + + case RM_GIST_ID: + switch (info) + { + case XLOG_GIST_PAGE_UPDATE: + rec = "PAGE_UPDATE"; + break; + case XLOG_GIST_PAGE_SPLIT: + rec = "PAGE_SPLIT"; + break; + case XLOG_GIST_CREATE_INDEX: + rec = "CREATE_INDEX"; + break; + } + break; + + case RM_SEQ_ID: + switch (info) + { + case XLOG_SEQ_LOG: + rec = "LOG"; + break; + } + break; + + case RM_SPGIST_ID: + switch (info) + { + case XLOG_SPGIST_CREATE_INDEX: + rec = "CREATE_INDEX"; + break; + case XLOG_SPGIST_ADD_LEAF: + rec = "ADD_LEAF"; + break; + case XLOG_SPGIST_MOVE_LEAFS: + rec = "MOVE_LEAFS"; + break; + case XLOG_SPGIST_ADD_NODE: + rec = "ADD_NODE"; + break; + case XLOG_SPGIST_SPLIT_TUPLE: + rec = "SPLIT_TUPLE"; + break; + case XLOG_SPGIST_PICKSPLIT: + rec = "PICKSPLIT"; + break; + case XLOG_SPGIST_VACUUM_LEAF: + rec = "VACUUM_LEAF"; + break; + case XLOG_SPGIST_VACUUM_ROOT: + rec = "VACUUM_ROOT"; + break; + case XLOG_SPGIST_VACUUM_REDIRECT: + rec = "VACUUM_REDIRECT"; + break; + } + break; + + default: + break; + } + + return psprintf("%s/%s", desc->rm_name, rec); +} + +/* + * Display a single row of record counts and sizes for an rmgr or record. + */ +static void +XLogDumpStatsRow(const char *name, + uint64 n, double n_pct, + uint64 rec_len, double rec_len_pct, + uint64 fpi_len, double fpi_len_pct, + uint64 total_len, double total_len_pct) +{ + printf("%-27s %20llu (%6.02f) %20llu (%6.02f) %20llu (%6.02f) %20llu (%6.02f)\n", + name, n, n_pct, rec_len, rec_len_pct, fpi_len, fpi_len_pct, + total_len, total_len_pct); +} + + +/* + * Display summary statistics about the records seen so far. + */ +static void +XLogDumpDisplayStats(XLogDumpConfig *config, XLogDumpStats *stats) +{ + int ri, rj; + uint64 total_count = 0; + uint64 total_rec_len = 0; + uint64 total_fpi_len = 0; + uint64 total_len = 0; + + /* + * Make a first pass to calculate column totals: + * count(*), + * sum(xl_len+SizeOfXLogRecord), + * sum(xl_tot_len-xl_len-SizeOfXLogRecord), and + * sum(xl_tot_len). + * These are used to calculate percentages for each record type. + */ + + for (ri = 0; ri < RM_NEXT_ID; ri++) + { + total_count += stats->rmgr_stats[ri].count; + total_rec_len += stats->rmgr_stats[ri].rec_len; + total_fpi_len += stats->rmgr_stats[ri].fpi_len; + } + total_len = total_rec_len+total_fpi_len; + + /* + * 27 is strlen("Transaction/COMMIT_PREPARED"), + * 20 is strlen(2^64), 8 is strlen("(100.00%)") + */ + + printf("%-27s %20s %8s %20s %8s %20s %8s %20s %8s\n" + "%-27s %20s %8s %20s %8s %20s %8s %20s %8s\n", + "Type", "N", "(%)", "Record size", "(%)", "FPI size", "(%)", "Combined size", "(%)", + "----", "-", "---", "-----------", "---", "--------", "---", "-------------", "---"); + + for (ri = 0; ri < RM_NEXT_ID; ri++) + { + uint64 count, rec_len, fpi_len; + + if (!config->stats_per_record) + { + const RmgrDescData *desc = &RmgrDescTable[ri]; + + count = stats->rmgr_stats[ri].count; + rec_len = stats->rmgr_stats[ri].rec_len; + fpi_len = stats->rmgr_stats[ri].fpi_len; + + XLogDumpStatsRow(desc->rm_name, + count, 100*(double)count/total_count, + rec_len, 100*(double)rec_len/total_rec_len, + fpi_len, 100*(double)fpi_len/total_fpi_len, + rec_len+fpi_len, 100*(double)(rec_len+fpi_len)/total_len); + } + else + { + for (rj = 0; rj < 16; rj++) + { + count = stats->record_stats[ri][rj].count; + rec_len = stats->record_stats[ri][rj].rec_len; + fpi_len = stats->record_stats[ri][rj].fpi_len; + + /* Skip undefined combinations and ones that didn't occur */ + if (count == 0) + continue; + + XLogDumpStatsRow(identify_record(ri, rj << 4), + count, 100*(double)count/total_count, + rec_len, 100*(double)rec_len/total_rec_len, + fpi_len, 100*(double)fpi_len/total_fpi_len, + rec_len+fpi_len, 100*(double)(rec_len+fpi_len)/total_len); + } + } + } + + printf("%-27s %20s %8s %20s %8s %20s %8s %20s\n", + "", "--------", "", "--------", "", "--------", "", "--------"); + + /* + * The percentages in earlier rows were calculated against the + * column total, but the ones that follow are against the row total. + * Note that these are displayed with a % symbol to differentiate + * them from the earlier ones, and are thus up to 9 characters long. + */ + + printf("%-27s %20llu %-9s%20llu %-9s%20llu %-9s%20llu %-6s\n", + "Total", + stats->count, "", + total_rec_len, psprintf("[%.02f%%]", 100*(double)total_rec_len/total_len), + total_fpi_len, psprintf("[%.02f%%]", 100*(double)total_fpi_len/total_len), + total_len, "[100%]"); +} + static void usage(void) { @@ -401,6 +969,8 @@ usage(void) printf(" (default: 1 or the value used in STARTSEG)\n"); printf(" -V, --version output version information, then exit\n"); printf(" -x, --xid=XID only show records with TransactionId XID\n"); + printf(" -z, --stats[=record] show statistics instead of records\n"); + printf(" (optionally, show per-record statistics)\n"); printf(" -?, --help show this help, then exit\n"); } @@ -412,6 +982,7 @@ main(int argc, char **argv) XLogReaderState *xlogreader_state; XLogDumpPrivate private; XLogDumpConfig config; + XLogDumpStats stats; XLogRecord *record; XLogRecPtr first_record; char *errormsg; @@ -428,6 +999,7 @@ main(int argc, char **argv) {"timeline", required_argument, NULL, 't'}, {"xid", required_argument, NULL, 'x'}, {"version", no_argument, NULL, 'V'}, + {"stats", optional_argument, NULL, 'z'}, {NULL, 0, NULL, 0} }; @@ -438,6 +1010,7 @@ main(int argc, char **argv) memset(&private, 0, sizeof(XLogDumpPrivate)); memset(&config, 0, sizeof(XLogDumpConfig)); + memset(&stats, 0, sizeof(XLogDumpStats)); private.timeline = 1; private.startptr = InvalidXLogRecPtr; @@ -451,6 +1024,8 @@ main(int argc, char **argv) config.filter_by_rmgr = -1; config.filter_by_xid = InvalidTransactionId; config.filter_by_xid_enabled = false; + config.stats = false; + config.stats_per_record = false; if (argc <= 1) { @@ -458,7 +1033,7 @@ main(int argc, char **argv) goto bad_argument; } - while ((option = getopt_long(argc, argv, "be:?fn:p:r:s:t:Vx:", + while ((option = getopt_long(argc, argv, "be:?fn:p:r:s:t:Vx:z::", long_options, &optindex)) != -1) { switch (option) @@ -551,6 +1126,20 @@ main(int argc, char **argv) } config.filter_by_xid_enabled = true; break; + case 'z': + config.stats = true; + if (optarg) + { + if (strcmp(optarg, "record") == 0) + config.stats_per_record = true; + else + { + fprintf(stderr, "%s: unrecognised argument to --stats: %s\n", + progname, optarg); + goto bad_argument; + } + } + break; default: goto bad_argument; } @@ -711,7 +1300,10 @@ main(int argc, char **argv) /* after reading the first record, continue at next one */ first_record = InvalidXLogRecPtr; - XLogDumpDisplayRecord(&config, xlogreader_state->ReadRecPtr, record); + if (config.stats == true) + XLogDumpCountRecord(&config, &stats, xlogreader_state->ReadRecPtr, record); + else + XLogDumpDisplayRecord(&config, xlogreader_state->ReadRecPtr, record); /* check whether we printed enough */ if (config.stop_after_records > 0 && @@ -719,6 +1311,9 @@ main(int argc, char **argv) break; } + if (config.stats == true) + XLogDumpDisplayStats(&config, &stats); + if (errormsg) fatal_error("error in WAL record at %X/%X: %s\n", (uint32) (xlogreader_state->ReadRecPtr >> 32), diff --git a/doc/src/sgml/pg_xlogdump.sgml b/doc/src/sgml/pg_xlogdump.sgml index 1d1a2ce..d9f4a6a 100644 --- a/doc/src/sgml/pg_xlogdump.sgml +++ b/doc/src/sgml/pg_xlogdump.sgml @@ -180,6 +180,18 @@ PostgreSQL documentation </varlistentry> <varlistentry> + <term><option>-z</option></term> + <term><option>--stats[=record]</option></term> + <listitem> + <para> + Display summary statistics (number and size of records and + full-page images) instead of individual records. Optionally + generate statistics per-record instead of per-rmgr. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><option>-?</></term> <term><option>--help</></term> <listitem>
-- Sent via pgsql-hackers mailing list (pgsql-hackers@postgresql.org) To make changes to your subscription: http://www.postgresql.org/mailpref/pgsql-hackers