LCOV - code coverage report
Current view: top level - src/bin/pg_walsummary - pg_walsummary.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 63 88 71.6 %
Date: 2024-05-09 11:10:38 Functions: 5 6 83.3 %
Legend: Lines: hit not hit

          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 : }

Generated by: LCOV version 1.14