LCOV - differential code coverage report
Current view: top level - src/backend/utils/mmgr - generation.c (source / functions) Coverage Total Hit UNC LBC UBC GNC CBC DUB DCB
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 61.1 % 288 176 32 2 78 42 134 20 54
Current Date: 2024-04-14 14:21:10 Functions: 72.2 % 18 13 2 3 7 6 1 2
Baseline: 16@8cea358b128 Branches: 32.2 % 230 74 27 3 126 15 59
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed [..60] days: 52.2 % 67 35 31 1 35
(240..) days: 63.8 % 221 141 1 2 77 7 134
Function coverage date bins:
[..60] days: 60.0 % 5 3 2 3
(240..) days: 76.9 % 13 10 3 4 6
Branch coverage date bins:
[..60] days: 31.6 % 38 12 26 12
(240..) days: 32.3 % 192 62 1 3 126 3 59

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * generation.c
                                  4                 :                :  *    Generational allocator definitions.
                                  5                 :                :  *
                                  6                 :                :  * Generation is a custom MemoryContext implementation designed for cases of
                                  7                 :                :  * chunks with similar lifespan.
                                  8                 :                :  *
                                  9                 :                :  * Portions Copyright (c) 2017-2024, PostgreSQL Global Development Group
                                 10                 :                :  *
                                 11                 :                :  * IDENTIFICATION
                                 12                 :                :  *    src/backend/utils/mmgr/generation.c
                                 13                 :                :  *
                                 14                 :                :  *
                                 15                 :                :  *  This memory context is based on the assumption that the chunks are freed
                                 16                 :                :  *  roughly in the same order as they were allocated (FIFO), or in groups with
                                 17                 :                :  *  similar lifespan (generations - hence the name of the context). This is
                                 18                 :                :  *  typical for various queue-like use cases, i.e. when tuples are constructed,
                                 19                 :                :  *  processed and then thrown away.
                                 20                 :                :  *
                                 21                 :                :  *  The memory context uses a very simple approach to free space management.
                                 22                 :                :  *  Instead of a complex global freelist, each block tracks a number
                                 23                 :                :  *  of allocated and freed chunks.  The block is classed as empty when the
                                 24                 :                :  *  number of free chunks is equal to the number of allocated chunks.  When
                                 25                 :                :  *  this occurs, instead of freeing the block, we try to "recycle" it, i.e.
                                 26                 :                :  *  reuse it for new allocations.  This is done by setting the block in the
                                 27                 :                :  *  context's 'freeblock' field.  If the freeblock field is already occupied
                                 28                 :                :  *  by another free block we simply return the newly empty block to malloc.
                                 29                 :                :  *
                                 30                 :                :  *  This approach to free blocks requires fewer malloc/free calls for truly
                                 31                 :                :  *  first allocated, first free'd allocation patterns.
                                 32                 :                :  *
                                 33                 :                :  *-------------------------------------------------------------------------
                                 34                 :                :  */
                                 35                 :                : 
                                 36                 :                : #include "postgres.h"
                                 37                 :                : 
                                 38                 :                : #include "lib/ilist.h"
                                 39                 :                : #include "port/pg_bitutils.h"
                                 40                 :                : #include "utils/memdebug.h"
                                 41                 :                : #include "utils/memutils.h"
                                 42                 :                : #include "utils/memutils_internal.h"
                                 43                 :                : #include "utils/memutils_memorychunk.h"
                                 44                 :                : 
                                 45                 :                : 
                                 46                 :                : #define Generation_BLOCKHDRSZ   MAXALIGN(sizeof(GenerationBlock))
                                 47                 :                : #define Generation_CHUNKHDRSZ   sizeof(MemoryChunk)
                                 48                 :                : 
                                 49                 :                : #define Generation_CHUNK_FRACTION   8
                                 50                 :                : 
                                 51                 :                : typedef struct GenerationBlock GenerationBlock; /* forward reference */
                                 52                 :                : 
                                 53                 :                : typedef void *GenerationPointer;
                                 54                 :                : 
                                 55                 :                : /*
                                 56                 :                :  * GenerationContext is a simple memory context not reusing allocated chunks,
                                 57                 :                :  * and freeing blocks once all chunks are freed.
                                 58                 :                :  */
                                 59                 :                : typedef struct GenerationContext
                                 60                 :                : {
                                 61                 :                :     MemoryContextData header;   /* Standard memory-context fields */
                                 62                 :                : 
                                 63                 :                :     /* Generational context parameters */
                                 64                 :                :     uint32      initBlockSize;  /* initial block size */
                                 65                 :                :     uint32      maxBlockSize;   /* maximum block size */
                                 66                 :                :     uint32      nextBlockSize;  /* next block size to allocate */
                                 67                 :                :     uint32      allocChunkLimit;    /* effective chunk size limit */
                                 68                 :                : 
                                 69                 :                :     GenerationBlock *block;     /* current (most recently allocated) block */
                                 70                 :                :     GenerationBlock *freeblock; /* pointer to an empty block that's being
                                 71                 :                :                                  * recycled, or NULL if there's no such block. */
                                 72                 :                :     dlist_head  blocks;         /* list of blocks */
                                 73                 :                : } GenerationContext;
                                 74                 :                : 
                                 75                 :                : /*
                                 76                 :                :  * GenerationBlock
                                 77                 :                :  *      GenerationBlock is the unit of memory that is obtained by generation.c
                                 78                 :                :  *      from malloc().  It contains zero or more MemoryChunks, which are the
                                 79                 :                :  *      units requested by palloc() and freed by pfree().  MemoryChunks cannot
                                 80                 :                :  *      be returned to malloc() individually, instead pfree() updates the free
                                 81                 :                :  *      counter of the block and when all chunks in a block are free the whole
                                 82                 :                :  *      block can be returned to malloc().
                                 83                 :                :  *
                                 84                 :                :  *      GenerationBlock is the header data for a block --- the usable space
                                 85                 :                :  *      within the block begins at the next alignment boundary.
                                 86                 :                :  */
                                 87                 :                : struct GenerationBlock
                                 88                 :                : {
                                 89                 :                :     dlist_node  node;           /* doubly-linked list of blocks */
                                 90                 :                :     GenerationContext *context; /* pointer back to the owning context */
                                 91                 :                :     Size        blksize;        /* allocated size of this block */
                                 92                 :                :     int         nchunks;        /* number of chunks in the block */
                                 93                 :                :     int         nfree;          /* number of free chunks */
                                 94                 :                :     char       *freeptr;        /* start of free space in this block */
                                 95                 :                :     char       *endptr;         /* end of space in this block */
                                 96                 :                : };
                                 97                 :                : 
                                 98                 :                : /*
                                 99                 :                :  * GenerationIsValid
                                100                 :                :  *      True iff set is valid generation set.
                                101                 :                :  */
                                102                 :                : #define GenerationIsValid(set) \
                                103                 :                :     (PointerIsValid(set) && IsA(set, GenerationContext))
                                104                 :                : 
                                105                 :                : /*
                                106                 :                :  * GenerationBlockIsValid
                                107                 :                :  *      True iff block is valid block of generation set.
                                108                 :                :  */
                                109                 :                : #define GenerationBlockIsValid(block) \
                                110                 :                :     (PointerIsValid(block) && GenerationIsValid((block)->context))
                                111                 :                : 
                                112                 :                : /*
                                113                 :                :  * GenerationBlockIsEmpty
                                114                 :                :  *      True iff block contains no chunks
                                115                 :                :  */
                                116                 :                : #define GenerationBlockIsEmpty(b) ((b)->nchunks == 0)
                                117                 :                : 
                                118                 :                : /*
                                119                 :                :  * We always store external chunks on a dedicated block.  This makes fetching
                                120                 :                :  * the block from an external chunk easy since it's always the first and only
                                121                 :                :  * chunk on the block.
                                122                 :                :  */
                                123                 :                : #define ExternalChunkGetBlock(chunk) \
                                124                 :                :     (GenerationBlock *) ((char *) chunk - Generation_BLOCKHDRSZ)
                                125                 :                : 
                                126                 :                : /* Obtain the keeper block for a generation context */
                                127                 :                : #define KeeperBlock(set) \
                                128                 :                :     ((GenerationBlock *) (((char *) set) + \
                                129                 :                :     MAXALIGN(sizeof(GenerationContext))))
                                130                 :                : 
                                131                 :                : /* Check if the block is the keeper block of the given generation context */
                                132                 :                : #define IsKeeperBlock(set, block) ((block) == (KeeperBlock(set)))
                                133                 :                : 
                                134                 :                : /* Inlined helper functions */
                                135                 :                : static inline void GenerationBlockInit(GenerationContext *context,
                                136                 :                :                                        GenerationBlock *block,
                                137                 :                :                                        Size blksize);
                                138                 :                : static inline void GenerationBlockMarkEmpty(GenerationBlock *block);
                                139                 :                : static inline Size GenerationBlockFreeBytes(GenerationBlock *block);
                                140                 :                : static inline void GenerationBlockFree(GenerationContext *set,
                                141                 :                :                                        GenerationBlock *block);
                                142                 :                : 
                                143                 :                : 
                                144                 :                : /*
                                145                 :                :  * Public routines
                                146                 :                :  */
                                147                 :                : 
                                148                 :                : 
                                149                 :                : /*
                                150                 :                :  * GenerationContextCreate
                                151                 :                :  *      Create a new Generation context.
                                152                 :                :  *
                                153                 :                :  * parent: parent context, or NULL if top-level context
                                154                 :                :  * name: name of context (must be statically allocated)
                                155                 :                :  * minContextSize: minimum context size
                                156                 :                :  * initBlockSize: initial allocation block size
                                157                 :                :  * maxBlockSize: maximum allocation block size
                                158                 :                :  */
                                159                 :                : MemoryContext
 2334 simon@2ndQuadrant.co      160                 :CBC         998 : GenerationContextCreate(MemoryContext parent,
                                161                 :                :                         const char *name,
                                162                 :                :                         Size minContextSize,
                                163                 :                :                         Size initBlockSize,
                                164                 :                :                         Size maxBlockSize)
                                165                 :                : {
                                166                 :                :     Size        firstBlockSize;
                                167                 :                :     Size        allocSize;
                                168                 :                :     GenerationContext *set;
                                169                 :                :     GenerationBlock *block;
                                170                 :                : 
                                171                 :                :     /* ensure MemoryChunk's size is properly maxaligned */
                                172                 :                :     StaticAssertDecl(Generation_CHUNKHDRSZ == MAXALIGN(Generation_CHUNKHDRSZ),
                                173                 :                :                      "sizeof(MemoryChunk) is not maxaligned");
                                174                 :                : 
                                175                 :                :     /*
                                176                 :                :      * First, validate allocation parameters.  Asserts seem sufficient because
                                177                 :                :      * nobody varies their parameters at runtime.  We somewhat arbitrarily
                                178                 :                :      * enforce a minimum 1K block size.  We restrict the maximum block size to
                                179                 :                :      * MEMORYCHUNK_MAX_BLOCKOFFSET as MemoryChunks are limited to this in
                                180                 :                :      * regards to addressing the offset between the chunk and the block that
                                181                 :                :      * the chunk is stored on.  We would be unable to store the offset between
                                182                 :                :      * the chunk and block for any chunks that were beyond
                                183                 :                :      * MEMORYCHUNK_MAX_BLOCKOFFSET bytes into the block if the block was to be
                                184                 :                :      * larger than this.
                                185                 :                :      */
  741 drowley@postgresql.o      186   [ +  -  -  + ]:            998 :     Assert(initBlockSize == MAXALIGN(initBlockSize) &&
                                187                 :                :            initBlockSize >= 1024);
                                188   [ +  -  +  -  :            998 :     Assert(maxBlockSize == MAXALIGN(maxBlockSize) &&
                                              -  + ]
                                189                 :                :            maxBlockSize >= initBlockSize &&
                                190                 :                :            AllocHugeSizeIsValid(maxBlockSize)); /* must be safe to double */
                                191   [ +  -  +  -  :            998 :     Assert(minContextSize == 0 ||
                                        +  -  -  + ]
                                192                 :                :            (minContextSize == MAXALIGN(minContextSize) &&
                                193                 :                :             minContextSize >= 1024 &&
                                194                 :                :             minContextSize <= maxBlockSize));
  594                           195         [ -  + ]:            998 :     Assert(maxBlockSize <= MEMORYCHUNK_MAX_BLOCKOFFSET);
                                196                 :                : 
                                197                 :                :     /* Determine size of initial block */
  741                           198                 :            998 :     allocSize = MAXALIGN(sizeof(GenerationContext)) +
                                199                 :                :         Generation_BLOCKHDRSZ + Generation_CHUNKHDRSZ;
                                200         [ +  - ]:            998 :     if (minContextSize != 0)
                                201                 :            998 :         allocSize = Max(allocSize, minContextSize);
                                202                 :                :     else
  741 drowley@postgresql.o      203                 :LBC    (195025) :         allocSize = Max(allocSize, initBlockSize);
                                204                 :                : 
                                205                 :                :     /*
                                206                 :                :      * Allocate the initial block.  Unlike other generation.c blocks, it
                                207                 :                :      * starts with the context header and its block header follows that.
                                208                 :                :      */
  741 drowley@postgresql.o      209                 :CBC         998 :     set = (GenerationContext *) malloc(allocSize);
 2314 tgl@sss.pgh.pa.us         210         [ -  + ]:            998 :     if (set == NULL)
                                211                 :                :     {
 2314 tgl@sss.pgh.pa.us         212                 :UBC           0 :         MemoryContextStats(TopMemoryContext);
                                213         [ #  # ]:              0 :         ereport(ERROR,
                                214                 :                :                 (errcode(ERRCODE_OUT_OF_MEMORY),
                                215                 :                :                  errmsg("out of memory"),
                                216                 :                :                  errdetail("Failed while creating memory context \"%s\".",
                                217                 :                :                            name)));
                                218                 :                :     }
                                219                 :                : 
                                220                 :                :     /*
                                221                 :                :      * Avoid writing code that can fail between here and MemoryContextCreate;
                                222                 :                :      * we'd leak the header if we ereport in this stretch.
                                223                 :                :      */
  741 drowley@postgresql.o      224                 :CBC         998 :     dlist_init(&set->blocks);
                                225                 :                : 
                                226                 :                :     /* Fill in the initial block's block header */
  272 drowley@postgresql.o      227                 :GNC         998 :     block = KeeperBlock(set);
                                228                 :                :     /* determine the block size and initialize it */
  741 drowley@postgresql.o      229                 :CBC         998 :     firstBlockSize = allocSize - MAXALIGN(sizeof(GenerationContext));
  594                           230                 :            998 :     GenerationBlockInit(set, block, firstBlockSize);
                                231                 :                : 
                                232                 :                :     /* add it to the doubly-linked list of blocks */
  741                           233                 :            998 :     dlist_push_head(&set->blocks, &block->node);
                                234                 :                : 
                                235                 :                :     /* use it as the current allocation block */
                                236                 :            998 :     set->block = block;
                                237                 :                : 
                                238                 :                :     /* No free block, yet */
                                239                 :            998 :     set->freeblock = NULL;
                                240                 :                : 
                                241                 :                :     /* Fill in GenerationContext-specific header fields */
  272 drowley@postgresql.o      242                 :GNC         998 :     set->initBlockSize = (uint32) initBlockSize;
                                243                 :            998 :     set->maxBlockSize = (uint32) maxBlockSize;
                                244                 :            998 :     set->nextBlockSize = (uint32) initBlockSize;
                                245                 :                : 
                                246                 :                :     /*
                                247                 :                :      * Compute the allocation chunk size limit for this context.
                                248                 :                :      *
                                249                 :                :      * Limit the maximum size a non-dedicated chunk can be so that we can fit
                                250                 :                :      * at least Generation_CHUNK_FRACTION of chunks this big onto the maximum
                                251                 :                :      * sized block.  We must further limit this value so that it's no more
                                252                 :                :      * than MEMORYCHUNK_MAX_VALUE.  We're unable to have non-external chunks
                                253                 :                :      * larger than that value as we store the chunk size in the MemoryChunk
                                254                 :                :      * 'value' field in the call to MemoryChunkSetHdrMask().
                                255                 :                :      */
  594 drowley@postgresql.o      256                 :CBC         998 :     set->allocChunkLimit = Min(maxBlockSize, MEMORYCHUNK_MAX_VALUE);
  741                           257                 :            998 :     while ((Size) (set->allocChunkLimit + Generation_CHUNKHDRSZ) >
                                258         [ +  + ]:           4990 :            (Size) ((Size) (maxBlockSize - Generation_BLOCKHDRSZ) / Generation_CHUNK_FRACTION))
                                259                 :           3992 :         set->allocChunkLimit >>= 1;
                                260                 :                : 
                                261                 :                :     /* Finally, do the type-independent part of context creation */
 2314 tgl@sss.pgh.pa.us         262                 :            998 :     MemoryContextCreate((MemoryContext) set,
                                263                 :                :                         T_GenerationContext,
                                264                 :                :                         MCTX_GENERATION_ID,
                                265                 :                :                         parent,
                                266                 :                :                         name);
                                267                 :                : 
  741 drowley@postgresql.o      268                 :            998 :     ((MemoryContext) set)->mem_allocated = firstBlockSize;
                                269                 :                : 
 2314 tgl@sss.pgh.pa.us         270                 :            998 :     return (MemoryContext) set;
                                271                 :                : }
                                272                 :                : 
                                273                 :                : /*
                                274                 :                :  * GenerationReset
                                275                 :                :  *      Frees all memory which is allocated in the given set.
                                276                 :                :  *
                                277                 :                :  * The initial "keeper" block (which shares a malloc chunk with the context
                                278                 :                :  * header) is not given back to the operating system though.  In this way, we
                                279                 :                :  * don't thrash malloc() when a context is repeatedly reset after small
                                280                 :                :  * allocations.
                                281                 :                :  */
                                282                 :                : void
 2334 simon@2ndQuadrant.co      283                 :            824 : GenerationReset(MemoryContext context)
                                284                 :                : {
 2328 rhaas@postgresql.org      285                 :            824 :     GenerationContext *set = (GenerationContext *) context;
                                286                 :                :     dlist_mutable_iter miter;
                                287                 :                : 
  534 peter@eisentraut.org      288   [ +  -  -  + ]:            824 :     Assert(GenerationIsValid(set));
                                289                 :                : 
                                290                 :                : #ifdef MEMORY_CONTEXT_CHECKING
                                291                 :                :     /* Check for corruption and leaks before freeing */
 2334 simon@2ndQuadrant.co      292                 :            824 :     GenerationCheck(context);
                                293                 :                : #endif
                                294                 :                : 
                                295                 :                :     /*
                                296                 :                :      * NULLify the free block pointer.  We must do this before calling
                                297                 :                :      * GenerationBlockFree as that function never expects to free the
                                298                 :                :      * freeblock.
                                299                 :                :      */
  741 drowley@postgresql.o      300                 :            824 :     set->freeblock = NULL;
                                301                 :                : 
 2334 simon@2ndQuadrant.co      302   [ +  -  +  + ]:           1736 :     dlist_foreach_modify(miter, &set->blocks)
                                303                 :                :     {
                                304                 :            912 :         GenerationBlock *block = dlist_container(GenerationBlock, node, miter.cur);
                                305                 :                : 
  272 drowley@postgresql.o      306         [ +  + ]:GNC         912 :         if (IsKeeperBlock(set, block))
  741 drowley@postgresql.o      307                 :CBC         824 :             GenerationBlockMarkEmpty(block);
                                308                 :                :         else
                                309                 :             88 :             GenerationBlockFree(set, block);
                                310                 :                :     }
                                311                 :                : 
                                312                 :                :     /* set it so new allocations to make use of the keeper block */
  272 drowley@postgresql.o      313                 :GNC         824 :     set->block = KeeperBlock(set);
                                314                 :                : 
                                315                 :                :     /* Reset block size allocation sequence, too */
  741 drowley@postgresql.o      316                 :CBC         824 :     set->nextBlockSize = set->initBlockSize;
                                317                 :                : 
                                318                 :                :     /* Ensure there is only 1 item in the dlist */
                                319         [ -  + ]:            824 :     Assert(!dlist_is_empty(&set->blocks));
                                320         [ -  + ]:            824 :     Assert(!dlist_has_next(&set->blocks, dlist_head_node(&set->blocks)));
 2334 simon@2ndQuadrant.co      321                 :            824 : }
                                322                 :                : 
                                323                 :                : /*
                                324                 :                :  * GenerationDelete
                                325                 :                :  *      Free all memory which is allocated in the given context.
                                326                 :                :  */
                                327                 :                : void
                                328                 :            824 : GenerationDelete(MemoryContext context)
                                329                 :                : {
                                330                 :                :     /* Reset to release all releasable GenerationBlocks */
                                331                 :            824 :     GenerationReset(context);
                                332                 :                :     /* And free the context header and keeper block */
 2314 tgl@sss.pgh.pa.us         333                 :            824 :     free(context);
 2334 simon@2ndQuadrant.co      334                 :            824 : }
                                335                 :                : 
                                336                 :                : /*
                                337                 :                :  * Helper for GenerationAlloc() that allocates an entire block for the chunk.
                                338                 :                :  *
                                339                 :                :  * GenerationAlloc()'s comment explains why this is separate.
                                340                 :                :  */
                                341                 :                : pg_noinline
                                342                 :                : static void *
   41 drowley@postgresql.o      343                 :GNC          88 : GenerationAllocLarge(MemoryContext context, Size size, int flags)
                                344                 :                : {
 2328 rhaas@postgresql.org      345                 :CBC          88 :     GenerationContext *set = (GenerationContext *) context;
                                346                 :                :     GenerationBlock *block;
                                347                 :                :     MemoryChunk *chunk;
                                348                 :                :     Size        chunk_size;
                                349                 :                :     Size        required_size;
                                350                 :                :     Size        blksize;
                                351                 :                : 
                                352                 :                :     /* validate 'size' is within the limits for the given 'flags' */
   41 drowley@postgresql.o      353                 :GNC          88 :     MemoryContextCheckSize(context, size, flags);
                                354                 :                : 
                                355                 :                : #ifdef MEMORY_CONTEXT_CHECKING
                                356                 :                :     /* ensure there's always space for the sentinel byte */
  585 drowley@postgresql.o      357                 :CBC          88 :     chunk_size = MAXALIGN(size + 1);
                                358                 :                : #else
                                359                 :                :     chunk_size = MAXALIGN(size);
                                360                 :                : #endif
                                361                 :             88 :     required_size = chunk_size + Generation_CHUNKHDRSZ;
   41 drowley@postgresql.o      362                 :GNC          88 :     blksize = required_size + Generation_BLOCKHDRSZ;
                                363                 :                : 
                                364                 :             88 :     block = (GenerationBlock *) malloc(blksize);
                                365         [ -  + ]:             88 :     if (block == NULL)
   41 drowley@postgresql.o      366                 :UNC           0 :         return MemoryContextAllocationFailure(context, size, flags);
                                367                 :                : 
   41 drowley@postgresql.o      368                 :GNC          88 :     context->mem_allocated += blksize;
                                369                 :                : 
                                370                 :                :     /* block with a single (used) chunk */
                                371                 :             88 :     block->context = set;
                                372                 :             88 :     block->blksize = blksize;
                                373                 :             88 :     block->nchunks = 1;
                                374                 :             88 :     block->nfree = 0;
                                375                 :                : 
                                376                 :                :     /* the block is completely full */
                                377                 :             88 :     block->freeptr = block->endptr = ((char *) block) + blksize;
                                378                 :                : 
                                379                 :             88 :     chunk = (MemoryChunk *) (((char *) block) + Generation_BLOCKHDRSZ);
                                380                 :                : 
                                381                 :                :     /* mark the MemoryChunk as externally managed */
                                382                 :             88 :     MemoryChunkSetHdrMaskExternal(chunk, MCTX_GENERATION_ID);
                                383                 :                : 
                                384                 :                : #ifdef MEMORY_CONTEXT_CHECKING
                                385                 :             88 :     chunk->requested_size = size;
                                386                 :                :     /* set mark to catch clobber of "unused" space */
                                387         [ -  + ]:             88 :     Assert(size < chunk_size);
                                388                 :             88 :     set_sentinel(MemoryChunkGetPointer(chunk), size);
                                389                 :                : #endif
                                390                 :                : #ifdef RANDOMIZE_ALLOCATED_MEMORY
                                391                 :                :     /* fill the allocated space with junk */
                                392                 :                :     randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
                                393                 :                : #endif
                                394                 :                : 
                                395                 :                :     /* add the block to the list of allocated blocks */
                                396                 :             88 :     dlist_push_head(&set->blocks, &block->node);
                                397                 :                : 
                                398                 :                :     /* Ensure any padding bytes are marked NOACCESS. */
                                399                 :                :     VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
                                400                 :                :                                chunk_size - size);
                                401                 :                : 
                                402                 :                :     /* Disallow access to the chunk header. */
                                403                 :                :     VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
                                404                 :                : 
                                405                 :             88 :     return MemoryChunkGetPointer(chunk);
                                406                 :                : }
                                407                 :                : 
                                408                 :                : /*
                                409                 :                :  * Small helper for allocating a new chunk from a chunk, to avoid duplicating
                                410                 :                :  * the code between GenerationAlloc() and GenerationAllocFromNewBlock().
                                411                 :                :  */
                                412                 :                : static inline void *
                                413                 :        1593086 : GenerationAllocChunkFromBlock(MemoryContext context, GenerationBlock *block,
                                414                 :                :                               Size size, Size chunk_size)
                                415                 :                : {
                                416                 :        1593086 :     MemoryChunk *chunk = (MemoryChunk *) (block->freeptr);
                                417                 :                : 
                                418                 :                :     /* validate we've been given a block with enough free space */
 2334 simon@2ndQuadrant.co      419         [ -  + ]:CBC     1593086 :     Assert(block != NULL);
   41 drowley@postgresql.o      420         [ -  + ]:GNC     1593086 :     Assert((block->endptr - block->freeptr) >=
                                421                 :                :            Generation_CHUNKHDRSZ + chunk_size);
                                422                 :                : 
                                423                 :                :     /* Prepare to initialize the chunk header. */
                                424                 :                :     VALGRIND_MAKE_MEM_UNDEFINED(chunk, Generation_CHUNKHDRSZ);
                                425                 :                : 
 2334 simon@2ndQuadrant.co      426                 :CBC     1593086 :     block->nchunks += 1;
                                427                 :        1593086 :     block->freeptr += (Generation_CHUNKHDRSZ + chunk_size);
                                428                 :                : 
 2333 tgl@sss.pgh.pa.us         429         [ -  + ]:        1593086 :     Assert(block->freeptr <= block->endptr);
                                430                 :                : 
  594 drowley@postgresql.o      431                 :        1593086 :     MemoryChunkSetHdrMask(chunk, block, chunk_size, MCTX_GENERATION_ID);
                                432                 :                : #ifdef MEMORY_CONTEXT_CHECKING
 2334 simon@2ndQuadrant.co      433                 :        1593086 :     chunk->requested_size = size;
                                434                 :                :     /* set mark to catch clobber of "unused" space */
  585 drowley@postgresql.o      435         [ -  + ]:        1593086 :     Assert(size < chunk_size);
                                436                 :        1593086 :     set_sentinel(MemoryChunkGetPointer(chunk), size);
                                437                 :                : #endif
                                438                 :                : #ifdef RANDOMIZE_ALLOCATED_MEMORY
                                439                 :                :     /* fill the allocated space with junk */
                                440                 :                :     randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
                                441                 :                : #endif
                                442                 :                : 
                                443                 :                :     /* Ensure any padding bytes are marked NOACCESS. */
                                444                 :                :     VALGRIND_MAKE_MEM_NOACCESS((char *) MemoryChunkGetPointer(chunk) + size,
                                445                 :                :                                chunk_size - size);
                                446                 :                : 
                                447                 :                :     /* Disallow access to the chunk header. */
                                448                 :                :     VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
                                449                 :                : 
  594                           450                 :        1593086 :     return MemoryChunkGetPointer(chunk);
                                451                 :                : }
                                452                 :                : 
                                453                 :                : /*
                                454                 :                :  * Helper for GenerationAlloc() that allocates a new block and returns a chunk
                                455                 :                :  * allocated from it.
                                456                 :                :  *
                                457                 :                :  * GenerationAlloc()'s comment explains why this is separate.
                                458                 :                :  */
                                459                 :                : pg_noinline
                                460                 :                : static void *
   41 drowley@postgresql.o      461                 :UNC           0 : GenerationAllocFromNewBlock(MemoryContext context, Size size, int flags,
                                462                 :                :                             Size chunk_size)
                                463                 :                : {
                                464                 :              0 :     GenerationContext *set = (GenerationContext *) context;
                                465                 :                :     GenerationBlock *block;
                                466                 :                :     Size        blksize;
                                467                 :                :     Size        required_size;
                                468                 :                : 
                                469                 :                :     /*
                                470                 :                :      * The first such block has size initBlockSize, and we double the space in
                                471                 :                :      * each succeeding block, but not more than maxBlockSize.
                                472                 :                :      */
                                473                 :              0 :     blksize = set->nextBlockSize;
                                474                 :              0 :     set->nextBlockSize <<= 1;
                                475         [ #  # ]:              0 :     if (set->nextBlockSize > set->maxBlockSize)
                                476                 :              0 :         set->nextBlockSize = set->maxBlockSize;
                                477                 :                : 
                                478                 :                :     /* we'll need space for the chunk, chunk hdr and block hdr */
                                479                 :              0 :     required_size = chunk_size + Generation_CHUNKHDRSZ + Generation_BLOCKHDRSZ;
                                480                 :                : 
                                481                 :                :     /* round the size up to the next power of 2 */
                                482         [ #  # ]:              0 :     if (blksize < required_size)
                                483                 :              0 :         blksize = pg_nextpower2_size_t(required_size);
                                484                 :                : 
                                485                 :              0 :     block = (GenerationBlock *) malloc(blksize);
                                486                 :                : 
                                487         [ #  # ]:              0 :     if (block == NULL)
                                488                 :              0 :         return MemoryContextAllocationFailure(context, size, flags);
                                489                 :                : 
                                490                 :              0 :     context->mem_allocated += blksize;
                                491                 :                : 
                                492                 :                :     /* initialize the new block */
                                493                 :              0 :     GenerationBlockInit(set, block, blksize);
                                494                 :                : 
                                495                 :                :     /* add it to the doubly-linked list of blocks */
                                496                 :              0 :     dlist_push_head(&set->blocks, &block->node);
                                497                 :                : 
                                498                 :                :     /* make this the current block */
                                499                 :              0 :     set->block = block;
                                500                 :                : 
                                501                 :              0 :     return GenerationAllocChunkFromBlock(context, block, size, chunk_size);
                                502                 :                : }
                                503                 :                : 
                                504                 :                : /*
                                505                 :                :  * GenerationAlloc
                                506                 :                :  *      Returns a pointer to allocated memory of given size or raises an ERROR
                                507                 :                :  *      on allocation failure, or returns NULL when flags contains
                                508                 :                :  *      MCXT_ALLOC_NO_OOM.
                                509                 :                :  *
                                510                 :                :  * No request may exceed:
                                511                 :                :  *      MAXALIGN_DOWN(SIZE_MAX) - Generation_BLOCKHDRSZ - Generation_CHUNKHDRSZ
                                512                 :                :  * All callers use a much-lower limit.
                                513                 :                :  *
                                514                 :                :  * Note: when using valgrind, it doesn't matter how the returned allocation
                                515                 :                :  * is marked, as mcxt.c will set it to UNDEFINED.  In some paths we will
                                516                 :                :  * return space that is marked NOACCESS - GenerationRealloc has to beware!
                                517                 :                :  *
                                518                 :                :  * This function should only contain the most common code paths.  Everything
                                519                 :                :  * else should be in pg_noinline helper functions, thus avoiding the overhead
                                520                 :                :  * of creating a stack frame for the common cases.  Allocating memory is often
                                521                 :                :  * a bottleneck in many workloads, so avoiding stack frame setup is
                                522                 :                :  * worthwhile.  Helper functions should always directly return the newly
                                523                 :                :  * allocated memory so that we can just return that address directly as a tail
                                524                 :                :  * call.
                                525                 :                :  */
                                526                 :                : void *
   41 drowley@postgresql.o      527                 :GNC     1593174 : GenerationAlloc(MemoryContext context, Size size, int flags)
                                528                 :                : {
                                529                 :        1593174 :     GenerationContext *set = (GenerationContext *) context;
                                530                 :                :     GenerationBlock *block;
                                531                 :                :     Size        chunk_size;
                                532                 :                :     Size        required_size;
                                533                 :                : 
                                534   [ +  -  -  + ]:        1593174 :     Assert(GenerationIsValid(set));
                                535                 :                : 
                                536                 :                : #ifdef MEMORY_CONTEXT_CHECKING
                                537                 :                :     /* ensure there's always space for the sentinel byte */
                                538                 :        1593174 :     chunk_size = MAXALIGN(size + 1);
                                539                 :                : #else
                                540                 :                :     chunk_size = MAXALIGN(size);
                                541                 :                : #endif
                                542                 :                : 
                                543                 :                :     /*
                                544                 :                :      * If requested size exceeds maximum for chunks we hand the the request
                                545                 :                :      * off to GenerationAllocLarge().
                                546                 :                :      */
                                547         [ +  + ]:        1593174 :     if (chunk_size > set->allocChunkLimit)
                                548                 :             88 :         return GenerationAllocLarge(context, size, flags);
                                549                 :                : 
                                550                 :        1593086 :     required_size = chunk_size + Generation_CHUNKHDRSZ;
                                551                 :                : 
                                552                 :                :     /*
                                553                 :                :      * Not an oversized chunk.  We try to first make use of the current block,
                                554                 :                :      * but if there's not enough space in it, instead of allocating a new
                                555                 :                :      * block, we look to see if the empty freeblock has enough space.  We
                                556                 :                :      * don't try reusing the keeper block.  If it's become empty we'll reuse
                                557                 :                :      * that again only if the context is reset.
                                558                 :                :      *
                                559                 :                :      * We only try reusing the freeblock if we've no space for this allocation
                                560                 :                :      * on the current block.  When a freeblock exists, we'll switch to it once
                                561                 :                :      * the first time we can't fit an allocation in the current block.  We
                                562                 :                :      * avoid ping-ponging between the two as we need to be careful not to
                                563                 :                :      * fragment differently sized consecutive allocations between several
                                564                 :                :      * blocks.  Going between the two could cause fragmentation for FIFO
                                565                 :                :      * workloads, which generation is meant to be good at.
                                566                 :                :      */
                                567                 :        1593086 :     block = set->block;
                                568                 :                : 
                                569         [ -  + ]:        1593086 :     if (unlikely(GenerationBlockFreeBytes(block) < required_size))
                                570                 :                :     {
   41 drowley@postgresql.o      571                 :UNC           0 :         GenerationBlock *freeblock = set->freeblock;
                                572                 :                : 
                                573                 :                :         /* freeblock, if set, must be empty */
                                574   [ #  #  #  # ]:              0 :         Assert(freeblock == NULL || GenerationBlockIsEmpty(freeblock));
                                575                 :                : 
                                576                 :                :         /* check if we have a freeblock and if it's big enough */
                                577   [ #  #  #  # ]:              0 :         if (freeblock != NULL &&
                                578                 :              0 :             GenerationBlockFreeBytes(freeblock) >= required_size)
                                579                 :                :         {
                                580                 :                :             /* make the freeblock the current block */
                                581                 :              0 :             set->freeblock = NULL;
                                582                 :              0 :             set->block = freeblock;
                                583                 :                : 
                                584                 :              0 :             return GenerationAllocChunkFromBlock(context,
                                585                 :                :                                                  freeblock,
                                586                 :                :                                                  size,
                                587                 :                :                                                  chunk_size);
                                588                 :                :         }
                                589                 :                :         else
                                590                 :                :         {
                                591                 :                :             /*
                                592                 :                :              * No freeblock, or it's not big enough for this allocation.  Make
                                593                 :                :              * a new block.
                                594                 :                :              */
                                595                 :              0 :             return GenerationAllocFromNewBlock(context, size, flags, chunk_size);
                                596                 :                :         }
                                597                 :                :     }
                                598                 :                : 
                                599                 :                :     /* The current block has space, so just allocate chunk there. */
   41 drowley@postgresql.o      600                 :GNC     1593086 :     return GenerationAllocChunkFromBlock(context, block, size, chunk_size);
                                601                 :                : }
                                602                 :                : 
                                603                 :                : /*
                                604                 :                :  * GenerationBlockInit
                                605                 :                :  *      Initializes 'block' assuming 'blksize'.  Does not update the context's
                                606                 :                :  *      mem_allocated field.
                                607                 :                :  */
                                608                 :                : static inline void
  594 drowley@postgresql.o      609                 :CBC         998 : GenerationBlockInit(GenerationContext *context, GenerationBlock *block,
                                610                 :                :                     Size blksize)
                                611                 :                : {
                                612                 :            998 :     block->context = context;
  741                           613                 :            998 :     block->blksize = blksize;
                                614                 :            998 :     block->nchunks = 0;
                                615                 :            998 :     block->nfree = 0;
                                616                 :                : 
                                617                 :            998 :     block->freeptr = ((char *) block) + Generation_BLOCKHDRSZ;
                                618                 :            998 :     block->endptr = ((char *) block) + blksize;
                                619                 :                : 
                                620                 :                :     /* Mark unallocated space NOACCESS. */
                                621                 :                :     VALGRIND_MAKE_MEM_NOACCESS(block->freeptr,
                                622                 :                :                                blksize - Generation_BLOCKHDRSZ);
                                623                 :            998 : }
                                624                 :                : 
                                625                 :                : /*
                                626                 :                :  * GenerationBlockMarkEmpty
                                627                 :                :  *      Set a block as empty.  Does not free the block.
                                628                 :                :  */
                                629                 :                : static inline void
                                630                 :           5925 : GenerationBlockMarkEmpty(GenerationBlock *block)
                                631                 :                : {
                                632                 :                : #if defined(USE_VALGRIND) || defined(CLOBBER_FREED_MEMORY)
                                633                 :           5925 :     char       *datastart = ((char *) block) + Generation_BLOCKHDRSZ;
                                634                 :                : #endif
                                635                 :                : 
                                636                 :                : #ifdef CLOBBER_FREED_MEMORY
                                637                 :           5925 :     wipe_mem(datastart, block->freeptr - datastart);
                                638                 :                : #else
                                639                 :                :     /* wipe_mem() would have done this */
                                640                 :                :     VALGRIND_MAKE_MEM_NOACCESS(datastart, block->freeptr - datastart);
                                641                 :                : #endif
                                642                 :                : 
                                643                 :                :     /* Reset the block, but don't return it to malloc */
                                644                 :           5925 :     block->nchunks = 0;
                                645                 :           5925 :     block->nfree = 0;
                                646                 :           5925 :     block->freeptr = ((char *) block) + Generation_BLOCKHDRSZ;
                                647                 :           5925 : }
                                648                 :                : 
                                649                 :                : /*
                                650                 :                :  * GenerationBlockFreeBytes
                                651                 :                :  *      Returns the number of bytes free in 'block'
                                652                 :                :  */
                                653                 :                : static inline Size
                                654                 :        1593086 : GenerationBlockFreeBytes(GenerationBlock *block)
                                655                 :                : {
                                656                 :        1593086 :     return (block->endptr - block->freeptr);
                                657                 :                : }
                                658                 :                : 
                                659                 :                : /*
                                660                 :                :  * GenerationBlockFree
                                661                 :                :  *      Remove 'block' from 'set' and release the memory consumed by it.
                                662                 :                :  */
                                663                 :                : static inline void
                                664                 :             88 : GenerationBlockFree(GenerationContext *set, GenerationBlock *block)
                                665                 :                : {
                                666                 :                :     /* Make sure nobody tries to free the keeper block */
  272 drowley@postgresql.o      667         [ -  + ]:GNC          88 :     Assert(!IsKeeperBlock(set, block));
                                668                 :                :     /* We shouldn't be freeing the freeblock either */
  741 drowley@postgresql.o      669         [ -  + ]:CBC          88 :     Assert(block != set->freeblock);
                                670                 :                : 
                                671                 :                :     /* release the block from the list of blocks */
                                672                 :             88 :     dlist_delete(&block->node);
                                673                 :                : 
                                674                 :             88 :     ((MemoryContext) set)->mem_allocated -= block->blksize;
                                675                 :                : 
                                676                 :                : #ifdef CLOBBER_FREED_MEMORY
                                677                 :             88 :     wipe_mem(block, block->blksize);
                                678                 :                : #endif
                                679                 :                : 
                                680                 :             88 :     free(block);
                                681                 :             88 : }
                                682                 :                : 
                                683                 :                : /*
                                684                 :                :  * GenerationFree
                                685                 :                :  *      Update number of chunks in the block, and consider freeing the block
                                686                 :                :  *      if it's become empty.
                                687                 :                :  */
                                688                 :                : void
  594                           689                 :        1592992 : GenerationFree(void *pointer)
                                690                 :                : {
                                691                 :        1592992 :     MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
                                692                 :                :     GenerationBlock *block;
                                693                 :                :     GenerationContext *set;
                                694                 :                : #if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
                                695                 :                :     || defined(CLOBBER_FREED_MEMORY)
                                696                 :                :     Size        chunksize;
                                697                 :                : #endif
                                698                 :                : 
                                699                 :                :     /* Allow access to the chunk header. */
                                700                 :                :     VALGRIND_MAKE_MEM_DEFINED(chunk, Generation_CHUNKHDRSZ);
                                701                 :                : 
                                702         [ -  + ]:        1592992 :     if (MemoryChunkIsExternal(chunk))
                                703                 :                :     {
  594 drowley@postgresql.o      704                 :UBC           0 :         block = ExternalChunkGetBlock(chunk);
                                705                 :                : 
                                706                 :                :         /*
                                707                 :                :          * Try to verify that we have a sane block pointer: the block header
                                708                 :                :          * should reference a generation context.
                                709                 :                :          */
  552 tgl@sss.pgh.pa.us         710   [ #  #  #  #  :              0 :         if (!GenerationBlockIsValid(block))
                                              #  # ]
                                711         [ #  # ]:              0 :             elog(ERROR, "could not find block containing chunk %p", chunk);
                                712                 :                : 
                                713                 :                : #if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
                                714                 :                :     || defined(CLOBBER_FREED_MEMORY)
  594 drowley@postgresql.o      715                 :              0 :         chunksize = block->endptr - (char *) pointer;
                                716                 :                : #endif
                                717                 :                :     }
                                718                 :                :     else
                                719                 :                :     {
  594 drowley@postgresql.o      720                 :CBC     1592992 :         block = MemoryChunkGetBlock(chunk);
                                721                 :                : 
                                722                 :                :         /*
                                723                 :                :          * In this path, for speed reasons we just Assert that the referenced
                                724                 :                :          * block is good.  Future field experience may show that this Assert
                                725                 :                :          * had better become a regular runtime test-and-elog check.
                                726                 :                :          */
  534 peter@eisentraut.org      727   [ +  -  +  -  :        1592992 :         Assert(GenerationBlockIsValid(block));
                                              -  + ]
                                728                 :                : 
                                729                 :                : #if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
                                730                 :                :     || defined(CLOBBER_FREED_MEMORY)
  594 drowley@postgresql.o      731                 :        1592992 :         chunksize = MemoryChunkGetValue(chunk);
                                732                 :                : #endif
                                733                 :                :     }
                                734                 :                : 
                                735                 :                : #ifdef MEMORY_CONTEXT_CHECKING
                                736                 :                :     /* Test for someone scribbling on unused space in chunk */
  585                           737         [ -  + ]:        1592992 :     Assert(chunk->requested_size < chunksize);
                                738         [ -  + ]:        1592992 :     if (!sentinel_ok(pointer, chunk->requested_size))
  585 drowley@postgresql.o      739         [ #  # ]:UBC           0 :         elog(WARNING, "detected write past chunk end in %s %p",
                                740                 :                :              ((MemoryContext) block->context)->name, chunk);
                                741                 :                : #endif
                                742                 :                : 
                                743                 :                : #ifdef CLOBBER_FREED_MEMORY
  594 drowley@postgresql.o      744                 :CBC     1592992 :     wipe_mem(pointer, chunksize);
                                745                 :                : #endif
                                746                 :                : 
                                747                 :                : #ifdef MEMORY_CONTEXT_CHECKING
                                748                 :                :     /* Reset requested_size to InvalidAllocSize in freed chunks */
                                749                 :        1592992 :     chunk->requested_size = InvalidAllocSize;
                                750                 :                : #endif
                                751                 :                : 
 2334 simon@2ndQuadrant.co      752                 :        1592992 :     block->nfree += 1;
                                753                 :                : 
                                754         [ -  + ]:        1592992 :     Assert(block->nchunks > 0);
                                755         [ -  + ]:        1592992 :     Assert(block->nfree <= block->nchunks);
   41 drowley@postgresql.o      756         [ -  + ]:GNC     1592992 :     Assert(block != block->context->freeblock);
                                757                 :                : 
                                758                 :                :     /* If there are still allocated chunks in the block, we're done. */
                                759         [ +  + ]:        1592992 :     if (likely(block->nfree < block->nchunks))
 2334 simon@2ndQuadrant.co      760                 :CBC     1587891 :         return;
                                761                 :                : 
  594 drowley@postgresql.o      762                 :           5101 :     set = block->context;
                                763                 :                : 
                                764                 :                :     /*-----------------------
                                765                 :                :      * The block this allocation was on has now become completely empty of
                                766                 :                :      * chunks.  In the general case, we can now return the memory for this
                                767                 :                :      * block back to malloc.  However, there are cases where we don't want to
                                768                 :                :      * do that:
                                769                 :                :      *
                                770                 :                :      * 1)   If it's the keeper block.  This block was malloc'd in the same
                                771                 :                :      *      allocation as the context itself and can't be free'd without
                                772                 :                :      *      freeing the context.
                                773                 :                :      * 2)   If it's the current block.  We could free this, but doing so would
                                774                 :                :      *      leave us nothing to set the current block to, so we just mark the
                                775                 :                :      *      block as empty so new allocations can reuse it again.
                                776                 :                :      * 3)   If we have no "freeblock" set, then we save a single block for
                                777                 :                :      *      future allocations to avoid having to malloc a new block again.
                                778                 :                :      *      This is useful for FIFO workloads as it avoids continual
                                779                 :                :      *      free/malloc cycles.
                                780                 :                :      */
   41 drowley@postgresql.o      781   [ -  +  -  - ]:GNC        5101 :     if (IsKeeperBlock(set, block) || set->block == block)
                                782                 :           5101 :         GenerationBlockMarkEmpty(block);    /* case 1 and 2 */
   41 drowley@postgresql.o      783         [ #  # ]:UNC           0 :     else if (set->freeblock == NULL)
                                784                 :                :     {
                                785                 :                :         /* case 3 */
  741                           786                 :              0 :         GenerationBlockMarkEmpty(block);
   41 drowley@postgresql.o      787                 :UBC           0 :         set->freeblock = block;
                                788                 :                :     }
                                789                 :                :     else
   41 drowley@postgresql.o      790                 :UNC           0 :         GenerationBlockFree(set, block);    /* Otherwise, free it */
                                791                 :                : }
                                792                 :                : 
                                793                 :                : /*
                                794                 :                :  * GenerationRealloc
                                795                 :                :  *      When handling repalloc, we simply allocate a new chunk, copy the data
                                796                 :                :  *      and discard the old one. The only exception is when the new size fits
                                797                 :                :  *      into the old chunk - in that case we just update chunk header.
                                798                 :                :  */
                                799                 :                : void *
   47                           800                 :              0 : GenerationRealloc(void *pointer, Size size, int flags)
                                801                 :                : {
  594 drowley@postgresql.o      802                 :UBC           0 :     MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
                                803                 :                :     GenerationContext *set;
                                804                 :                :     GenerationBlock *block;
                                805                 :                :     GenerationPointer newPointer;
                                806                 :                :     Size        oldsize;
                                807                 :                : 
                                808                 :                :     /* Allow access to the chunk header. */
                                809                 :                :     VALGRIND_MAKE_MEM_DEFINED(chunk, Generation_CHUNKHDRSZ);
                                810                 :                : 
                                811         [ #  # ]:              0 :     if (MemoryChunkIsExternal(chunk))
                                812                 :                :     {
                                813                 :              0 :         block = ExternalChunkGetBlock(chunk);
                                814                 :                : 
                                815                 :                :         /*
                                816                 :                :          * Try to verify that we have a sane block pointer: the block header
                                817                 :                :          * should reference a generation context.
                                818                 :                :          */
  552 tgl@sss.pgh.pa.us         819   [ #  #  #  #  :              0 :         if (!GenerationBlockIsValid(block))
                                              #  # ]
                                820         [ #  # ]:              0 :             elog(ERROR, "could not find block containing chunk %p", chunk);
                                821                 :                : 
  594 drowley@postgresql.o      822                 :              0 :         oldsize = block->endptr - (char *) pointer;
                                823                 :                :     }
                                824                 :                :     else
                                825                 :                :     {
                                826                 :              0 :         block = MemoryChunkGetBlock(chunk);
                                827                 :                : 
                                828                 :                :         /*
                                829                 :                :          * In this path, for speed reasons we just Assert that the referenced
                                830                 :                :          * block is good.  Future field experience may show that this Assert
                                831                 :                :          * had better become a regular runtime test-and-elog check.
                                832                 :                :          */
  534 peter@eisentraut.org      833   [ #  #  #  #  :              0 :         Assert(GenerationBlockIsValid(block));
                                              #  # ]
                                834                 :                : 
  594 drowley@postgresql.o      835                 :              0 :         oldsize = MemoryChunkGetValue(chunk);
                                836                 :                :     }
                                837                 :                : 
                                838                 :              0 :     set = block->context;
                                839                 :                : 
                                840                 :                : #ifdef MEMORY_CONTEXT_CHECKING
                                841                 :                :     /* Test for someone scribbling on unused space in chunk */
  585                           842         [ #  # ]:              0 :     Assert(chunk->requested_size < oldsize);
                                843         [ #  # ]:              0 :     if (!sentinel_ok(pointer, chunk->requested_size))
                                844         [ #  # ]:              0 :         elog(WARNING, "detected write past chunk end in %s %p",
                                845                 :                :              ((MemoryContext) set)->name, chunk);
                                846                 :                : #endif
                                847                 :                : 
                                848                 :                :     /*
                                849                 :                :      * Maybe the allocated area already is >= the new size.  (In particular,
                                850                 :                :      * we always fall out here if the requested size is a decrease.)
                                851                 :                :      *
                                852                 :                :      * This memory context does not use power-of-2 chunk sizing and instead
                                853                 :                :      * carves the chunks to be as small as possible, so most repalloc() calls
                                854                 :                :      * will end up in the palloc/memcpy/pfree branch.
                                855                 :                :      *
                                856                 :                :      * XXX Perhaps we should annotate this condition with unlikely()?
                                857                 :                :      */
 2334 simon@2ndQuadrant.co      858         [ #  # ]:              0 :     if (oldsize >= size)
                                859                 :                :     {
                                860                 :                : #ifdef MEMORY_CONTEXT_CHECKING
                                861                 :              0 :         Size        oldrequest = chunk->requested_size;
                                862                 :                : 
                                863                 :                : #ifdef RANDOMIZE_ALLOCATED_MEMORY
                                864                 :                :         /* We can only fill the extra space if we know the prior request */
                                865                 :                :         if (size > oldrequest)
                                866                 :                :             randomize_mem((char *) pointer + oldrequest,
                                867                 :                :                           size - oldrequest);
                                868                 :                : #endif
                                869                 :                : 
                                870                 :              0 :         chunk->requested_size = size;
                                871                 :                : 
                                872                 :                :         /*
                                873                 :                :          * If this is an increase, mark any newly-available part UNDEFINED.
                                874                 :                :          * Otherwise, mark the obsolete part NOACCESS.
                                875                 :                :          */
                                876                 :                :         if (size > oldrequest)
                                877                 :                :             VALGRIND_MAKE_MEM_UNDEFINED((char *) pointer + oldrequest,
                                878                 :                :                                         size - oldrequest);
                                879                 :                :         else
                                880                 :                :             VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size,
                                881                 :                :                                        oldsize - size);
                                882                 :                : 
                                883                 :                :         /* set mark to catch clobber of "unused" space */
  585 drowley@postgresql.o      884                 :              0 :         set_sentinel(pointer, size);
                                885                 :                : #else                           /* !MEMORY_CONTEXT_CHECKING */
                                886                 :                : 
                                887                 :                :         /*
                                888                 :                :          * We don't have the information to determine whether we're growing
                                889                 :                :          * the old request or shrinking it, so we conservatively mark the
                                890                 :                :          * entire new allocation DEFINED.
                                891                 :                :          */
                                892                 :                :         VALGRIND_MAKE_MEM_NOACCESS(pointer, oldsize);
                                893                 :                :         VALGRIND_MAKE_MEM_DEFINED(pointer, size);
                                894                 :                : #endif
                                895                 :                : 
                                896                 :                :         /* Disallow access to the chunk header. */
                                897                 :                :         VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
                                898                 :                : 
 2334 simon@2ndQuadrant.co      899                 :              0 :         return pointer;
                                900                 :                :     }
                                901                 :                : 
                                902                 :                :     /* allocate new chunk (this also checks size is valid) */
   47 drowley@postgresql.o      903                 :UNC           0 :     newPointer = GenerationAlloc((MemoryContext) set, size, flags);
                                904                 :                : 
                                905                 :                :     /* leave immediately if request was not completed */
 2334 simon@2ndQuadrant.co      906         [ #  # ]:UBC           0 :     if (newPointer == NULL)
                                907                 :                :     {
                                908                 :                :         /* Disallow access to the chunk header. */
                                909                 :                :         VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
   47 drowley@postgresql.o      910                 :UNC           0 :         return MemoryContextAllocationFailure((MemoryContext) set, size, flags);
                                911                 :                :     }
                                912                 :                : 
                                913                 :                :     /*
                                914                 :                :      * GenerationAlloc() may have returned a region that is still NOACCESS.
                                915                 :                :      * Change it to UNDEFINED for the moment; memcpy() will then transfer
                                916                 :                :      * definedness from the old allocation to the new.  If we know the old
                                917                 :                :      * allocation, copy just that much.  Otherwise, make the entire old chunk
                                918                 :                :      * defined to avoid errors as we copy the currently-NOACCESS trailing
                                919                 :                :      * bytes.
                                920                 :                :      */
                                921                 :                :     VALGRIND_MAKE_MEM_UNDEFINED(newPointer, size);
                                922                 :                : #ifdef MEMORY_CONTEXT_CHECKING
 2334 simon@2ndQuadrant.co      923                 :UBC           0 :     oldsize = chunk->requested_size;
                                924                 :                : #else
                                925                 :                :     VALGRIND_MAKE_MEM_DEFINED(pointer, oldsize);
                                926                 :                : #endif
                                927                 :                : 
                                928                 :                :     /* transfer existing data (certain to fit) */
                                929                 :              0 :     memcpy(newPointer, pointer, oldsize);
                                930                 :                : 
                                931                 :                :     /* free old chunk */
  594 drowley@postgresql.o      932                 :              0 :     GenerationFree(pointer);
                                933                 :                : 
 2334 simon@2ndQuadrant.co      934                 :              0 :     return newPointer;
                                935                 :                : }
                                936                 :                : 
                                937                 :                : /*
                                938                 :                :  * GenerationGetChunkContext
                                939                 :                :  *      Return the MemoryContext that 'pointer' belongs to.
                                940                 :                :  */
                                941                 :                : MemoryContext
  594 drowley@postgresql.o      942                 :              0 : GenerationGetChunkContext(void *pointer)
                                943                 :                : {
                                944                 :              0 :     MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
                                945                 :                :     GenerationBlock *block;
                                946                 :                : 
                                947                 :                :     /* Allow access to the chunk header. */
                                948                 :                :     VALGRIND_MAKE_MEM_DEFINED(chunk, Generation_CHUNKHDRSZ);
                                949                 :                : 
                                950         [ #  # ]:              0 :     if (MemoryChunkIsExternal(chunk))
                                951                 :              0 :         block = ExternalChunkGetBlock(chunk);
                                952                 :                :     else
                                953                 :              0 :         block = (GenerationBlock *) MemoryChunkGetBlock(chunk);
                                954                 :                : 
                                955                 :                :     /* Disallow access to the chunk header. */
                                956                 :                :     VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
                                957                 :                : 
  534 peter@eisentraut.org      958   [ #  #  #  #  :              0 :     Assert(GenerationBlockIsValid(block));
                                              #  # ]
  594 drowley@postgresql.o      959                 :              0 :     return &block->context->header;
                                960                 :                : }
                                961                 :                : 
                                962                 :                : /*
                                963                 :                :  * GenerationGetChunkSpace
                                964                 :                :  *      Given a currently-allocated chunk, determine the total space
                                965                 :                :  *      it occupies (including all memory-allocation overhead).
                                966                 :                :  */
                                967                 :                : Size
  594 drowley@postgresql.o      968                 :CBC          88 : GenerationGetChunkSpace(void *pointer)
                                969                 :                : {
                                970                 :             88 :     MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
                                971                 :                :     Size        chunksize;
                                972                 :                : 
                                973                 :                :     /* Allow access to the chunk header. */
                                974                 :                :     VALGRIND_MAKE_MEM_DEFINED(chunk, Generation_CHUNKHDRSZ);
                                975                 :                : 
                                976         [ +  - ]:             88 :     if (MemoryChunkIsExternal(chunk))
                                977                 :                :     {
                                978                 :             88 :         GenerationBlock *block = ExternalChunkGetBlock(chunk);
                                979                 :                : 
  534 peter@eisentraut.org      980   [ +  -  +  -  :             88 :         Assert(GenerationBlockIsValid(block));
                                              -  + ]
  594 drowley@postgresql.o      981                 :             88 :         chunksize = block->endptr - (char *) pointer;
                                982                 :                :     }
                                983                 :                :     else
  594 drowley@postgresql.o      984                 :LBC  (19166187) :         chunksize = MemoryChunkGetValue(chunk);
                                985                 :                : 
                                986                 :                :     /* Disallow access to the chunk header. */
                                987                 :                :     VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
                                988                 :                : 
  594 drowley@postgresql.o      989                 :CBC          88 :     return Generation_CHUNKHDRSZ + chunksize;
                                990                 :                : }
                                991                 :                : 
                                992                 :                : /*
                                993                 :                :  * GenerationIsEmpty
                                994                 :                :  *      Is a GenerationContext empty of any allocated space?
                                995                 :                :  */
                                996                 :                : bool
 2334 simon@2ndQuadrant.co      997                 :UBC           0 : GenerationIsEmpty(MemoryContext context)
                                998                 :                : {
 2328 rhaas@postgresql.org      999                 :              0 :     GenerationContext *set = (GenerationContext *) context;
                               1000                 :                :     dlist_iter  iter;
                               1001                 :                : 
  534 peter@eisentraut.org     1002   [ #  #  #  # ]:              0 :     Assert(GenerationIsValid(set));
                               1003                 :                : 
  741 drowley@postgresql.o     1004   [ #  #  #  # ]:              0 :     dlist_foreach(iter, &set->blocks)
                               1005                 :                :     {
                               1006                 :              0 :         GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur);
                               1007                 :                : 
                               1008         [ #  # ]:              0 :         if (block->nchunks > 0)
                               1009                 :              0 :             return false;
                               1010                 :                :     }
                               1011                 :                : 
                               1012                 :              0 :     return true;
                               1013                 :                : }
                               1014                 :                : 
                               1015                 :                : /*
                               1016                 :                :  * GenerationStats
                               1017                 :                :  *      Compute stats about memory consumption of a Generation context.
                               1018                 :                :  *
                               1019                 :                :  * printfunc: if not NULL, pass a human-readable stats string to this.
                               1020                 :                :  * passthru: pass this pointer through to printfunc.
                               1021                 :                :  * totals: if not NULL, add stats about this context into *totals.
                               1022                 :                :  * print_to_stderr: print stats to stderr if true, elog otherwise.
                               1023                 :                :  *
                               1024                 :                :  * XXX freespace only accounts for empty space at the end of the block, not
                               1025                 :                :  * space of freed chunks (which is unknown).
                               1026                 :                :  */
                               1027                 :                : void
 2210 tgl@sss.pgh.pa.us        1028                 :              0 : GenerationStats(MemoryContext context,
                               1029                 :                :                 MemoryStatsPrintFunc printfunc, void *passthru,
                               1030                 :                :                 MemoryContextCounters *totals, bool print_to_stderr)
                               1031                 :                : {
 2328 rhaas@postgresql.org     1032                 :              0 :     GenerationContext *set = (GenerationContext *) context;
 2334 simon@2ndQuadrant.co     1033                 :              0 :     Size        nblocks = 0;
                               1034                 :              0 :     Size        nchunks = 0;
                               1035                 :              0 :     Size        nfreechunks = 0;
                               1036                 :                :     Size        totalspace;
                               1037                 :              0 :     Size        freespace = 0;
                               1038                 :                :     dlist_iter  iter;
                               1039                 :                : 
  534 peter@eisentraut.org     1040   [ #  #  #  # ]:              0 :     Assert(GenerationIsValid(set));
                               1041                 :                : 
                               1042                 :                :     /* Include context header in totalspace */
 2210 tgl@sss.pgh.pa.us        1043                 :              0 :     totalspace = MAXALIGN(sizeof(GenerationContext));
                               1044                 :                : 
 2334 simon@2ndQuadrant.co     1045   [ #  #  #  # ]:              0 :     dlist_foreach(iter, &set->blocks)
                               1046                 :                :     {
                               1047                 :              0 :         GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur);
                               1048                 :                : 
                               1049                 :              0 :         nblocks++;
                               1050                 :              0 :         nchunks += block->nchunks;
                               1051                 :              0 :         nfreechunks += block->nfree;
 2333 tgl@sss.pgh.pa.us        1052                 :              0 :         totalspace += block->blksize;
 2334 simon@2ndQuadrant.co     1053                 :              0 :         freespace += (block->endptr - block->freeptr);
                               1054                 :                :     }
                               1055                 :                : 
 2210 tgl@sss.pgh.pa.us        1056         [ #  # ]:              0 :     if (printfunc)
                               1057                 :                :     {
                               1058                 :                :         char        stats_string[200];
                               1059                 :                : 
                               1060                 :              0 :         snprintf(stats_string, sizeof(stats_string),
                               1061                 :                :                  "%zu total in %zu blocks (%zu chunks); %zu free (%zu chunks); %zu used",
                               1062                 :                :                  totalspace, nblocks, nchunks, freespace,
                               1063                 :                :                  nfreechunks, totalspace - freespace);
 1104 fujii@postgresql.org     1064                 :              0 :         printfunc(context, passthru, stats_string, print_to_stderr);
                               1065                 :                :     }
                               1066                 :                : 
 2334 simon@2ndQuadrant.co     1067         [ #  # ]:              0 :     if (totals)
                               1068                 :                :     {
                               1069                 :              0 :         totals->nblocks += nblocks;
                               1070                 :              0 :         totals->freechunks += nfreechunks;
                               1071                 :              0 :         totals->totalspace += totalspace;
                               1072                 :              0 :         totals->freespace += freespace;
                               1073                 :                :     }
                               1074                 :              0 : }
                               1075                 :                : 
                               1076                 :                : 
                               1077                 :                : #ifdef MEMORY_CONTEXT_CHECKING
                               1078                 :                : 
                               1079                 :                : /*
                               1080                 :                :  * GenerationCheck
                               1081                 :                :  *      Walk through chunks and check consistency of memory.
                               1082                 :                :  *
                               1083                 :                :  * NOTE: report errors as WARNING, *not* ERROR or FATAL.  Otherwise you'll
                               1084                 :                :  * find yourself in an infinite loop when trouble occurs, because this
                               1085                 :                :  * routine will be entered again when elog cleanup tries to release memory!
                               1086                 :                :  */
                               1087                 :                : void
 2334 simon@2ndQuadrant.co     1088                 :CBC         824 : GenerationCheck(MemoryContext context)
                               1089                 :                : {
 2328 rhaas@postgresql.org     1090                 :            824 :     GenerationContext *gen = (GenerationContext *) context;
 2314 tgl@sss.pgh.pa.us        1091                 :            824 :     const char *name = context->name;
                               1092                 :                :     dlist_iter  iter;
 1654 tomas.vondra@postgre     1093                 :            824 :     Size        total_allocated = 0;
                               1094                 :                : 
                               1095                 :                :     /* walk all blocks in this context */
 2334 simon@2ndQuadrant.co     1096   [ +  -  +  + ]:           1736 :     dlist_foreach(iter, &gen->blocks)
                               1097                 :                :     {
 2333 tgl@sss.pgh.pa.us        1098                 :            912 :         GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur);
                               1099                 :                :         int         nfree,
                               1100                 :                :                     nchunks;
                               1101                 :                :         char       *ptr;
  594 drowley@postgresql.o     1102                 :            912 :         bool        has_external_chunk = false;
                               1103                 :                : 
 1657 tomas.vondra@postgre     1104                 :            912 :         total_allocated += block->blksize;
                               1105                 :                : 
                               1106                 :                :         /*
                               1107                 :                :          * nfree > nchunks is surely wrong.  Equality is allowed as the block
                               1108                 :                :          * might completely empty if it's the freeblock.
                               1109                 :                :          */
  741 drowley@postgresql.o     1110         [ -  + ]:            912 :         if (block->nfree > block->nchunks)
 2334 simon@2ndQuadrant.co     1111         [ #  # ]:UBC           0 :             elog(WARNING, "problem in Generation %s: number of free chunks %d in block %p exceeds %d allocated",
                               1112                 :                :                  name, block->nfree, block, block->nchunks);
                               1113                 :                : 
                               1114                 :                :         /* check block belongs to the correct context */
  594 drowley@postgresql.o     1115         [ -  + ]:CBC         912 :         if (block->context != gen)
  594 drowley@postgresql.o     1116         [ #  # ]:UBC           0 :             elog(WARNING, "problem in Generation %s: bogus context link in block %p",
                               1117                 :                :                  name, block);
                               1118                 :                : 
                               1119                 :                :         /* Now walk through the chunks and count them. */
 2334 simon@2ndQuadrant.co     1120                 :CBC         912 :         nfree = 0;
                               1121                 :            912 :         nchunks = 0;
                               1122                 :            912 :         ptr = ((char *) block) + Generation_BLOCKHDRSZ;
                               1123                 :                : 
                               1124         [ +  + ]:           1346 :         while (ptr < block->freeptr)
                               1125                 :                :         {
  594 drowley@postgresql.o     1126                 :            434 :             MemoryChunk *chunk = (MemoryChunk *) ptr;
                               1127                 :                :             GenerationBlock *chunkblock;
                               1128                 :                :             Size        chunksize;
                               1129                 :                : 
                               1130                 :                :             /* Allow access to the chunk header. */
                               1131                 :                :             VALGRIND_MAKE_MEM_DEFINED(chunk, Generation_CHUNKHDRSZ);
                               1132                 :                : 
                               1133         [ +  + ]:            434 :             if (MemoryChunkIsExternal(chunk))
                               1134                 :                :             {
                               1135                 :             88 :                 chunkblock = ExternalChunkGetBlock(chunk);
                               1136                 :             88 :                 chunksize = block->endptr - (char *) MemoryChunkGetPointer(chunk);
                               1137                 :             88 :                 has_external_chunk = true;
                               1138                 :                :             }
                               1139                 :                :             else
                               1140                 :                :             {
                               1141                 :            346 :                 chunkblock = MemoryChunkGetBlock(chunk);
                               1142                 :            346 :                 chunksize = MemoryChunkGetValue(chunk);
                               1143                 :                :             }
                               1144                 :                : 
                               1145                 :                :             /* move to the next chunk */
                               1146                 :            434 :             ptr += (chunksize + Generation_CHUNKHDRSZ);
                               1147                 :                : 
 2333 tgl@sss.pgh.pa.us        1148                 :            434 :             nchunks += 1;
                               1149                 :                : 
                               1150                 :                :             /* chunks have both block and context pointers, so check both */
  594 drowley@postgresql.o     1151         [ -  + ]:            434 :             if (chunkblock != block)
 2334 simon@2ndQuadrant.co     1152         [ #  # ]:UBC           0 :                 elog(WARNING, "problem in Generation %s: bogus block link in block %p, chunk %p",
                               1153                 :                :                      name, block, chunk);
                               1154                 :                : 
                               1155                 :                : 
                               1156                 :                :             /* is chunk allocated? */
  594 drowley@postgresql.o     1157         [ +  + ]:CBC         434 :             if (chunk->requested_size != InvalidAllocSize)
                               1158                 :                :             {
                               1159                 :                :                 /* now make sure the chunk size is correct */
                               1160         [ +  - ]:            175 :                 if (chunksize < chunk->requested_size ||
                               1161         [ -  + ]:            175 :                     chunksize != MAXALIGN(chunksize))
  594 drowley@postgresql.o     1162         [ #  # ]:UBC           0 :                     elog(WARNING, "problem in Generation %s: bogus chunk size in block %p, chunk %p",
                               1163                 :                :                          name, block, chunk);
                               1164                 :                : 
                               1165                 :                :                 /* check sentinel */
  585 drowley@postgresql.o     1166         [ -  + ]:CBC         175 :                 Assert(chunk->requested_size < chunksize);
                               1167         [ -  + ]:            175 :                 if (!sentinel_ok(chunk, Generation_CHUNKHDRSZ + chunk->requested_size))
 2334 simon@2ndQuadrant.co     1168         [ #  # ]:UBC           0 :                     elog(WARNING, "problem in Generation %s: detected write past chunk end in block %p, chunk %p",
                               1169                 :                :                          name, block, chunk);
                               1170                 :                :             }
                               1171                 :                :             else
 2334 simon@2ndQuadrant.co     1172                 :CBC         259 :                 nfree += 1;
                               1173                 :                : 
                               1174                 :                :             /* if chunk is allocated, disallow access to the chunk header */
  594 drowley@postgresql.o     1175                 :            434 :             if (chunk->requested_size != InvalidAllocSize)
                               1176                 :                :                 VALGRIND_MAKE_MEM_NOACCESS(chunk, Generation_CHUNKHDRSZ);
                               1177                 :                :         }
                               1178                 :                : 
                               1179                 :                :         /*
                               1180                 :                :          * Make sure we got the expected number of allocated and free chunks
                               1181                 :                :          * (as tracked in the block header).
                               1182                 :                :          */
 2334 simon@2ndQuadrant.co     1183         [ -  + ]:            912 :         if (nchunks != block->nchunks)
 2334 simon@2ndQuadrant.co     1184         [ #  # ]:UBC           0 :             elog(WARNING, "problem in Generation %s: number of allocated chunks %d in block %p does not match header %d",
                               1185                 :                :                  name, nchunks, block, block->nchunks);
                               1186                 :                : 
 2334 simon@2ndQuadrant.co     1187         [ -  + ]:CBC         912 :         if (nfree != block->nfree)
 2334 simon@2ndQuadrant.co     1188         [ #  # ]:UBC           0 :             elog(WARNING, "problem in Generation %s: number of free chunks %d in block %p does not match header %d",
                               1189                 :                :                  name, nfree, block, block->nfree);
                               1190                 :                : 
  594 drowley@postgresql.o     1191   [ +  +  -  + ]:CBC         912 :         if (has_external_chunk && nchunks > 1)
  594 drowley@postgresql.o     1192         [ #  # ]:UBC           0 :             elog(WARNING, "problem in Generation %s: external chunk on non-dedicated block %p",
                               1193                 :                :                  name, block);
                               1194                 :                : 
                               1195                 :                :     }
                               1196                 :                : 
 1487 jdavis@postgresql.or     1197         [ -  + ]:CBC         824 :     Assert(total_allocated == context->mem_allocated);
 2334 simon@2ndQuadrant.co     1198                 :            824 : }
                               1199                 :                : 
                               1200                 :                : #endif                          /* MEMORY_CONTEXT_CHECKING */
        

Generated by: LCOV version 2.1-beta2-3-g6141622