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