LCOV - code coverage report
Current view: top level - src/backend/utils/mmgr - alignedalloc.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 19.2 % 26 5
Test Date: 2026-03-03 18:14:56 Functions: 25.0 % 4 1
Legend: Lines:     hit not hit

            Line data    Source code
       1              : /*-------------------------------------------------------------------------
       2              :  *
       3              :  * alignedalloc.c
       4              :  *    Allocator functions to implement palloc_aligned
       5              :  *
       6              :  * This is not a fully-fledged MemoryContext type as there is no means to
       7              :  * create a MemoryContext of this type.  The code here only serves to allow
       8              :  * operations such as pfree() and repalloc() to work correctly on a memory
       9              :  * chunk that was allocated by palloc_aligned().
      10              :  *
      11              :  * Portions Copyright (c) 2022-2026, PostgreSQL Global Development Group
      12              :  *
      13              :  * IDENTIFICATION
      14              :  *    src/backend/utils/mmgr/alignedalloc.c
      15              :  *
      16              :  *-------------------------------------------------------------------------
      17              :  */
      18              : 
      19              : #include "postgres.h"
      20              : 
      21              : #include "utils/memdebug.h"
      22              : #include "utils/memutils_memorychunk.h"
      23              : 
      24              : /*
      25              :  * AlignedAllocFree
      26              :  *      Frees allocated memory; memory is removed from its owning context.
      27              :  */
      28              : void
      29       166287 : AlignedAllocFree(void *pointer)
      30              : {
      31       166287 :     MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
      32              :     void       *unaligned;
      33              : 
      34              :     VALGRIND_MAKE_MEM_DEFINED(chunk, sizeof(MemoryChunk));
      35              : 
      36              :     Assert(!MemoryChunkIsExternal(chunk));
      37              : 
      38              :     /* obtain the original (unaligned) allocated pointer */
      39       166287 :     unaligned = MemoryChunkGetBlock(chunk);
      40              : 
      41              : #ifdef MEMORY_CONTEXT_CHECKING
      42              :     /* Test for someone scribbling on unused space in chunk */
      43              :     if (!sentinel_ok(pointer, chunk->requested_size))
      44              :         elog(WARNING, "detected write past chunk end in %s %p",
      45              :              GetMemoryChunkContext(unaligned)->name, chunk);
      46              : #endif
      47              : 
      48              :     /*
      49              :      * Create a dummy vchunk covering the start of the unaligned chunk, but
      50              :      * not overlapping the aligned chunk.  This will be freed while pfree'ing
      51              :      * the unaligned chunk, keeping Valgrind happy.  Then when we return to
      52              :      * the outer pfree, that will clean up the vchunk for the aligned chunk.
      53              :      */
      54              :     VALGRIND_MEMPOOL_ALLOC(GetMemoryChunkContext(unaligned), unaligned,
      55              :                            (char *) pointer - (char *) unaligned);
      56              : 
      57              :     /* Recursively pfree the unaligned chunk */
      58       166287 :     pfree(unaligned);
      59       166287 : }
      60              : 
      61              : /*
      62              :  * AlignedAllocRealloc
      63              :  *      Change the allocated size of a chunk and return possibly a different
      64              :  *      pointer to a memory address aligned to the same boundary as the
      65              :  *      originally requested alignment.  The contents of 'pointer' will be
      66              :  *      copied into the returned pointer up until 'size'.  Any additional
      67              :  *      memory will be uninitialized.
      68              :  */
      69              : void *
      70            0 : AlignedAllocRealloc(void *pointer, Size size, int flags)
      71              : {
      72            0 :     MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer);
      73              :     Size        alignto;
      74              :     void       *unaligned;
      75              :     MemoryContext ctx;
      76              :     Size        old_size;
      77              :     void       *newptr;
      78              : 
      79              :     VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk));
      80              : 
      81            0 :     alignto = MemoryChunkGetValue(redirchunk);
      82            0 :     unaligned = MemoryChunkGetBlock(redirchunk);
      83              : 
      84              :     /* sanity check this is a power of 2 value */
      85              :     Assert((alignto & (alignto - 1)) == 0);
      86              : 
      87              :     /*
      88              :      * Determine the size of the original allocation.  We can't determine this
      89              :      * exactly as GetMemoryChunkSpace() returns the total space used for the
      90              :      * allocation, which for contexts like aset includes rounding up to the
      91              :      * next power of 2.  However, this value is just used to memcpy() the old
      92              :      * data into the new allocation, so we only need to concern ourselves with
      93              :      * not reading beyond the end of the original allocation's memory.  The
      94              :      * drawback here is that we may copy more bytes than we need to, which
      95              :      * only amounts to wasted effort.  We can safely subtract the extra bytes
      96              :      * that we requested to allow us to align the pointer.  We must also
      97              :      * subtract the space for the unaligned pointer's MemoryChunk since
      98              :      * GetMemoryChunkSpace should have included that.  This does assume that
      99              :      * all context types use MemoryChunk as a chunk header.
     100              :      */
     101            0 :     old_size = GetMemoryChunkSpace(unaligned) -
     102              :         PallocAlignedExtraBytes(alignto) - sizeof(MemoryChunk);
     103              : 
     104              : #ifdef MEMORY_CONTEXT_CHECKING
     105              :     /* check that GetMemoryChunkSpace returned something realistic */
     106              :     Assert(old_size >= redirchunk->requested_size);
     107              : #endif
     108              : 
     109              :     /*
     110              :      * To keep things simple, we always allocate a new aligned chunk and copy
     111              :      * data into it.  Because of the above inaccuracy, this may end in copying
     112              :      * more data than was in the original allocation request size, but that
     113              :      * should be OK.
     114              :      */
     115            0 :     ctx = GetMemoryChunkContext(unaligned);
     116            0 :     newptr = MemoryContextAllocAligned(ctx, size, alignto, flags);
     117              : 
     118              :     /* Cope cleanly with OOM */
     119            0 :     if (unlikely(newptr == NULL))
     120              :     {
     121              :         VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk));
     122            0 :         return MemoryContextAllocationFailure(ctx, size, flags);
     123              :     }
     124              : 
     125              :     /*
     126              :      * We may memcpy more than the original allocation request size, which
     127              :      * would result in trying to copy trailing bytes that the original
     128              :      * MemoryContextAllocAligned call marked NOACCESS.  So we must mark the
     129              :      * entire old_size as defined.  That's slightly annoying, but probably not
     130              :      * worth improving.
     131              :      */
     132              :     VALGRIND_MAKE_MEM_DEFINED(pointer, old_size);
     133            0 :     memcpy(newptr, pointer, Min(size, old_size));
     134              : 
     135              :     /*
     136              :      * Create a dummy vchunk covering the start of the old unaligned chunk,
     137              :      * but not overlapping the aligned chunk.  This will be freed while
     138              :      * pfree'ing the old unaligned chunk, keeping Valgrind happy.  Then when
     139              :      * we return to repalloc, it will move the vchunk for the aligned chunk.
     140              :      */
     141              :     VALGRIND_MEMPOOL_ALLOC(ctx, unaligned,
     142              :                            (char *) pointer - (char *) unaligned);
     143              : 
     144            0 :     pfree(unaligned);
     145              : 
     146            0 :     return newptr;
     147              : }
     148              : 
     149              : /*
     150              :  * AlignedAllocGetChunkContext
     151              :  *      Return the MemoryContext that 'pointer' belongs to.
     152              :  */
     153              : MemoryContext
     154            0 : AlignedAllocGetChunkContext(void *pointer)
     155              : {
     156            0 :     MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer);
     157              :     MemoryContext cxt;
     158              : 
     159              :     VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk));
     160              : 
     161              :     Assert(!MemoryChunkIsExternal(redirchunk));
     162              : 
     163            0 :     cxt = GetMemoryChunkContext(MemoryChunkGetBlock(redirchunk));
     164              : 
     165              :     VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk));
     166              : 
     167            0 :     return cxt;
     168              : }
     169              : 
     170              : /*
     171              :  * AlignedAllocGetChunkSpace
     172              :  *      Given a currently-allocated chunk, determine the total space
     173              :  *      it occupies (including all memory-allocation overhead).
     174              :  */
     175              : Size
     176            0 : AlignedAllocGetChunkSpace(void *pointer)
     177              : {
     178            0 :     MemoryChunk *redirchunk = PointerGetMemoryChunk(pointer);
     179              :     void       *unaligned;
     180              :     Size        space;
     181              : 
     182              :     VALGRIND_MAKE_MEM_DEFINED(redirchunk, sizeof(MemoryChunk));
     183              : 
     184            0 :     unaligned = MemoryChunkGetBlock(redirchunk);
     185            0 :     space = GetMemoryChunkSpace(unaligned);
     186              : 
     187              :     VALGRIND_MAKE_MEM_NOACCESS(redirchunk, sizeof(MemoryChunk));
     188              : 
     189            0 :     return space;
     190              : }
        

Generated by: LCOV version 2.0-1