Line data Source code
1 : /*-------------------------------------------------------------------------
2 : *
3 : * pg_walsummary.c
4 : * Prints the contents of WAL summary files.
5 : *
6 : * Copyright (c) 2017-2024, PostgreSQL Global Development Group
7 : *
8 : * IDENTIFICATION
9 : * src/bin/pg_walsummary/pg_walsummary.c
10 : *
11 : *-------------------------------------------------------------------------
12 : */
13 : #include "postgres_fe.h"
14 :
15 : #include <fcntl.h>
16 : #include <limits.h>
17 :
18 : #include "common/blkreftable.h"
19 : #include "common/int.h"
20 : #include "common/logging.h"
21 : #include "fe_utils/option_utils.h"
22 : #include "getopt_long.h"
23 : #include "lib/stringinfo.h"
24 :
25 : typedef struct ws_options
26 : {
27 : bool individual;
28 : bool quiet;
29 : } ws_options;
30 :
31 : typedef struct ws_file_info
32 : {
33 : int fd;
34 : char *filename;
35 : } ws_file_info;
36 :
37 : static BlockNumber *block_buffer = NULL;
38 : static unsigned block_buffer_size = 512; /* Initial size. */
39 :
40 : static void dump_one_relation(ws_options *opt, RelFileLocator *rlocator,
41 : ForkNumber forknum, BlockNumber limit_block,
42 : BlockRefTableReader *reader);
43 : static void help(const char *progname);
44 : static int compare_block_numbers(const void *a, const void *b);
45 : static int walsummary_read_callback(void *callback_arg, void *data,
46 : int length);
47 : static void walsummary_error_callback(void *callback_arg, char *fmt,...) pg_attribute_printf(2, 3);
48 :
49 : /*
50 : * Main program.
51 : */
52 : int
53 10 : main(int argc, char *argv[])
54 : {
55 : static struct option long_options[] = {
56 : {"individual", no_argument, NULL, 'i'},
57 : {"quiet", no_argument, NULL, 'q'},
58 : {NULL, 0, NULL, 0}
59 : };
60 :
61 : const char *progname;
62 : int optindex;
63 : int c;
64 : ws_options opt;
65 :
66 10 : memset(&opt, 0, sizeof(ws_options));
67 :
68 10 : pg_logging_init(argv[0]);
69 10 : progname = get_progname(argv[0]);
70 10 : set_pglocale_pgservice(argv[0], PG_TEXTDOMAIN("pg_walsummary"));
71 10 : handle_help_version_opts(argc, argv, progname, help);
72 :
73 : /* process command-line options */
74 8 : while ((c = getopt_long(argc, argv, "f:iqw:",
75 : long_options, &optindex)) != -1)
76 : {
77 4 : switch (c)
78 : {
79 2 : case 'i':
80 2 : opt.individual = true;
81 2 : break;
82 0 : case 'q':
83 0 : opt.quiet = true;
84 0 : break;
85 2 : default:
86 : /* getopt_long already emitted a complaint */
87 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
88 2 : exit(1);
89 : }
90 : }
91 :
92 4 : if (optind >= argc)
93 : {
94 2 : pg_log_error("%s: no input files specified", progname);
95 2 : pg_log_error_hint("Try \"%s --help\" for more information.", progname);
96 2 : exit(1);
97 : }
98 :
99 4 : while (optind < argc)
100 : {
101 : ws_file_info ws;
102 : BlockRefTableReader *reader;
103 : RelFileLocator rlocator;
104 : ForkNumber forknum;
105 : BlockNumber limit_block;
106 :
107 2 : ws.filename = argv[optind++];
108 2 : if ((ws.fd = open(ws.filename, O_RDONLY | PG_BINARY, 0)) < 0)
109 0 : pg_fatal("could not open file \"%s\": %m", ws.filename);
110 :
111 2 : reader = CreateBlockRefTableReader(walsummary_read_callback, &ws,
112 : ws.filename,
113 : walsummary_error_callback, NULL);
114 4 : while (BlockRefTableReaderNextRelation(reader, &rlocator, &forknum,
115 : &limit_block))
116 2 : dump_one_relation(&opt, &rlocator, forknum, limit_block, reader);
117 :
118 2 : DestroyBlockRefTableReader(reader);
119 2 : close(ws.fd);
120 : }
121 :
122 2 : exit(0);
123 : }
124 :
125 : /*
126 : * Dump details for one relation.
127 : */
128 : static void
129 2 : dump_one_relation(ws_options *opt, RelFileLocator *rlocator,
130 : ForkNumber forknum, BlockNumber limit_block,
131 : BlockRefTableReader *reader)
132 : {
133 2 : unsigned i = 0;
134 : unsigned nblocks;
135 2 : BlockNumber startblock = InvalidBlockNumber;
136 2 : BlockNumber endblock = InvalidBlockNumber;
137 :
138 : /* Dump limit block, if any. */
139 2 : if (limit_block != InvalidBlockNumber)
140 0 : printf("TS %u, DB %u, REL %u, FORK %s: limit %u\n",
141 : rlocator->spcOid, rlocator->dbOid, rlocator->relNumber,
142 : forkNames[forknum], limit_block);
143 :
144 : /* If we haven't allocated a block buffer yet, do that now. */
145 2 : if (block_buffer == NULL)
146 2 : block_buffer = palloc_array(BlockNumber, block_buffer_size);
147 :
148 : /* Try to fill the block buffer. */
149 2 : nblocks = BlockRefTableReaderGetBlocks(reader,
150 : block_buffer,
151 : block_buffer_size);
152 :
153 : /* If we filled the block buffer completely, we must enlarge it. */
154 2 : while (nblocks >= block_buffer_size)
155 : {
156 : unsigned new_size;
157 :
158 : /* Double the size, being careful about overflow. */
159 0 : new_size = block_buffer_size * 2;
160 0 : if (new_size < block_buffer_size)
161 0 : new_size = PG_UINT32_MAX;
162 0 : block_buffer = repalloc_array(block_buffer, BlockNumber, new_size);
163 :
164 : /* Try to fill the newly-allocated space. */
165 0 : nblocks +=
166 0 : BlockRefTableReaderGetBlocks(reader,
167 0 : block_buffer + block_buffer_size,
168 0 : new_size - block_buffer_size);
169 :
170 : /* Save the new size for later calls. */
171 0 : block_buffer_size = new_size;
172 : }
173 :
174 : /* If we don't need to produce any output, skip the rest of this. */
175 2 : if (opt->quiet)
176 0 : return;
177 :
178 : /*
179 : * Sort the returned block numbers. If the block reference table was using
180 : * the bitmap representation for a given chunk, the block numbers in that
181 : * chunk will already be sorted, but when the array-of-offsets
182 : * representation is used, we can receive block numbers here out of order.
183 : */
184 2 : qsort(block_buffer, nblocks, sizeof(BlockNumber), compare_block_numbers);
185 :
186 : /* Dump block references. */
187 6 : while (i < nblocks)
188 : {
189 : /*
190 : * Find the next range of blocks to print, but if --individual was
191 : * specified, then consider each block a separate range.
192 : */
193 4 : startblock = endblock = block_buffer[i++];
194 4 : if (!opt->individual)
195 : {
196 0 : while (i < nblocks && block_buffer[i] == endblock + 1)
197 : {
198 0 : endblock++;
199 0 : i++;
200 : }
201 : }
202 :
203 : /* Format this range of block numbers as a string. */
204 4 : if (startblock == endblock)
205 4 : printf("TS %u, DB %u, REL %u, FORK %s: block %u\n",
206 : rlocator->spcOid, rlocator->dbOid, rlocator->relNumber,
207 : forkNames[forknum], startblock);
208 : else
209 0 : printf("TS %u, DB %u, REL %u, FORK %s: blocks %u..%u\n",
210 : rlocator->spcOid, rlocator->dbOid, rlocator->relNumber,
211 : forkNames[forknum], startblock, endblock);
212 : }
213 : }
214 :
215 : /*
216 : * Quicksort comparator for block numbers.
217 : */
218 : static int
219 2 : compare_block_numbers(const void *a, const void *b)
220 : {
221 2 : BlockNumber aa = *(BlockNumber *) a;
222 2 : BlockNumber bb = *(BlockNumber *) b;
223 :
224 2 : return pg_cmp_u32(aa, bb);
225 : }
226 :
227 : /*
228 : * Error callback.
229 : */
230 : void
231 0 : walsummary_error_callback(void *callback_arg, char *fmt,...)
232 : {
233 : va_list ap;
234 :
235 0 : va_start(ap, fmt);
236 0 : pg_log_generic_v(PG_LOG_ERROR, PG_LOG_PRIMARY, fmt, ap);
237 0 : va_end(ap);
238 :
239 0 : exit(1);
240 : }
241 :
242 : /*
243 : * Read callback.
244 : */
245 : int
246 2 : walsummary_read_callback(void *callback_arg, void *data, int length)
247 : {
248 2 : ws_file_info *ws = callback_arg;
249 : int rc;
250 :
251 2 : if ((rc = read(ws->fd, data, length)) < 0)
252 0 : pg_fatal("could not read file \"%s\": %m", ws->filename);
253 :
254 2 : return rc;
255 : }
256 :
257 : /*
258 : * help
259 : *
260 : * Prints help page for the program
261 : *
262 : * progname: the name of the executed program, such as "pg_walsummary"
263 : */
264 : static void
265 2 : help(const char *progname)
266 : {
267 2 : printf(_("%s prints the contents of a WAL summary file.\n\n"), progname);
268 2 : printf(_("Usage:\n"));
269 2 : printf(_(" %s [OPTION]... FILE...\n"), progname);
270 2 : printf(_("\nOptions:\n"));
271 2 : printf(_(" -i, --individual list block numbers individually, not as ranges\n"));
272 2 : printf(_(" -q, --quiet don't print anything, just parse the files\n"));
273 2 : printf(_(" -V, --version output version information, then exit\n"));
274 2 : printf(_(" -?, --help show this help, then exit\n"));
275 :
276 2 : printf(_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT);
277 2 : printf(_("%s home page: <%s>\n"), PACKAGE_NAME, PACKAGE_URL);
278 2 : }
|