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

Reply via email to