LCOV - code coverage report
Current view: top level - src/bin/pg_walsummary - pg_walsummary.c (source / functions) Hit Total Coverage
Test: PostgreSQL 18devel Lines: 63 88 71.6 %
Date: 2024-11-21 08:14:44 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             : 
      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 : }

Generated by: LCOV version 1.14