PostgreSQL Source Code git master
Loading...
Searching...
No Matches
memutils_internal.h File Reference
#include "utils/memutils.h"
Include dependency graph for memutils_internal.h:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Macros

#define PallocAlignedExtraBytes(alignto)    ((alignto) + (sizeof(MemoryChunk) - MAXIMUM_ALIGNOF))
 
#define MEMORY_CONTEXT_METHODID_BITS   4
 
#define MEMORY_CONTEXT_METHODID_MASK    ((((uint64) 1) << MEMORY_CONTEXT_METHODID_BITS) - 1)
 

Typedefs

typedef enum MemoryContextMethodID MemoryContextMethodID
 

Enumerations

enum  MemoryContextMethodID {
  MCTX_0_RESERVED_UNUSEDMEM_ID , MCTX_1_RESERVED_GLIBC_ID , MCTX_2_RESERVED_GLIBC_ID , MCTX_ASET_ID ,
  MCTX_GENERATION_ID , MCTX_SLAB_ID , MCTX_ALIGNED_REDIRECT_ID , MCTX_BUMP_ID ,
  MCTX_8_UNUSED_ID , MCTX_9_UNUSED_ID , MCTX_10_UNUSED_ID , MCTX_11_UNUSED_ID ,
  MCTX_12_UNUSED_ID , MCTX_13_UNUSED_ID , MCTX_14_UNUSED_ID , MCTX_15_RESERVED_WIPEDMEM_ID
}
 

Functions

voidAllocSetAlloc (MemoryContext context, Size size, int flags)
 
void AllocSetFree (void *pointer)
 
voidAllocSetRealloc (void *pointer, Size size, int flags)
 
void AllocSetReset (MemoryContext context)
 
void AllocSetDelete (MemoryContext context)
 
MemoryContext AllocSetGetChunkContext (void *pointer)
 
Size AllocSetGetChunkSpace (void *pointer)
 
bool AllocSetIsEmpty (MemoryContext context)
 
void AllocSetStats (MemoryContext context, MemoryStatsPrintFunc printfunc, void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
 
voidGenerationAlloc (MemoryContext context, Size size, int flags)
 
void GenerationFree (void *pointer)
 
voidGenerationRealloc (void *pointer, Size size, int flags)
 
void GenerationReset (MemoryContext context)
 
void GenerationDelete (MemoryContext context)
 
MemoryContext GenerationGetChunkContext (void *pointer)
 
Size GenerationGetChunkSpace (void *pointer)
 
bool GenerationIsEmpty (MemoryContext context)
 
void GenerationStats (MemoryContext context, MemoryStatsPrintFunc printfunc, void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
 
voidSlabAlloc (MemoryContext context, Size size, int flags)
 
void SlabFree (void *pointer)
 
voidSlabRealloc (void *pointer, Size size, int flags)
 
void SlabReset (MemoryContext context)
 
void SlabDelete (MemoryContext context)
 
MemoryContext SlabGetChunkContext (void *pointer)
 
Size SlabGetChunkSpace (void *pointer)
 
bool SlabIsEmpty (MemoryContext context)
 
void SlabStats (MemoryContext context, MemoryStatsPrintFunc printfunc, void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
 
void AlignedAllocFree (void *pointer)
 
voidAlignedAllocRealloc (void *pointer, Size size, int flags)
 
MemoryContext AlignedAllocGetChunkContext (void *pointer)
 
Size AlignedAllocGetChunkSpace (void *pointer)
 
voidBumpAlloc (MemoryContext context, Size size, int flags)
 
void BumpFree (void *pointer)
 
voidBumpRealloc (void *pointer, Size size, int flags)
 
void BumpReset (MemoryContext context)
 
void BumpDelete (MemoryContext context)
 
MemoryContext BumpGetChunkContext (void *pointer)
 
Size BumpGetChunkSpace (void *pointer)
 
bool BumpIsEmpty (MemoryContext context)
 
void BumpStats (MemoryContext context, MemoryStatsPrintFunc printfunc, void *passthru, MemoryContextCounters *totals, bool print_to_stderr)
 
void MemoryContextCreate (MemoryContext node, NodeTag tag, MemoryContextMethodID method_id, MemoryContext parent, const char *name)
 
voidMemoryContextAllocationFailure (MemoryContext context, Size size, int flags)
 
pg_noreturn void MemoryContextSizeFailure (MemoryContext context, Size size, int flags)
 
static void MemoryContextCheckSize (MemoryContext context, Size size, int flags)
 

Macro Definition Documentation

◆ MEMORY_CONTEXT_METHODID_BITS

#define MEMORY_CONTEXT_METHODID_BITS   4

Definition at line 145 of file memutils_internal.h.

◆ MEMORY_CONTEXT_METHODID_MASK

#define MEMORY_CONTEXT_METHODID_MASK    ((((uint64) 1) << MEMORY_CONTEXT_METHODID_BITS) - 1)

Definition at line 146 of file memutils_internal.h.

167{
168 if (unlikely(!AllocSizeIsValid(size)))
169 {
170 if (!(flags & MCXT_ALLOC_HUGE) || !AllocHugeSizeIsValid(size))
171 MemoryContextSizeFailure(context, size, flags);
172 }
173}
174
175#endif /* MEMUTILS_INTERNAL_H */
#define unlikely(x)
Definition c.h:438
#define MCXT_ALLOC_HUGE
Definition fe_memutils.h:28
#define AllocHugeSizeIsValid(size)
Definition memutils.h:49
#define AllocSizeIsValid(size)
Definition memutils.h:42
pg_noreturn void MemoryContextSizeFailure(MemoryContext context, Size size, int flags)
Definition mcxt.c:1219

◆ PallocAlignedExtraBytes

#define PallocAlignedExtraBytes (   alignto)     ((alignto) + (sizeof(MemoryChunk) - MAXIMUM_ALIGNOF))

Definition at line 104 of file memutils_internal.h.

Typedef Documentation

◆ MemoryContextMethodID

Enumeration Type Documentation

◆ MemoryContextMethodID

Enumerator
MCTX_0_RESERVED_UNUSEDMEM_ID 
MCTX_1_RESERVED_GLIBC_ID 
MCTX_2_RESERVED_GLIBC_ID 
MCTX_ASET_ID 
MCTX_GENERATION_ID 
MCTX_SLAB_ID 
MCTX_ALIGNED_REDIRECT_ID 
MCTX_BUMP_ID 
MCTX_8_UNUSED_ID 
MCTX_9_UNUSED_ID 
MCTX_10_UNUSED_ID 
MCTX_11_UNUSED_ID 
MCTX_12_UNUSED_ID 
MCTX_13_UNUSED_ID 
MCTX_14_UNUSED_ID 
MCTX_15_RESERVED_WIPEDMEM_ID 

Definition at line 121 of file memutils_internal.h.

122{
123 MCTX_0_RESERVED_UNUSEDMEM_ID, /* 0000 occurs in never-used memory */
124 MCTX_1_RESERVED_GLIBC_ID, /* glibc malloc'd chunks usually match 0001 */
125 MCTX_2_RESERVED_GLIBC_ID, /* glibc malloc'd chunks > 128kB match 0010 */
138 MCTX_15_RESERVED_WIPEDMEM_ID /* 1111 occurs in wipe_mem'd memory */
MemoryContextMethodID
@ MCTX_15_RESERVED_WIPEDMEM_ID
@ MCTX_GENERATION_ID
@ MCTX_14_UNUSED_ID
@ MCTX_12_UNUSED_ID
@ MCTX_10_UNUSED_ID
@ MCTX_BUMP_ID
@ MCTX_11_UNUSED_ID
@ MCTX_8_UNUSED_ID
@ MCTX_1_RESERVED_GLIBC_ID
@ MCTX_SLAB_ID
@ MCTX_9_UNUSED_ID
@ MCTX_0_RESERVED_UNUSEDMEM_ID
@ MCTX_ASET_ID
@ MCTX_2_RESERVED_GLIBC_ID
@ MCTX_ALIGNED_REDIRECT_ID
@ MCTX_13_UNUSED_ID

Function Documentation

◆ AlignedAllocFree()

void AlignedAllocFree ( void pointer)
extern

Definition at line 29 of file alignedalloc.c.

30{
31 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
32 void *unaligned;
33
35
37
38 /* obtain the original (unaligned) allocated pointer */
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",
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 */
55 (char *) pointer - (char *) unaligned);
56
57 /* Recursively pfree the unaligned chunk */
59}
#define Assert(condition)
Definition c.h:943
#define WARNING
Definition elog.h:37
#define elog(elevel,...)
Definition elog.h:228
void pfree(void *pointer)
Definition mcxt.c:1616
MemoryContext GetMemoryChunkContext(void *pointer)
Definition mcxt.c:756
#define VALGRIND_MAKE_MEM_DEFINED(addr, size)
Definition memdebug.h:26
#define VALGRIND_MEMPOOL_ALLOC(context, addr, size)
Definition memdebug.h:29
static bool MemoryChunkIsExternal(MemoryChunk *chunk)
static void * MemoryChunkGetBlock(MemoryChunk *chunk)
#define PointerGetMemoryChunk(p)
static int fb(int x)
const char * name

References Assert, elog, fb(), GetMemoryChunkContext(), MemoryChunkGetBlock(), MemoryChunkIsExternal(), name, pfree(), PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, VALGRIND_MEMPOOL_ALLOC, and WARNING.

◆ AlignedAllocGetChunkContext()

◆ AlignedAllocGetChunkSpace()

Size AlignedAllocGetChunkSpace ( void pointer)
extern

Definition at line 176 of file alignedalloc.c.

177{
179 void *unaligned;
180 Size space;
181
183
186
188
189 return space;
190}
size_t Size
Definition c.h:689
Size GetMemoryChunkSpace(void *pointer)
Definition mcxt.c:770

References fb(), GetMemoryChunkSpace(), MemoryChunkGetBlock(), PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ AlignedAllocRealloc()

void * AlignedAllocRealloc ( void pointer,
Size  size,
int  flags 
)
extern

Definition at line 70 of file alignedalloc.c.

71{
74 void *unaligned;
75 MemoryContext ctx;
77 void *newptr;
78
80
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 */
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 */
116 newptr = MemoryContextAllocAligned(ctx, size, alignto, flags);
117
118 /* Cope cleanly with OOM */
119 if (unlikely(newptr == NULL))
120 {
122 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 */
133 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 */
142 (char *) pointer - (char *) unaligned);
143
145
146 return newptr;
147}
#define Min(x, y)
Definition c.h:1091
memcpy(sums, checksumBaseOffsets, sizeof(checksumBaseOffsets))
void * MemoryContextAllocAligned(MemoryContext context, Size size, Size alignto, int flags)
Definition mcxt.c:1482
void * MemoryContextAllocationFailure(MemoryContext context, Size size, int flags)
Definition mcxt.c:1198
#define PallocAlignedExtraBytes(alignto)
static Size MemoryChunkGetValue(MemoryChunk *chunk)

References Assert, fb(), GetMemoryChunkContext(), GetMemoryChunkSpace(), memcpy(), MemoryChunkGetBlock(), MemoryChunkGetValue(), MemoryContextAllocAligned(), MemoryContextAllocationFailure(), Min, PallocAlignedExtraBytes, pfree(), PointerGetMemoryChunk, unlikely, VALGRIND_MAKE_MEM_DEFINED, VALGRIND_MAKE_MEM_NOACCESS, and VALGRIND_MEMPOOL_ALLOC.

◆ AllocSetAlloc()

void * AllocSetAlloc ( MemoryContext  context,
Size  size,
int  flags 
)
extern

Definition at line 1012 of file aset.c.

1013{
1014 AllocSet set = (AllocSet) context;
1015 AllocBlock block;
1016 MemoryChunk *chunk;
1017 int fidx;
1018 Size chunk_size;
1020
1021 Assert(AllocSetIsValid(set));
1022
1023 /* due to the keeper block set->blocks should never be NULL */
1024 Assert(set->blocks != NULL);
1025
1026 /*
1027 * If requested size exceeds maximum for chunks we hand the request off to
1028 * AllocSetAllocLarge().
1029 */
1030 if (size > set->allocChunkLimit)
1031 return AllocSetAllocLarge(context, size, flags);
1032
1033 /*
1034 * Request is small enough to be treated as a chunk. Look in the
1035 * corresponding free list to see if there is a free chunk we could reuse.
1036 * If one is found, remove it from the free list, make it again a member
1037 * of the alloc set and return its data address.
1038 *
1039 * Note that we don't attempt to ensure there's space for the sentinel
1040 * byte here. We expect a large proportion of allocations to be for sizes
1041 * which are already a power of 2. If we were to always make space for a
1042 * sentinel byte in MEMORY_CONTEXT_CHECKING builds, then we'd end up
1043 * doubling the memory requirements for such allocations.
1044 */
1045 fidx = AllocSetFreeIndex(size);
1046 chunk = set->freelist[fidx];
1047 if (chunk != NULL)
1048 {
1050
1051 /* Allow access to the chunk header. */
1053
1054 Assert(fidx == MemoryChunkGetValue(chunk));
1055
1056 /* pop this chunk off the freelist */
1058 set->freelist[fidx] = link->next;
1060
1061#ifdef MEMORY_CONTEXT_CHECKING
1062 chunk->requested_size = size;
1063 /* set mark to catch clobber of "unused" space */
1065 set_sentinel(MemoryChunkGetPointer(chunk), size);
1066#endif
1067#ifdef RANDOMIZE_ALLOCATED_MEMORY
1068 /* fill the allocated space with junk */
1069 randomize_mem((char *) MemoryChunkGetPointer(chunk), size);
1070#endif
1071
1072 /* Ensure any padding bytes are marked NOACCESS. */
1075
1076 /* Disallow access to the chunk header. */
1078
1079 return MemoryChunkGetPointer(chunk);
1080 }
1081
1082 /*
1083 * Choose the actual chunk size to allocate.
1084 */
1085 chunk_size = GetChunkSizeFromFreeListIdx(fidx);
1086 Assert(chunk_size >= size);
1087
1088 block = set->blocks;
1089 availspace = block->endptr - block->freeptr;
1090
1091 /*
1092 * If there is enough room in the active allocation block, we will put the
1093 * chunk into that block. Else must start a new one.
1094 */
1095 if (unlikely(availspace < (chunk_size + ALLOC_CHUNKHDRSZ)))
1096 return AllocSetAllocFromNewBlock(context, size, flags, fidx);
1097
1098 /* There's enough space on the current block, so allocate from that */
1099 return AllocSetAllocChunkFromBlock(context, block, size, chunk_size, fidx);
1100}
static pg_noinline void * AllocSetAllocFromNewBlock(MemoryContext context, Size size, int flags, int fidx)
Definition aset.c:861
#define AllocSetIsValid(set)
Definition aset.c:200
#define GetFreeListLink(chkptr)
Definition aset.c:138
#define ALLOC_CHUNKHDRSZ
Definition aset.c:109
#define GetChunkSizeFromFreeListIdx(fidx)
Definition aset.c:146
static int AllocSetFreeIndex(Size size)
Definition aset.c:277
static void * AllocSetAllocChunkFromBlock(MemoryContext context, AllocBlock block, Size size, Size chunk_size, int fidx)
Definition aset.c:816
static pg_noinline void * AllocSetAllocLarge(MemoryContext context, Size size, int flags)
Definition aset.c:735
AllocSetContext * AllocSet
Definition aset.c:173
#define MemoryChunkGetPointer(c)
char * freeptr
Definition aset.c:192
char * endptr
Definition aset.c:193
uint32 allocChunkLimit
Definition aset.c:168
AllocBlock blocks
Definition aset.c:162
MemoryChunk * freelist[ALLOCSET_NUM_FREELISTS]
Definition aset.c:163

References ALLOC_CHUNKHDRSZ, AllocSetContext::allocChunkLimit, AllocSetAllocChunkFromBlock(), AllocSetAllocFromNewBlock(), AllocSetAllocLarge(), AllocSetFreeIndex(), AllocSetIsValid, Assert, AllocSetContext::blocks, AllocBlockData::endptr, fb(), AllocSetContext::freelist, AllocBlockData::freeptr, GetChunkSizeFromFreeListIdx, GetFreeListLink, MemoryChunkGetPointer, MemoryChunkGetValue(), unlikely, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

Referenced by AllocSetRealloc().

◆ AllocSetDelete()

void AllocSetDelete ( MemoryContext  context)
extern

Definition at line 632 of file aset.c.

633{
634 AllocSet set = (AllocSet) context;
635 AllocBlock block = set->blocks;
637
639
640#ifdef MEMORY_CONTEXT_CHECKING
641 /* Check for corruption and leaks before freeing */
642 AllocSetCheck(context);
643#endif
644
645 /* Remember keeper block size for Assert below */
646 keepersize = KeeperBlock(set)->endptr - ((char *) set);
647
648 /*
649 * If the context is a candidate for a freelist, put it into that freelist
650 * instead of destroying it.
651 */
652 if (set->freeListIndex >= 0)
653 {
655
656 /*
657 * Reset the context, if it needs it, so that we aren't hanging on to
658 * more than the initial malloc chunk.
659 */
660 if (!context->isReset)
661 MemoryContextResetOnly(context);
662
663 /*
664 * If the freelist is full, just discard what's already in it. See
665 * comments with context_freelists[].
666 */
667 if (freelist->num_free >= MAX_FREE_CONTEXTS)
668 {
669 while (freelist->first_free != NULL)
670 {
671 AllocSetContext *oldset = freelist->first_free;
672
674 freelist->num_free--;
675
676 /* Destroy the context's vpool --- see notes below */
678
679 /* All that remains is to free the header/initial block */
680 free(oldset);
681 }
682 Assert(freelist->num_free == 0);
683 }
684
685 /* Now add the just-deleted context to the freelist. */
686 set->header.nextchild = (MemoryContext) freelist->first_free;
687 freelist->first_free = set;
688 freelist->num_free++;
689
690 return;
691 }
692
693 /* Free all blocks, except the keeper which is part of context header */
694 while (block != NULL)
695 {
696 AllocBlock next = block->next;
697
698 if (!IsKeeperBlock(set, block))
699 context->mem_allocated -= block->endptr - ((char *) block);
700
701#ifdef CLOBBER_FREED_MEMORY
702 wipe_mem(block, block->freeptr - ((char *) block));
703#endif
704
705 if (!IsKeeperBlock(set, block))
706 {
707 /* As in AllocSetReset, free block-header vchunks explicitly */
708 VALGRIND_MEMPOOL_FREE(set, block);
709 free(block);
710 }
711
712 block = next;
713 }
714
715 Assert(context->mem_allocated == keepersize);
716
717 /*
718 * Destroy the vpool. We don't seem to need to explicitly free the
719 * initial block's header vchunk, nor any user-data vchunks that Valgrind
720 * still knows about; they'll all go away automatically.
721 */
723
724 /* Finally, free the context header, including the keeper block */
725 free(set);
726}
#define IsKeeperBlock(set, block)
Definition aset.c:248
#define KeeperBlock(set)
Definition aset.c:244
#define MAX_FREE_CONTEXTS
Definition aset.c:241
static AllocSetFreeList context_freelists[2]
Definition aset.c:257
static int32 next
Definition blutils.c:225
#define PG_USED_FOR_ASSERTS_ONLY
Definition c.h:249
void MemoryContextResetOnly(MemoryContext context)
Definition mcxt.c:422
#define VALGRIND_DESTROY_MEMPOOL(context)
Definition memdebug.h:25
#define VALGRIND_MEMPOOL_FREE(context, addr)
Definition memdebug.h:30
struct MemoryContextData * MemoryContext
Definition palloc.h:36
#define free(a)
AllocBlock next
Definition aset.c:191
MemoryContextData header
Definition aset.c:160
int freeListIndex
Definition aset.c:170
AllocSetContext * first_free
Definition aset.c:253
MemoryContext nextchild
Definition memnodes.h:130

References AllocSetIsValid, Assert, AllocSetContext::blocks, context_freelists, AllocBlockData::endptr, fb(), AllocSetFreeList::first_free, free, AllocSetContext::freeListIndex, AllocBlockData::freeptr, AllocSetContext::header, IsKeeperBlock, MemoryContextData::isReset, KeeperBlock, MAX_FREE_CONTEXTS, MemoryContextData::mem_allocated, MemoryContextResetOnly(), next, AllocBlockData::next, MemoryContextData::nextchild, AllocSetFreeList::num_free, PG_USED_FOR_ASSERTS_ONLY, VALGRIND_DESTROY_MEMPOOL, and VALGRIND_MEMPOOL_FREE.

◆ AllocSetFree()

void AllocSetFree ( void pointer)
extern

Definition at line 1107 of file aset.c.

1108{
1109 AllocSet set;
1110 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1111
1112 /* Allow access to the chunk header. */
1114
1115 if (MemoryChunkIsExternal(chunk))
1116 {
1117 /* Release single-chunk block. */
1118 AllocBlock block = ExternalChunkGetBlock(chunk);
1119
1120 /*
1121 * Try to verify that we have a sane block pointer: the block header
1122 * should reference an aset and the freeptr should match the endptr.
1123 */
1124 if (!AllocBlockIsValid(block) || block->freeptr != block->endptr)
1125 elog(ERROR, "could not find block containing chunk %p", chunk);
1126
1127 set = block->aset;
1128
1129#ifdef MEMORY_CONTEXT_CHECKING
1130 {
1131 /* Test for someone scribbling on unused space in chunk */
1132 Assert(chunk->requested_size < (block->endptr - (char *) pointer));
1133 if (!sentinel_ok(pointer, chunk->requested_size))
1134 elog(WARNING, "detected write past chunk end in %s %p",
1135 set->header.name, chunk);
1136 }
1137#endif
1138
1139 /* OK, remove block from aset's list and free it */
1140 if (block->prev)
1141 block->prev->next = block->next;
1142 else
1143 set->blocks = block->next;
1144 if (block->next)
1145 block->next->prev = block->prev;
1146
1147 set->header.mem_allocated -= block->endptr - ((char *) block);
1148
1149#ifdef CLOBBER_FREED_MEMORY
1150 wipe_mem(block, block->freeptr - ((char *) block));
1151#endif
1152
1153 /* As in AllocSetReset, free block-header vchunks explicitly */
1154 VALGRIND_MEMPOOL_FREE(set, block);
1155
1156 free(block);
1157 }
1158 else
1159 {
1160 AllocBlock block = MemoryChunkGetBlock(chunk);
1161 int fidx;
1163
1164 /*
1165 * In this path, for speed reasons we just Assert that the referenced
1166 * block is good. We can also Assert that the value field is sane.
1167 * Future field experience may show that these Asserts had better
1168 * become regular runtime test-and-elog checks.
1169 */
1170 Assert(AllocBlockIsValid(block));
1171 set = block->aset;
1172
1173 fidx = MemoryChunkGetValue(chunk);
1175 link = GetFreeListLink(chunk);
1176
1177 /*
1178 * It might seem odd that we use elevel ERROR for double-pfree but
1179 * only WARNING for write-past-chunk-end. But the two conditions are
1180 * not very comparable. In the double-pfree case we can prevent
1181 * corruption before it happens; while if we let it go through, the
1182 * result would be a corrupted freelist that allows this chunk to get
1183 * re-allocated twice. Thus the original bug could cascade into
1184 * hard-to-understand misbehavior that might manifest far away from
1185 * the actual source of the problem. On the other hand, a write past
1186 * chunk end can be relatively benign if just a few bytes too many
1187 * were written: often, only padding or unused space gets affected.
1188 * Moreover, whatever damage was done is already done, and we're just
1189 * reporting after the fact with no ability to clean it up. So just
1190 * warn, like AllocSetCheck would do if the chunk didn't get freed.
1191 */
1192#ifdef MEMORY_CONTEXT_CHECKING
1193 /* Test for previously-freed chunk */
1194 if (unlikely(chunk->requested_size == InvalidAllocSize))
1195 elog(ERROR, "detected double pfree in %s %p",
1196 set->header.name, chunk);
1197 /* Test for someone scribbling on unused space in chunk */
1198 if (chunk->requested_size < GetChunkSizeFromFreeListIdx(fidx))
1199 if (!sentinel_ok(pointer, chunk->requested_size))
1200 elog(WARNING, "detected write past chunk end in %s %p",
1201 set->header.name, chunk);
1202#endif
1203
1204#ifdef CLOBBER_FREED_MEMORY
1206#endif
1207 /* push this chunk onto the top of the free list */
1209 link->next = set->freelist[fidx];
1211 set->freelist[fidx] = chunk;
1212
1213#ifdef MEMORY_CONTEXT_CHECKING
1214
1215 /*
1216 * Reset requested_size to InvalidAllocSize in chunks that are on free
1217 * list.
1218 */
1219 chunk->requested_size = InvalidAllocSize;
1220#endif
1221 }
1222}
#define AllocBlockIsValid(block)
Definition aset.c:207
#define FreeListIdxIsValid(fidx)
Definition aset.c:142
#define ExternalChunkGetBlock(chunk)
Definition aset.c:215
#define ERROR
Definition elog.h:40
#define InvalidAllocSize
Definition memutils.h:47
AllocBlock prev
Definition aset.c:190
AllocSet aset
Definition aset.c:189
const char * name
Definition memnodes.h:131

References ALLOC_CHUNKHDRSZ, AllocBlockIsValid, AllocBlockData::aset, Assert, AllocSetContext::blocks, elog, AllocBlockData::endptr, ERROR, ExternalChunkGetBlock, fb(), free, AllocSetContext::freelist, FreeListIdxIsValid, AllocBlockData::freeptr, GetChunkSizeFromFreeListIdx, GetFreeListLink, AllocSetContext::header, InvalidAllocSize, MemoryContextData::mem_allocated, MemoryChunkGetBlock(), MemoryChunkGetValue(), MemoryChunkIsExternal(), MemoryContextData::name, AllocBlockData::next, PointerGetMemoryChunk, AllocBlockData::prev, unlikely, VALGRIND_MAKE_MEM_DEFINED, VALGRIND_MAKE_MEM_NOACCESS, VALGRIND_MEMPOOL_FREE, and WARNING.

Referenced by AllocSetRealloc().

◆ AllocSetGetChunkContext()

MemoryContext AllocSetGetChunkContext ( void pointer)
extern

Definition at line 1514 of file aset.c.

1515{
1516 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1517 AllocBlock block;
1518 AllocSet set;
1519
1520 /* Allow access to the chunk header. */
1522
1523 if (MemoryChunkIsExternal(chunk))
1524 block = ExternalChunkGetBlock(chunk);
1525 else
1526 block = (AllocBlock) MemoryChunkGetBlock(chunk);
1527
1528 /* Disallow access to the chunk header. */
1530
1531 Assert(AllocBlockIsValid(block));
1532 set = block->aset;
1533
1534 return &set->header;
1535}
struct AllocBlockData * AllocBlock
Definition aset.c:113

References ALLOC_CHUNKHDRSZ, AllocBlockIsValid, AllocBlockData::aset, Assert, ExternalChunkGetBlock, AllocSetContext::header, MemoryChunkGetBlock(), MemoryChunkIsExternal(), PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ AllocSetGetChunkSpace()

Size AllocSetGetChunkSpace ( void pointer)
extern

Definition at line 1543 of file aset.c.

1544{
1545 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1546 int fidx;
1547
1548 /* Allow access to the chunk header. */
1550
1551 if (MemoryChunkIsExternal(chunk))
1552 {
1553 AllocBlock block = ExternalChunkGetBlock(chunk);
1554
1555 /* Disallow access to the chunk header. */
1557
1558 Assert(AllocBlockIsValid(block));
1559
1560 return block->endptr - (char *) chunk;
1561 }
1562
1563 fidx = MemoryChunkGetValue(chunk);
1565
1566 /* Disallow access to the chunk header. */
1568
1570}

References ALLOC_CHUNKHDRSZ, AllocBlockIsValid, Assert, AllocBlockData::endptr, ExternalChunkGetBlock, fb(), FreeListIdxIsValid, GetChunkSizeFromFreeListIdx, MemoryChunkGetValue(), MemoryChunkIsExternal(), PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ AllocSetIsEmpty()

bool AllocSetIsEmpty ( MemoryContext  context)
extern

Definition at line 1577 of file aset.c.

1578{
1579 Assert(AllocSetIsValid(context));
1580
1581 /*
1582 * For now, we say "empty" only if the context is new or just reset. We
1583 * could examine the freelists to determine if all space has been freed,
1584 * but it's not really worth the trouble for present uses of this
1585 * functionality.
1586 */
1587 if (context->isReset)
1588 return true;
1589 return false;
1590}

References AllocSetIsValid, Assert, and MemoryContextData::isReset.

◆ AllocSetRealloc()

void * AllocSetRealloc ( void pointer,
Size  size,
int  flags 
)
extern

Definition at line 1237 of file aset.c.

1238{
1239 AllocBlock block;
1240 AllocSet set;
1241 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1243 int fidx;
1244
1245 /* Allow access to the chunk header. */
1247
1248 if (MemoryChunkIsExternal(chunk))
1249 {
1250 /*
1251 * The chunk must have been allocated as a single-chunk block. Use
1252 * realloc() to make the containing block bigger, or smaller, with
1253 * minimum space wastage.
1254 */
1256 Size chksize;
1257 Size blksize;
1259
1260 block = ExternalChunkGetBlock(chunk);
1261
1262 /*
1263 * Try to verify that we have a sane block pointer: the block header
1264 * should reference an aset and the freeptr should match the endptr.
1265 */
1266 if (!AllocBlockIsValid(block) || block->freeptr != block->endptr)
1267 elog(ERROR, "could not find block containing chunk %p", chunk);
1268
1269 set = block->aset;
1270
1271 /* only check size in paths where the limits could be hit */
1272 MemoryContextCheckSize((MemoryContext) set, size, flags);
1273
1274 oldchksize = block->endptr - (char *) pointer;
1275
1276#ifdef MEMORY_CONTEXT_CHECKING
1277 /* Test for someone scribbling on unused space in chunk */
1278 Assert(chunk->requested_size < oldchksize);
1279 if (!sentinel_ok(pointer, chunk->requested_size))
1280 elog(WARNING, "detected write past chunk end in %s %p",
1281 set->header.name, chunk);
1282#endif
1283
1284#ifdef MEMORY_CONTEXT_CHECKING
1285 /* ensure there's always space for the sentinel byte */
1286 chksize = MAXALIGN(size + 1);
1287#else
1288 chksize = MAXALIGN(size);
1289#endif
1290
1291 /* Do the realloc */
1293 oldblksize = block->endptr - ((char *) block);
1294
1295 newblock = (AllocBlock) realloc(block, blksize);
1296 if (newblock == NULL)
1297 {
1298 /* Disallow access to the chunk header. */
1300 return MemoryContextAllocationFailure(&set->header, size, flags);
1301 }
1302
1303 /*
1304 * Move the block-header vchunk explicitly. (mcxt.c will take care of
1305 * moving the vchunk for the user data.)
1306 */
1308 block = newblock;
1309
1310 /* updated separately, not to underflow when (oldblksize > blksize) */
1312 set->header.mem_allocated += blksize;
1313
1314 block->freeptr = block->endptr = ((char *) block) + blksize;
1315
1316 /* Update pointers since block has likely been moved */
1317 chunk = (MemoryChunk *) (((char *) block) + ALLOC_BLOCKHDRSZ);
1318 pointer = MemoryChunkGetPointer(chunk);
1319 if (block->prev)
1320 block->prev->next = block;
1321 else
1322 set->blocks = block;
1323 if (block->next)
1324 block->next->prev = block;
1325
1326#ifdef MEMORY_CONTEXT_CHECKING
1327#ifdef RANDOMIZE_ALLOCATED_MEMORY
1328
1329 /*
1330 * We can only randomize the extra space if we know the prior request.
1331 * When using Valgrind, randomize_mem() also marks memory UNDEFINED.
1332 */
1333 if (size > chunk->requested_size)
1334 randomize_mem((char *) pointer + chunk->requested_size,
1335 size - chunk->requested_size);
1336#else
1337
1338 /*
1339 * If this is an increase, realloc() will have marked any
1340 * newly-allocated part (from oldchksize to chksize) UNDEFINED, but we
1341 * also need to adjust trailing bytes from the old allocation (from
1342 * chunk->requested_size to oldchksize) as they are marked NOACCESS.
1343 * Make sure not to mark too many bytes in case chunk->requested_size
1344 * < size < oldchksize.
1345 */
1346#ifdef USE_VALGRIND
1347 if (Min(size, oldchksize) > chunk->requested_size)
1348 VALGRIND_MAKE_MEM_UNDEFINED((char *) pointer + chunk->requested_size,
1349 Min(size, oldchksize) - chunk->requested_size);
1350#endif
1351#endif
1352
1353 chunk->requested_size = size;
1354 /* set mark to catch clobber of "unused" space */
1355 Assert(size < chksize);
1356 set_sentinel(pointer, size);
1357#else /* !MEMORY_CONTEXT_CHECKING */
1358
1359 /*
1360 * We may need to adjust marking of bytes from the old allocation as
1361 * some of them may be marked NOACCESS. We don't know how much of the
1362 * old chunk size was the requested size; it could have been as small
1363 * as one byte. We have to be conservative and just mark the entire
1364 * old portion DEFINED. Make sure not to mark memory beyond the new
1365 * allocation in case it's smaller than the old one.
1366 */
1367 VALGRIND_MAKE_MEM_DEFINED(pointer, Min(size, oldchksize));
1368#endif
1369
1370 /* Ensure any padding bytes are marked NOACCESS. */
1371 VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size, chksize - size);
1372
1373 /* Disallow access to the chunk header. */
1375
1376 return pointer;
1377 }
1378
1379 block = MemoryChunkGetBlock(chunk);
1380
1381 /*
1382 * In this path, for speed reasons we just Assert that the referenced
1383 * block is good. We can also Assert that the value field is sane. Future
1384 * field experience may show that these Asserts had better become regular
1385 * runtime test-and-elog checks.
1386 */
1387 Assert(AllocBlockIsValid(block));
1388 set = block->aset;
1389
1390 fidx = MemoryChunkGetValue(chunk);
1393
1394#ifdef MEMORY_CONTEXT_CHECKING
1395 /* See comments in AllocSetFree about uses of ERROR and WARNING here */
1396 /* Test for previously-freed chunk */
1397 if (unlikely(chunk->requested_size == InvalidAllocSize))
1398 elog(ERROR, "detected realloc of freed chunk in %s %p",
1399 set->header.name, chunk);
1400 /* Test for someone scribbling on unused space in chunk */
1401 if (chunk->requested_size < oldchksize)
1402 if (!sentinel_ok(pointer, chunk->requested_size))
1403 elog(WARNING, "detected write past chunk end in %s %p",
1404 set->header.name, chunk);
1405#endif
1406
1407 /*
1408 * Chunk sizes are aligned to power of 2 in AllocSetAlloc(). Maybe the
1409 * allocated area already is >= the new size. (In particular, we will
1410 * fall out here if the requested size is a decrease.)
1411 */
1412 if (oldchksize >= size)
1413 {
1414#ifdef MEMORY_CONTEXT_CHECKING
1415 Size oldrequest = chunk->requested_size;
1416
1417#ifdef RANDOMIZE_ALLOCATED_MEMORY
1418 /* We can only fill the extra space if we know the prior request */
1419 if (size > oldrequest)
1420 randomize_mem((char *) pointer + oldrequest,
1421 size - oldrequest);
1422#endif
1423
1424 chunk->requested_size = size;
1425
1426 /*
1427 * If this is an increase, mark any newly-available part UNDEFINED.
1428 * Otherwise, mark the obsolete part NOACCESS.
1429 */
1430 if (size > oldrequest)
1431 VALGRIND_MAKE_MEM_UNDEFINED((char *) pointer + oldrequest,
1432 size - oldrequest);
1433 else
1434 VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size,
1435 oldchksize - size);
1436
1437 /* set mark to catch clobber of "unused" space */
1438 if (size < oldchksize)
1439 set_sentinel(pointer, size);
1440#else /* !MEMORY_CONTEXT_CHECKING */
1441
1442 /*
1443 * We don't have the information to determine whether we're growing
1444 * the old request or shrinking it, so we conservatively mark the
1445 * entire new allocation DEFINED.
1446 */
1448 VALGRIND_MAKE_MEM_DEFINED(pointer, size);
1449#endif
1450
1451 /* Disallow access to the chunk header. */
1453
1454 return pointer;
1455 }
1456 else
1457 {
1458 /*
1459 * Enlarge-a-small-chunk case. We just do this by brute force, ie,
1460 * allocate a new chunk and copy the data. Since we know the existing
1461 * data isn't huge, this won't involve any great memcpy expense, so
1462 * it's not worth being smarter. (At one time we tried to avoid
1463 * memcpy when it was possible to enlarge the chunk in-place, but that
1464 * turns out to misbehave unpleasantly for repeated cycles of
1465 * palloc/repalloc/pfree: the eventually freed chunks go into the
1466 * wrong freelist for the next initial palloc request, and so we leak
1467 * memory indefinitely. See pgsql-hackers archives for 2007-08-11.)
1468 */
1470 Size oldsize;
1471
1472 /* allocate new chunk (this also checks size is valid) */
1473 newPointer = AllocSetAlloc((MemoryContext) set, size, flags);
1474
1475 /* leave immediately if request was not completed */
1476 if (newPointer == NULL)
1477 {
1478 /* Disallow access to the chunk header. */
1480 return MemoryContextAllocationFailure((MemoryContext) set, size, flags);
1481 }
1482
1483 /*
1484 * AllocSetAlloc() may have returned a region that is still NOACCESS.
1485 * Change it to UNDEFINED for the moment; memcpy() will then transfer
1486 * definedness from the old allocation to the new. If we know the old
1487 * allocation, copy just that much. Otherwise, make the entire old
1488 * chunk defined to avoid errors as we copy the currently-NOACCESS
1489 * trailing bytes.
1490 */
1492#ifdef MEMORY_CONTEXT_CHECKING
1493 oldsize = chunk->requested_size;
1494#else
1497#endif
1498
1499 /* transfer existing data (certain to fit) */
1500 memcpy(newPointer, pointer, oldsize);
1501
1502 /* free old chunk */
1503 AllocSetFree(pointer);
1504
1505 return newPointer;
1506 }
1507}
#define ALLOC_BLOCKHDRSZ
Definition aset.c:108
void * AllocSetAlloc(MemoryContext context, Size size, int flags)
Definition aset.c:1012
void * AllocPointer
Definition aset.c:119
void AllocSetFree(void *pointer)
Definition aset.c:1107
#define MAXALIGN(LEN)
Definition c.h:896
#define VALGRIND_MEMPOOL_CHANGE(context, optr, nptr, size)
Definition memdebug.h:31
#define VALGRIND_MAKE_MEM_UNDEFINED(addr, size)
Definition memdebug.h:28
static void MemoryContextCheckSize(MemoryContext context, Size size, int flags)
#define realloc(a, b)

References ALLOC_BLOCKHDRSZ, ALLOC_CHUNKHDRSZ, AllocBlockIsValid, AllocSetAlloc(), AllocSetFree(), AllocBlockData::aset, Assert, elog, AllocBlockData::endptr, ERROR, ExternalChunkGetBlock, fb(), FreeListIdxIsValid, AllocBlockData::freeptr, GetChunkSizeFromFreeListIdx, AllocSetContext::header, InvalidAllocSize, MAXALIGN, MemoryContextData::mem_allocated, memcpy(), MemoryChunkGetBlock(), MemoryChunkGetPointer, MemoryChunkGetValue(), MemoryChunkIsExternal(), MemoryContextAllocationFailure(), MemoryContextCheckSize(), Min, MemoryContextData::name, AllocBlockData::next, PointerGetMemoryChunk, AllocBlockData::prev, realloc, unlikely, VALGRIND_MAKE_MEM_DEFINED, VALGRIND_MAKE_MEM_NOACCESS, VALGRIND_MAKE_MEM_UNDEFINED, VALGRIND_MEMPOOL_CHANGE, and WARNING.

◆ AllocSetReset()

void AllocSetReset ( MemoryContext  context)
extern

Definition at line 546 of file aset.c.

547{
548 AllocSet set = (AllocSet) context;
549 AllocBlock block;
551
553
554#ifdef MEMORY_CONTEXT_CHECKING
555 /* Check for corruption and leaks before freeing */
556 AllocSetCheck(context);
557#endif
558
559 /* Remember keeper block size for Assert below */
560 keepersize = KeeperBlock(set)->endptr - ((char *) set);
561
562 /* Clear chunk freelists */
563 MemSetAligned(set->freelist, 0, sizeof(set->freelist));
564
565 block = set->blocks;
566
567 /* New blocks list will be just the keeper block */
568 set->blocks = KeeperBlock(set);
569
570 while (block != NULL)
571 {
572 AllocBlock next = block->next;
573
574 if (IsKeeperBlock(set, block))
575 {
576 /* Reset the block, but don't return it to malloc */
577 char *datastart = ((char *) block) + ALLOC_BLOCKHDRSZ;
578
579#ifdef CLOBBER_FREED_MEMORY
581#else
582 /* wipe_mem() would have done this */
584#endif
585 block->freeptr = datastart;
586 block->prev = NULL;
587 block->next = NULL;
588 }
589 else
590 {
591 /* Normal case, release the block */
592 context->mem_allocated -= block->endptr - ((char *) block);
593
594#ifdef CLOBBER_FREED_MEMORY
595 wipe_mem(block, block->freeptr - ((char *) block));
596#endif
597
598 /*
599 * We need to free the block header's vchunk explicitly, although
600 * the user-data vchunks within will go away in the TRIM below.
601 * Otherwise Valgrind complains about leaked allocations.
602 */
603 VALGRIND_MEMPOOL_FREE(set, block);
604
605 free(block);
606 }
607 block = next;
608 }
609
610 Assert(context->mem_allocated == keepersize);
611
612 /*
613 * Instruct Valgrind to throw away all the vchunks associated with this
614 * context, except for the one covering the AllocSetContext and
615 * keeper-block header. This gets rid of the vchunks for whatever user
616 * data is getting discarded by the context reset.
617 */
619
620 /* Reset block size allocation sequence, too */
621 set->nextBlockSize = set->initBlockSize;
622}
#define FIRST_BLOCKHDRSZ
Definition aset.c:110
#define MemSetAligned(start, val, len)
Definition c.h:1137
#define VALGRIND_MEMPOOL_TRIM(context, addr, size)
Definition memdebug.h:32
uint32 initBlockSize
Definition aset.c:165
uint32 nextBlockSize
Definition aset.c:167

References ALLOC_BLOCKHDRSZ, AllocSetIsValid, Assert, AllocSetContext::blocks, AllocBlockData::endptr, fb(), FIRST_BLOCKHDRSZ, free, AllocSetContext::freelist, AllocBlockData::freeptr, AllocSetContext::initBlockSize, IsKeeperBlock, KeeperBlock, MemoryContextData::mem_allocated, MemSetAligned, next, AllocBlockData::next, AllocSetContext::nextBlockSize, PG_USED_FOR_ASSERTS_ONLY, AllocBlockData::prev, VALGRIND_MAKE_MEM_NOACCESS, VALGRIND_MEMPOOL_FREE, and VALGRIND_MEMPOOL_TRIM.

◆ AllocSetStats()

void AllocSetStats ( MemoryContext  context,
MemoryStatsPrintFunc  printfunc,
void passthru,
MemoryContextCounters totals,
bool  print_to_stderr 
)
extern

Definition at line 1602 of file aset.c.

1605{
1606 AllocSet set = (AllocSet) context;
1607 Size nblocks = 0;
1608 Size freechunks = 0;
1609 Size totalspace;
1610 Size freespace = 0;
1611 AllocBlock block;
1612 int fidx;
1613
1614 Assert(AllocSetIsValid(set));
1615
1616 /* Include context header in totalspace */
1617 totalspace = MAXALIGN(sizeof(AllocSetContext));
1618
1619 for (block = set->blocks; block != NULL; block = block->next)
1620 {
1621 nblocks++;
1622 totalspace += block->endptr - ((char *) block);
1623 freespace += block->endptr - block->freeptr;
1624 }
1625 for (fidx = 0; fidx < ALLOCSET_NUM_FREELISTS; fidx++)
1626 {
1628 MemoryChunk *chunk = set->freelist[fidx];
1629
1630 while (chunk != NULL)
1631 {
1633
1634 /* Allow access to the chunk header. */
1636 Assert(MemoryChunkGetValue(chunk) == fidx);
1638
1639 freechunks++;
1640 freespace += chksz + ALLOC_CHUNKHDRSZ;
1641
1643 chunk = link->next;
1645 }
1646 }
1647
1648 if (printfunc)
1649 {
1650 char stats_string[200];
1651
1653 "%zu total in %zu blocks; %zu free (%zu chunks); %zu used",
1654 totalspace, nblocks, freespace, freechunks,
1655 totalspace - freespace);
1657 }
1658
1659 if (totals)
1660 {
1661 totals->nblocks += nblocks;
1662 totals->freechunks += freechunks;
1663 totals->totalspace += totalspace;
1664 totals->freespace += freespace;
1665 }
1666}
#define ALLOCSET_NUM_FREELISTS
Definition aset.c:84
#define snprintf
Definition port.h:260

References ALLOC_CHUNKHDRSZ, ALLOCSET_NUM_FREELISTS, AllocSetIsValid, Assert, AllocSetContext::blocks, AllocBlockData::endptr, fb(), AllocSetContext::freelist, AllocBlockData::freeptr, GetChunkSizeFromFreeListIdx, GetFreeListLink, MAXALIGN, MemoryChunkGetValue(), AllocBlockData::next, snprintf, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ BumpAlloc()

void * BumpAlloc ( MemoryContext  context,
Size  size,
int  flags 
)
extern

Definition at line 517 of file bump.c.

518{
519 BumpContext *set = (BumpContext *) context;
520 BumpBlock *block;
521 Size chunk_size;
523
524 Assert(BumpIsValid(set));
525
526#ifdef MEMORY_CONTEXT_CHECKING
527 /* ensure there's always space for the sentinel byte */
528 chunk_size = MAXALIGN(size + 1);
529#else
530 chunk_size = MAXALIGN(size);
531#endif
532
533 /*
534 * If requested size exceeds maximum for chunks we hand the request off to
535 * BumpAllocLarge().
536 */
537 if (chunk_size > set->allocChunkLimit)
538 return BumpAllocLarge(context, size, flags);
539
540 required_size = chunk_size + Bump_CHUNKHDRSZ;
541
542 /*
543 * Not an oversized chunk. We try to first make use of the latest block,
544 * but if there's not enough space in it we must allocate a new block.
545 */
546 block = dlist_container(BumpBlock, node, dlist_head_node(&set->blocks));
547
549 return BumpAllocFromNewBlock(context, size, flags, chunk_size);
550
551 /* The current block has space, so just allocate chunk there. */
552 return BumpAllocChunkFromBlock(context, block, size, chunk_size);
553}
#define Bump_CHUNKHDRSZ
Definition bump.c:56
static pg_noinline void * BumpAllocLarge(MemoryContext context, Size size, int flags)
Definition bump.c:313
#define BumpIsValid(set)
Definition bump.c:102
static pg_noinline void * BumpAllocFromNewBlock(MemoryContext context, Size size, int flags, Size chunk_size)
Definition bump.c:453
static void * BumpAllocChunkFromBlock(MemoryContext context, BumpBlock *block, Size size, Size chunk_size)
Definition bump.c:394
static Size BumpBlockFreeBytes(BumpBlock *block)
Definition bump.c:611
static dlist_node * dlist_head_node(dlist_head *head)
Definition ilist.h:565
#define dlist_container(type, membername, ptr)
Definition ilist.h:593
dlist_head blocks
Definition bump.c:78
uint32 allocChunkLimit
Definition bump.c:76

References BumpContext::allocChunkLimit, Assert, BumpContext::blocks, Bump_CHUNKHDRSZ, BumpAllocChunkFromBlock(), BumpAllocFromNewBlock(), BumpAllocLarge(), BumpBlockFreeBytes(), BumpIsValid, dlist_container, dlist_head_node(), fb(), and MAXALIGN.

◆ BumpDelete()

void BumpDelete ( MemoryContext  context)
extern

Definition at line 294 of file bump.c.

295{
296 /* Reset to release all releasable BumpBlocks */
297 BumpReset(context);
298
299 /* Destroy the vpool -- see notes in aset.c */
301
302 /* And free the context header and keeper block */
303 free(context);
304}
void BumpReset(MemoryContext context)
Definition bump.c:251

References BumpReset(), free, and VALGRIND_DESTROY_MEMPOOL.

◆ BumpFree()

void BumpFree ( void pointer)
extern

Definition at line 646 of file bump.c.

647{
648 elog(ERROR, "%s is not supported by the bump memory allocator", "pfree");
649}

References elog, and ERROR.

◆ BumpGetChunkContext()

MemoryContext BumpGetChunkContext ( void pointer)
extern

Definition at line 667 of file bump.c.

668{
669 elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkContext");
670 return NULL; /* keep compiler quiet */
671}

References elog, ERROR, and fb().

◆ BumpGetChunkSpace()

Size BumpGetChunkSpace ( void pointer)
extern

Definition at line 678 of file bump.c.

679{
680 elog(ERROR, "%s is not supported by the bump memory allocator", "GetMemoryChunkSpace");
681 return 0; /* keep compiler quiet */
682}

References elog, and ERROR.

◆ BumpIsEmpty()

bool BumpIsEmpty ( MemoryContext  context)
extern

Definition at line 689 of file bump.c.

690{
691 BumpContext *set = (BumpContext *) context;
692 dlist_iter iter;
693
694 Assert(BumpIsValid(set));
695
696 dlist_foreach(iter, &set->blocks)
697 {
698 BumpBlock *block = dlist_container(BumpBlock, node, iter.cur);
699
700 if (!BumpBlockIsEmpty(block))
701 return false;
702 }
703
704 return true;
705}
static bool BumpBlockIsEmpty(BumpBlock *block)
Definition bump.c:578
#define dlist_foreach(iter, lhead)
Definition ilist.h:623
dlist_node * cur
Definition ilist.h:179

References Assert, BumpContext::blocks, BumpBlockIsEmpty(), BumpIsValid, dlist_iter::cur, dlist_container, and dlist_foreach.

◆ BumpRealloc()

void * BumpRealloc ( void pointer,
Size  size,
int  flags 
)
extern

Definition at line 656 of file bump.c.

657{
658 elog(ERROR, "%s is not supported by the bump memory allocator", "realloc");
659 return NULL; /* keep compiler quiet */
660}

References elog, ERROR, and fb().

◆ BumpReset()

void BumpReset ( MemoryContext  context)
extern

Definition at line 251 of file bump.c.

252{
253 BumpContext *set = (BumpContext *) context;
255
256 Assert(BumpIsValid(set));
257
258#ifdef MEMORY_CONTEXT_CHECKING
259 /* Check for corruption and leaks before freeing */
260 BumpCheck(context);
261#endif
262
264 {
265 BumpBlock *block = dlist_container(BumpBlock, node, miter.cur);
266
267 if (IsKeeperBlock(set, block))
268 BumpBlockMarkEmpty(block);
269 else
270 BumpBlockFree(set, block);
271 }
272
273 /*
274 * Instruct Valgrind to throw away all the vchunks associated with this
275 * context, except for the one covering the BumpContext and keeper-block
276 * header. This gets rid of the vchunks for whatever user data is getting
277 * discarded by the context reset.
278 */
280
281 /* Reset block size allocation sequence, too */
282 set->nextBlockSize = set->initBlockSize;
283
284 /* Ensure there is only 1 item in the dlist */
287}
static void BumpBlockFree(BumpContext *set, BumpBlock *block)
Definition bump.c:621
static void BumpBlockMarkEmpty(BumpBlock *block)
Definition bump.c:589
#define IsKeeperBlock(set, blk)
Definition bump.c:64
#define FIRST_BLOCKHDRSZ
Definition bump.c:49
static bool dlist_has_next(const dlist_head *head, const dlist_node *node)
Definition ilist.h:503
#define dlist_foreach_modify(iter, lhead)
Definition ilist.h:640
static bool dlist_is_empty(const dlist_head *head)
Definition ilist.h:336
uint32 initBlockSize
Definition bump.c:73
uint32 nextBlockSize
Definition bump.c:75

References Assert, BumpContext::blocks, BumpBlockFree(), BumpBlockMarkEmpty(), BumpIsValid, dlist_container, dlist_foreach_modify, dlist_has_next(), dlist_head_node(), dlist_is_empty(), fb(), FIRST_BLOCKHDRSZ, BumpContext::initBlockSize, IsKeeperBlock, BumpContext::nextBlockSize, and VALGRIND_MEMPOOL_TRIM.

Referenced by BumpDelete().

◆ BumpStats()

void BumpStats ( MemoryContext  context,
MemoryStatsPrintFunc  printfunc,
void passthru,
MemoryContextCounters totals,
bool  print_to_stderr 
)
extern

Definition at line 717 of file bump.c.

719{
720 BumpContext *set = (BumpContext *) context;
721 Size nblocks = 0;
722 Size totalspace = 0;
723 Size freespace = 0;
724 dlist_iter iter;
725
726 Assert(BumpIsValid(set));
727
728 dlist_foreach(iter, &set->blocks)
729 {
730 BumpBlock *block = dlist_container(BumpBlock, node, iter.cur);
731
732 nblocks++;
733 totalspace += (block->endptr - (char *) block);
734 freespace += (block->endptr - block->freeptr);
735 }
736
737 if (printfunc)
738 {
739 char stats_string[200];
740
742 "%zu total in %zu blocks; %zu free; %zu used",
743 totalspace, nblocks, freespace, totalspace - freespace);
745 }
746
747 if (totals)
748 {
749 totals->nblocks += nblocks;
750 totals->totalspace += totalspace;
751 totals->freespace += freespace;
752 }
753}
char * endptr
Definition bump.c:95
char * freeptr
Definition bump.c:94

References Assert, BumpContext::blocks, BumpIsValid, dlist_iter::cur, dlist_container, dlist_foreach, BumpBlock::endptr, fb(), BumpBlock::freeptr, and snprintf.

◆ GenerationAlloc()

void * GenerationAlloc ( MemoryContext  context,
Size  size,
int  flags 
)
extern

Definition at line 553 of file generation.c.

554{
555 GenerationContext *set = (GenerationContext *) context;
556 GenerationBlock *block;
557 Size chunk_size;
559
561
562#ifdef MEMORY_CONTEXT_CHECKING
563 /* ensure there's always space for the sentinel byte */
564 chunk_size = MAXALIGN(size + 1);
565#else
566 chunk_size = MAXALIGN(size);
567#endif
568
569 /*
570 * If requested size exceeds maximum for chunks we hand the request off to
571 * GenerationAllocLarge().
572 */
573 if (chunk_size > set->allocChunkLimit)
574 return GenerationAllocLarge(context, size, flags);
575
577
578 /*
579 * Not an oversized chunk. We try to first make use of the current block,
580 * but if there's not enough space in it, instead of allocating a new
581 * block, we look to see if the empty freeblock has enough space. We
582 * don't try reusing the keeper block. If it's become empty we'll reuse
583 * that again only if the context is reset.
584 *
585 * We only try reusing the freeblock if we've no space for this allocation
586 * on the current block. When a freeblock exists, we'll switch to it once
587 * the first time we can't fit an allocation in the current block. We
588 * avoid ping-ponging between the two as we need to be careful not to
589 * fragment differently sized consecutive allocations between several
590 * blocks. Going between the two could cause fragmentation for FIFO
591 * workloads, which generation is meant to be good at.
592 */
593 block = set->block;
594
596 {
597 GenerationBlock *freeblock = set->freeblock;
598
599 /* freeblock, if set, must be empty */
600 Assert(freeblock == NULL || GenerationBlockIsEmpty(freeblock));
601
602 /* check if we have a freeblock and if it's big enough */
603 if (freeblock != NULL &&
605 {
606 /* make the freeblock the current block */
607 set->freeblock = NULL;
608 set->block = freeblock;
609
610 return GenerationAllocChunkFromBlock(context,
611 freeblock,
612 size,
613 chunk_size);
614 }
615 else
616 {
617 /*
618 * No freeblock, or it's not big enough for this allocation. Make
619 * a new block.
620 */
621 return GenerationAllocFromNewBlock(context, size, flags, chunk_size);
622 }
623 }
624
625 /* The current block has space, so just allocate chunk there. */
626 return GenerationAllocChunkFromBlock(context, block, size, chunk_size);
627}
static pg_noinline void * GenerationAllocLarge(MemoryContext context, Size size, int flags)
Definition generation.c:363
static Size GenerationBlockFreeBytes(GenerationBlock *block)
Definition generation.c:680
#define Generation_CHUNKHDRSZ
Definition generation.c:47
static void * GenerationAllocChunkFromBlock(MemoryContext context, GenerationBlock *block, Size size, Size chunk_size)
Definition generation.c:436
#define GenerationBlockIsEmpty(b)
Definition generation.c:118
static pg_noinline void * GenerationAllocFromNewBlock(MemoryContext context, Size size, int flags, Size chunk_size)
Definition generation.c:484
#define GenerationIsValid(set)
Definition generation.c:104
GenerationBlock * freeblock
Definition generation.c:72
GenerationBlock * block
Definition generation.c:71
uint32 allocChunkLimit
Definition generation.c:69

References GenerationContext::allocChunkLimit, Assert, GenerationContext::block, fb(), GenerationContext::freeblock, Generation_CHUNKHDRSZ, GenerationAllocChunkFromBlock(), GenerationAllocFromNewBlock(), GenerationAllocLarge(), GenerationBlockFreeBytes(), GenerationBlockIsEmpty, GenerationIsValid, MAXALIGN, and unlikely.

Referenced by GenerationRealloc().

◆ GenerationDelete()

void GenerationDelete ( MemoryContext  context)
extern

Definition at line 344 of file generation.c.

345{
346 /* Reset to release all releasable GenerationBlocks */
347 GenerationReset(context);
348
349 /* Destroy the vpool -- see notes in aset.c */
351
352 /* And free the context header and keeper block */
353 free(context);
354}
void GenerationReset(MemoryContext context)
Definition generation.c:291

References free, GenerationReset(), and VALGRIND_DESTROY_MEMPOOL.

◆ GenerationFree()

void GenerationFree ( void pointer)
extern

Definition at line 718 of file generation.c.

719{
720 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
721 GenerationBlock *block;
723#if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
724 || defined(CLOBBER_FREED_MEMORY)
726#endif
727
728 /* Allow access to the chunk header. */
730
731 if (MemoryChunkIsExternal(chunk))
732 {
733 block = ExternalChunkGetBlock(chunk);
734
735 /*
736 * Try to verify that we have a sane block pointer: the block header
737 * should reference a generation context.
738 */
739 if (!GenerationBlockIsValid(block))
740 elog(ERROR, "could not find block containing chunk %p", chunk);
741
742#if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
743 || defined(CLOBBER_FREED_MEMORY)
744 chunksize = block->endptr - (char *) pointer;
745#endif
746 }
747 else
748 {
749 block = MemoryChunkGetBlock(chunk);
750
751 /*
752 * In this path, for speed reasons we just Assert that the referenced
753 * block is good. Future field experience may show that this Assert
754 * had better become a regular runtime test-and-elog check.
755 */
757
758#if (defined(MEMORY_CONTEXT_CHECKING) && defined(USE_ASSERT_CHECKING)) \
759 || defined(CLOBBER_FREED_MEMORY)
761#endif
762 }
763
764#ifdef MEMORY_CONTEXT_CHECKING
765 /* See comments in AllocSetFree about uses of ERROR and WARNING here */
766 /* Test for previously-freed chunk */
767 if (unlikely(chunk->requested_size == InvalidAllocSize))
768 elog(ERROR, "detected double pfree in %s %p",
769 ((MemoryContext) block->context)->name, chunk);
770 /* Test for someone scribbling on unused space in chunk */
771 Assert(chunk->requested_size < chunksize);
772 if (!sentinel_ok(pointer, chunk->requested_size))
773 elog(WARNING, "detected write past chunk end in %s %p",
774 ((MemoryContext) block->context)->name, chunk);
775#endif
776
777#ifdef CLOBBER_FREED_MEMORY
778 wipe_mem(pointer, chunksize);
779#endif
780
781#ifdef MEMORY_CONTEXT_CHECKING
782 /* Reset requested_size to InvalidAllocSize in freed chunks */
783 chunk->requested_size = InvalidAllocSize;
784#endif
785
786 block->nfree += 1;
787
788 Assert(block->nchunks > 0);
789 Assert(block->nfree <= block->nchunks);
790 Assert(block != block->context->freeblock);
791
792 /* If there are still allocated chunks in the block, we're done. */
793 if (likely(block->nfree < block->nchunks))
794 return;
795
796 set = block->context;
797
798 /*-----------------------
799 * The block this allocation was on has now become completely empty of
800 * chunks. In the general case, we can now return the memory for this
801 * block back to malloc. However, there are cases where we don't want to
802 * do that:
803 *
804 * 1) If it's the keeper block. This block was malloc'd in the same
805 * allocation as the context itself and can't be free'd without
806 * freeing the context.
807 * 2) If it's the current block. We could free this, but doing so would
808 * leave us nothing to set the current block to, so we just mark the
809 * block as empty so new allocations can reuse it again.
810 * 3) If we have no "freeblock" set, then we save a single block for
811 * future allocations to avoid having to malloc a new block again.
812 * This is useful for FIFO workloads as it avoids continual
813 * free/malloc cycles.
814 */
815 if (IsKeeperBlock(set, block) || set->block == block)
816 GenerationBlockMarkEmpty(block); /* case 1 and 2 */
817 else if (set->freeblock == NULL)
818 {
819 /* case 3 */
821 set->freeblock = block;
822 }
823 else
824 GenerationBlockFree(set, block); /* Otherwise, free it */
825}
#define likely(x)
Definition c.h:437
#define IsKeeperBlock(set, block)
Definition generation.c:134
static void GenerationBlockFree(GenerationContext *set, GenerationBlock *block)
Definition generation.c:690
static void GenerationBlockMarkEmpty(GenerationBlock *block)
Definition generation.c:656
#define GenerationBlockIsValid(block)
Definition generation.c:111
#define ExternalChunkGetBlock(chunk)
Definition generation.c:125
GenerationContext * context
Definition generation.c:92

References Assert, GenerationContext::block, GenerationBlock::context, elog, GenerationBlock::endptr, ERROR, ExternalChunkGetBlock, fb(), GenerationContext::freeblock, Generation_CHUNKHDRSZ, GenerationBlockFree(), GenerationBlockIsValid, GenerationBlockMarkEmpty(), InvalidAllocSize, IsKeeperBlock, likely, MemoryChunkGetBlock(), MemoryChunkGetValue(), MemoryChunkIsExternal(), GenerationBlock::nchunks, GenerationBlock::nfree, PointerGetMemoryChunk, unlikely, VALGRIND_MAKE_MEM_DEFINED, and WARNING.

Referenced by GenerationRealloc().

◆ GenerationGetChunkContext()

MemoryContext GenerationGetChunkContext ( void pointer)
extern

Definition at line 986 of file generation.c.

987{
988 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
989 GenerationBlock *block;
990
991 /* Allow access to the chunk header. */
993
994 if (MemoryChunkIsExternal(chunk))
995 block = ExternalChunkGetBlock(chunk);
996 else
997 block = (GenerationBlock *) MemoryChunkGetBlock(chunk);
998
999 /* Disallow access to the chunk header. */
1001
1003 return &block->context->header;
1004}
MemoryContextData header
Definition generation.c:63

References Assert, GenerationBlock::context, ExternalChunkGetBlock, Generation_CHUNKHDRSZ, GenerationBlockIsValid, GenerationContext::header, MemoryChunkGetBlock(), MemoryChunkIsExternal(), PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ GenerationGetChunkSpace()

Size GenerationGetChunkSpace ( void pointer)
extern

Definition at line 1012 of file generation.c.

1013{
1014 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
1016
1017 /* Allow access to the chunk header. */
1019
1020 if (MemoryChunkIsExternal(chunk))
1021 {
1022 GenerationBlock *block = ExternalChunkGetBlock(chunk);
1023
1025 chunksize = block->endptr - (char *) pointer;
1026 }
1027 else
1029
1030 /* Disallow access to the chunk header. */
1032
1034}

References Assert, GenerationBlock::endptr, ExternalChunkGetBlock, fb(), Generation_CHUNKHDRSZ, GenerationBlockIsValid, MemoryChunkGetValue(), MemoryChunkIsExternal(), PointerGetMemoryChunk, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ GenerationIsEmpty()

bool GenerationIsEmpty ( MemoryContext  context)
extern

Definition at line 1041 of file generation.c.

1042{
1043 GenerationContext *set = (GenerationContext *) context;
1044 dlist_iter iter;
1045
1047
1048 dlist_foreach(iter, &set->blocks)
1049 {
1050 GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur);
1051
1052 if (block->nchunks > 0)
1053 return false;
1054 }
1055
1056 return true;
1057}
dlist_head blocks
Definition generation.c:74

References Assert, GenerationContext::blocks, dlist_iter::cur, dlist_container, dlist_foreach, GenerationIsValid, and GenerationBlock::nchunks.

◆ GenerationRealloc()

void * GenerationRealloc ( void pointer,
Size  size,
int  flags 
)
extern

Definition at line 834 of file generation.c.

835{
836 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
838 GenerationBlock *block;
841
842 /* Allow access to the chunk header. */
844
845 if (MemoryChunkIsExternal(chunk))
846 {
847 block = ExternalChunkGetBlock(chunk);
848
849 /*
850 * Try to verify that we have a sane block pointer: the block header
851 * should reference a generation context.
852 */
853 if (!GenerationBlockIsValid(block))
854 elog(ERROR, "could not find block containing chunk %p", chunk);
855
856 oldsize = block->endptr - (char *) pointer;
857 }
858 else
859 {
860 block = MemoryChunkGetBlock(chunk);
861
862 /*
863 * In this path, for speed reasons we just Assert that the referenced
864 * block is good. Future field experience may show that this Assert
865 * had better become a regular runtime test-and-elog check.
866 */
868
870 }
871
872 set = block->context;
873
874#ifdef MEMORY_CONTEXT_CHECKING
875 /* See comments in AllocSetFree about uses of ERROR and WARNING here */
876 /* Test for previously-freed chunk */
877 if (unlikely(chunk->requested_size == InvalidAllocSize))
878 elog(ERROR, "detected realloc of freed chunk in %s %p",
879 ((MemoryContext) set)->name, chunk);
880 /* Test for someone scribbling on unused space in chunk */
881 Assert(chunk->requested_size < oldsize);
882 if (!sentinel_ok(pointer, chunk->requested_size))
883 elog(WARNING, "detected write past chunk end in %s %p",
884 ((MemoryContext) set)->name, chunk);
885#endif
886
887 /*
888 * Maybe the allocated area already big enough. (In particular, we always
889 * fall out here if the requested size is a decrease.)
890 *
891 * This memory context does not use power-of-2 chunk sizing and instead
892 * carves the chunks to be as small as possible, so most repalloc() calls
893 * will end up in the palloc/memcpy/pfree branch.
894 *
895 * XXX Perhaps we should annotate this condition with unlikely()?
896 */
897#ifdef MEMORY_CONTEXT_CHECKING
898 /* With MEMORY_CONTEXT_CHECKING, we need an extra byte for the sentinel */
899 if (oldsize > size)
900#else
901 if (oldsize >= size)
902#endif
903 {
904#ifdef MEMORY_CONTEXT_CHECKING
905 Size oldrequest = chunk->requested_size;
906
907#ifdef RANDOMIZE_ALLOCATED_MEMORY
908 /* We can only fill the extra space if we know the prior request */
909 if (size > oldrequest)
910 randomize_mem((char *) pointer + oldrequest,
911 size - oldrequest);
912#endif
913
914 chunk->requested_size = size;
915
916 /*
917 * If this is an increase, mark any newly-available part UNDEFINED.
918 * Otherwise, mark the obsolete part NOACCESS.
919 */
920 if (size > oldrequest)
921 VALGRIND_MAKE_MEM_UNDEFINED((char *) pointer + oldrequest,
922 size - oldrequest);
923 else
924 VALGRIND_MAKE_MEM_NOACCESS((char *) pointer + size,
925 oldsize - size);
926
927 /* set mark to catch clobber of "unused" space */
928 set_sentinel(pointer, size);
929#else /* !MEMORY_CONTEXT_CHECKING */
930
931 /*
932 * We don't have the information to determine whether we're growing
933 * the old request or shrinking it, so we conservatively mark the
934 * entire new allocation DEFINED.
935 */
937 VALGRIND_MAKE_MEM_DEFINED(pointer, size);
938#endif
939
940 /* Disallow access to the chunk header. */
942
943 return pointer;
944 }
945
946 /* allocate new chunk (this also checks size is valid) */
947 newPointer = GenerationAlloc((MemoryContext) set, size, flags);
948
949 /* leave immediately if request was not completed */
950 if (newPointer == NULL)
951 {
952 /* Disallow access to the chunk header. */
954 return MemoryContextAllocationFailure((MemoryContext) set, size, flags);
955 }
956
957 /*
958 * GenerationAlloc() may have returned a region that is still NOACCESS.
959 * Change it to UNDEFINED for the moment; memcpy() will then transfer
960 * definedness from the old allocation to the new. If we know the old
961 * allocation, copy just that much. Otherwise, make the entire old chunk
962 * defined to avoid errors as we copy the currently-NOACCESS trailing
963 * bytes.
964 */
966#ifdef MEMORY_CONTEXT_CHECKING
967 oldsize = chunk->requested_size;
968#else
970#endif
971
972 /* transfer existing data (certain to fit) */
973 memcpy(newPointer, pointer, oldsize);
974
975 /* free old chunk */
976 GenerationFree(pointer);
977
978 return newPointer;
979}
void GenerationFree(void *pointer)
Definition generation.c:718
void * GenerationPointer
Definition generation.c:55
void * GenerationAlloc(MemoryContext context, Size size, int flags)
Definition generation.c:553

References Assert, GenerationBlock::context, elog, GenerationBlock::endptr, ERROR, ExternalChunkGetBlock, fb(), Generation_CHUNKHDRSZ, GenerationAlloc(), GenerationBlockIsValid, GenerationFree(), InvalidAllocSize, memcpy(), MemoryChunkGetBlock(), MemoryChunkGetValue(), MemoryChunkIsExternal(), MemoryContextAllocationFailure(), name, PointerGetMemoryChunk, unlikely, VALGRIND_MAKE_MEM_DEFINED, VALGRIND_MAKE_MEM_NOACCESS, VALGRIND_MAKE_MEM_UNDEFINED, and WARNING.

◆ GenerationReset()

void GenerationReset ( MemoryContext  context)
extern

Definition at line 291 of file generation.c.

292{
293 GenerationContext *set = (GenerationContext *) context;
295
297
298#ifdef MEMORY_CONTEXT_CHECKING
299 /* Check for corruption and leaks before freeing */
300 GenerationCheck(context);
301#endif
302
303 /*
304 * NULLify the free block pointer. We must do this before calling
305 * GenerationBlockFree as that function never expects to free the
306 * freeblock.
307 */
308 set->freeblock = NULL;
309
311 {
313
314 if (IsKeeperBlock(set, block))
316 else
317 GenerationBlockFree(set, block);
318 }
319
320 /*
321 * Instruct Valgrind to throw away all the vchunks associated with this
322 * context, except for the one covering the GenerationContext and
323 * keeper-block header. This gets rid of the vchunks for whatever user
324 * data is getting discarded by the context reset.
325 */
327
328 /* set it so new allocations to make use of the keeper block */
329 set->block = KeeperBlock(set);
330
331 /* Reset block size allocation sequence, too */
332 set->nextBlockSize = set->initBlockSize;
333
334 /* Ensure there is only 1 item in the dlist */
337}
#define KeeperBlock(set)
Definition generation.c:129
#define FIRST_BLOCKHDRSZ
Definition generation.c:48

References Assert, GenerationContext::block, GenerationContext::blocks, dlist_container, dlist_foreach_modify, dlist_has_next(), dlist_head_node(), dlist_is_empty(), fb(), FIRST_BLOCKHDRSZ, GenerationContext::freeblock, GenerationBlockFree(), GenerationBlockMarkEmpty(), GenerationIsValid, GenerationContext::initBlockSize, IsKeeperBlock, KeeperBlock, GenerationContext::nextBlockSize, and VALGRIND_MEMPOOL_TRIM.

Referenced by GenerationDelete().

◆ GenerationStats()

void GenerationStats ( MemoryContext  context,
MemoryStatsPrintFunc  printfunc,
void passthru,
MemoryContextCounters totals,
bool  print_to_stderr 
)
extern

Definition at line 1072 of file generation.c.

1075{
1076 GenerationContext *set = (GenerationContext *) context;
1077 Size nblocks = 0;
1078 Size nchunks = 0;
1079 Size nfreechunks = 0;
1080 Size totalspace;
1081 Size freespace = 0;
1082 dlist_iter iter;
1083
1085
1086 /* Include context header in totalspace */
1087 totalspace = MAXALIGN(sizeof(GenerationContext));
1088
1089 dlist_foreach(iter, &set->blocks)
1090 {
1091 GenerationBlock *block = dlist_container(GenerationBlock, node, iter.cur);
1092
1093 nblocks++;
1094 nchunks += block->nchunks;
1095 nfreechunks += block->nfree;
1096 totalspace += block->blksize;
1097 freespace += (block->endptr - block->freeptr);
1098 }
1099
1100 if (printfunc)
1101 {
1102 char stats_string[200];
1103
1105 "%zu total in %zu blocks (%zu chunks); %zu free (%zu chunks); %zu used",
1106 totalspace, nblocks, nchunks, freespace,
1107 nfreechunks, totalspace - freespace);
1109 }
1110
1111 if (totals)
1112 {
1113 totals->nblocks += nblocks;
1114 totals->freechunks += nfreechunks;
1115 totals->totalspace += totalspace;
1116 totals->freespace += freespace;
1117 }
1118}

References Assert, GenerationBlock::blksize, GenerationContext::blocks, dlist_iter::cur, dlist_container, dlist_foreach, GenerationBlock::endptr, fb(), GenerationBlock::freeptr, GenerationIsValid, MAXALIGN, GenerationBlock::nchunks, GenerationBlock::nfree, and snprintf.

◆ MemoryContextAllocationFailure()

void * MemoryContextAllocationFailure ( MemoryContext  context,
Size  size,
int  flags 
)
extern

Definition at line 1198 of file mcxt.c.

1199{
1200 if ((flags & MCXT_ALLOC_NO_OOM) == 0)
1201 {
1202 if (TopMemoryContext)
1204 ereport(ERROR,
1206 errmsg("out of memory"),
1207 errdetail("Failed on request of size %zu in memory context \"%s\".",
1208 size, context->name)));
1209 }
1210 return NULL;
1211}
int errcode(int sqlerrcode)
Definition elog.c:874
int errdetail(const char *fmt,...) pg_attribute_printf(1
#define ereport(elevel,...)
Definition elog.h:152
#define MCXT_ALLOC_NO_OOM
Definition fe_memutils.h:29
MemoryContext TopMemoryContext
Definition mcxt.c:166
void MemoryContextStats(MemoryContext context)
Definition mcxt.c:863
static char * errmsg

References ereport, errcode(), errdetail(), errmsg, ERROR, fb(), MCXT_ALLOC_NO_OOM, MemoryContextStats(), MemoryContextData::name, and TopMemoryContext.

Referenced by AlignedAllocRealloc(), AllocSetAllocFromNewBlock(), AllocSetAllocLarge(), AllocSetRealloc(), BumpAllocFromNewBlock(), BumpAllocLarge(), GenerationAllocFromNewBlock(), GenerationAllocLarge(), GenerationRealloc(), and SlabAllocFromNewBlock().

◆ MemoryContextCheckSize()

static void MemoryContextCheckSize ( MemoryContext  context,
Size  size,
int  flags 
)
inlinestatic

Definition at line 167 of file memutils_internal.h.

168{
169 if (unlikely(!AllocSizeIsValid(size)))
170 {
171 if (!(flags & MCXT_ALLOC_HUGE) || !AllocHugeSizeIsValid(size))
172 MemoryContextSizeFailure(context, size, flags);
173 }
174}

References AllocHugeSizeIsValid, AllocSizeIsValid, MCXT_ALLOC_HUGE, MemoryContextSizeFailure(), and unlikely.

Referenced by AllocSetAllocLarge(), AllocSetRealloc(), BumpAllocLarge(), and GenerationAllocLarge().

◆ MemoryContextCreate()

void MemoryContextCreate ( MemoryContext  node,
NodeTag  tag,
MemoryContextMethodID  method_id,
MemoryContext  parent,
const char name 
)
extern

Definition at line 1149 of file mcxt.c.

1154{
1155 /* Creating new memory contexts is not allowed in a critical section */
1157
1158 /* Validate parent, to help prevent crazy context linkages */
1159 Assert(parent == NULL || MemoryContextIsValid(parent));
1160 Assert(node != parent);
1161
1162 /* Initialize all standard fields of memory context header */
1163 node->type = tag;
1164 node->isReset = true;
1165 node->methods = &mcxt_methods[method_id];
1166 node->parent = parent;
1167 node->firstchild = NULL;
1168 node->mem_allocated = 0;
1169 node->prevchild = NULL;
1170 node->name = name;
1171 node->ident = NULL;
1172 node->reset_cbs = NULL;
1173
1174 /* OK to link node into context tree */
1175 if (parent)
1176 {
1177 node->nextchild = parent->firstchild;
1178 if (parent->firstchild != NULL)
1179 parent->firstchild->prevchild = node;
1180 parent->firstchild = node;
1181 /* inherit allowInCritSection flag from parent */
1182 node->allowInCritSection = parent->allowInCritSection;
1183 }
1184 else
1185 {
1186 node->nextchild = NULL;
1187 node->allowInCritSection = false;
1188 }
1189}
volatile uint32 CritSectionCount
Definition globals.c:45
static const MemoryContextMethods mcxt_methods[]
Definition mcxt.c:63
#define MemoryContextIsValid(context)
Definition memnodes.h:145
MemoryContext prevchild
Definition memnodes.h:129
MemoryContext firstchild
Definition memnodes.h:128
bool allowInCritSection
Definition memnodes.h:124
const char * ident
Definition memnodes.h:132
MemoryContext parent
Definition memnodes.h:127
MemoryContextCallback * reset_cbs
Definition memnodes.h:133
const MemoryContextMethods * methods
Definition memnodes.h:126

References MemoryContextData::allowInCritSection, Assert, CritSectionCount, fb(), MemoryContextData::firstchild, MemoryContextData::ident, MemoryContextData::isReset, mcxt_methods, MemoryContextData::mem_allocated, MemoryContextIsValid, MemoryContextData::methods, name, MemoryContextData::name, MemoryContextData::nextchild, MemoryContextData::parent, MemoryContextData::prevchild, and MemoryContextData::reset_cbs.

Referenced by AllocSetContextCreateInternal(), BumpContextCreate(), GenerationContextCreate(), and SlabContextCreate().

◆ MemoryContextSizeFailure()

pg_noreturn void MemoryContextSizeFailure ( MemoryContext  context,
Size  size,
int  flags 
)
extern

Definition at line 1219 of file mcxt.c.

1220{
1221 elog(ERROR, "invalid memory alloc request size %zu", size);
1222}

References elog, and ERROR.

Referenced by MemoryContextCheckSize().

◆ SlabAlloc()

void * SlabAlloc ( MemoryContext  context,
Size  size,
int  flags 
)
extern

Definition at line 659 of file slab.c.

660{
661 SlabContext *slab = (SlabContext *) context;
662 SlabBlock *block;
663 MemoryChunk *chunk;
664
665 Assert(SlabIsValid(slab));
666
667 /* sanity check that this is pointing to a valid blocklist */
668 Assert(slab->curBlocklistIndex >= 0);
670
671 /*
672 * Make sure we only allow correct request size. This doubles as the
673 * MemoryContextCheckSize check.
674 */
675 if (unlikely(size != slab->chunkSize))
676 SlabAllocInvalidSize(context, size);
677
678 if (unlikely(slab->curBlocklistIndex == 0))
679 {
680 /*
681 * Handle the case when there are no partially filled blocks
682 * available. This happens either when the last allocation took the
683 * last chunk in the block, or when SlabFree() free'd the final block.
684 */
685 return SlabAllocFromNewBlock(context, size, flags);
686 }
687 else
688 {
689 dlist_head *blocklist = &slab->blocklist[slab->curBlocklistIndex];
691
692 Assert(!dlist_is_empty(blocklist));
693
694 /* grab the block from the blocklist */
695 block = dlist_head_element(SlabBlock, node, blocklist);
696
697 /* make sure we actually got a valid block, with matching nfree */
698 Assert(block != NULL);
699 Assert(slab->curBlocklistIndex == SlabBlocklistIndex(slab, block->nfree));
700 Assert(block->nfree > 0);
701
702 /* fetch the next chunk from this block */
703 chunk = SlabGetNextFreeChunk(slab, block);
704
705 /* get the new blocklist index based on the new free chunk count */
707
708 /*
709 * Handle the case where the blocklist index changes. This also deals
710 * with blocks becoming full as only full blocks go at index 0.
711 */
713 {
714 dlist_delete_from(blocklist, &block->node);
716
717 if (dlist_is_empty(blocklist))
719 }
720 }
721
722 return SlabAllocSetupNewChunk(context, block, chunk, size);
723}
static void dlist_delete_from(dlist_head *head, dlist_node *node)
Definition ilist.h:429
#define dlist_head_element(type, membername, lhead)
Definition ilist.h:603
static void dlist_push_head(dlist_head *head, dlist_node *node)
Definition ilist.h:347
static pg_noinline void * SlabAllocFromNewBlock(MemoryContext context, Size size, int flags)
Definition slab.c:565
#define SlabIsValid(set)
Definition slab.c:196
static MemoryChunk * SlabGetNextFreeChunk(SlabContext *slab, SlabBlock *block)
Definition slab.c:271
static int32 SlabBlocklistIndex(SlabContext *slab, int nfree)
Definition slab.c:211
static void * SlabAllocSetupNewChunk(MemoryContext context, SlabBlock *block, MemoryChunk *chunk, Size size)
Definition slab.c:523
static int32 SlabFindNextBlockListIndex(SlabContext *slab)
Definition slab.c:251
pg_noreturn static pg_noinline void SlabAllocInvalidSize(MemoryContext context, Size size)
Definition slab.c:635
int32 nfree
Definition slab.c:149
dlist_node node
Definition slab.c:153
dlist_head blocklist[SLAB_BLOCKLIST_COUNT]
Definition slab.c:129
int32 chunksPerBlock
Definition slab.c:110
int32 curBlocklistIndex
Definition slab.c:111
uint32 chunkSize
Definition slab.c:107

References Assert, SlabContext::blocklist, SlabContext::chunkSize, SlabContext::chunksPerBlock, SlabContext::curBlocklistIndex, dlist_delete_from(), dlist_head_element, dlist_is_empty(), dlist_push_head(), fb(), SlabBlock::nfree, SlabBlock::node, SlabAllocFromNewBlock(), SlabAllocInvalidSize(), SlabAllocSetupNewChunk(), SlabBlocklistIndex(), SlabFindNextBlockListIndex(), SlabGetNextFreeChunk(), SlabIsValid, and unlikely.

◆ SlabDelete()

void SlabDelete ( MemoryContext  context)
extern

Definition at line 506 of file slab.c.

507{
508 /* Reset to release all the SlabBlocks */
509 SlabReset(context);
510
511 /* Destroy the vpool -- see notes in aset.c */
513
514 /* And free the context header */
515 free(context);
516}
void SlabReset(MemoryContext context)
Definition slab.c:436

References free, SlabReset(), and VALGRIND_DESTROY_MEMPOOL.

◆ SlabFree()

void SlabFree ( void pointer)
extern

Definition at line 730 of file slab.c.

731{
732 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
733 SlabBlock *block;
734 SlabContext *slab;
735 int curBlocklistIdx;
736 int newBlocklistIdx;
737
738 /* Allow access to the chunk header. */
740
741 block = MemoryChunkGetBlock(chunk);
742
743 /*
744 * For speed reasons we just Assert that the referenced block is good.
745 * Future field experience may show that this Assert had better become a
746 * regular runtime test-and-elog check.
747 */
748 Assert(SlabBlockIsValid(block));
749 slab = block->slab;
750
751#ifdef MEMORY_CONTEXT_CHECKING
752 /* See comments in AllocSetFree about uses of ERROR and WARNING here */
753 /* Test for previously-freed chunk */
754 if (unlikely(chunk->requested_size == InvalidAllocSize))
755 elog(ERROR, "detected double pfree in %s %p",
756 slab->header.name, chunk);
757 /* Test for someone scribbling on unused space in chunk */
759 if (!sentinel_ok(pointer, slab->chunkSize))
760 elog(WARNING, "detected write past chunk end in %s %p",
761 slab->header.name, chunk);
762 /* Reset requested_size to InvalidAllocSize in free chunks */
763 chunk->requested_size = InvalidAllocSize;
764#endif
765
766 /* push this chunk onto the head of the block's free list */
767 *(MemoryChunk **) pointer = block->freehead;
768 block->freehead = chunk;
769
770 block->nfree++;
771
772 Assert(block->nfree > 0);
773 Assert(block->nfree <= slab->chunksPerBlock);
774
775#ifdef CLOBBER_FREED_MEMORY
776 /* don't wipe the free list MemoryChunk pointer stored in the chunk */
777 wipe_mem((char *) pointer + sizeof(MemoryChunk *),
778 slab->chunkSize - sizeof(MemoryChunk *));
779#endif
780
781 curBlocklistIdx = SlabBlocklistIndex(slab, block->nfree - 1);
783
784 /*
785 * Check if the block needs to be moved to another element on the
786 * blocklist based on it now having 1 more free chunk.
787 */
789 {
790 /* do the move */
793
794 /*
795 * The blocklist[curBlocklistIdx] may now be empty or we may now be
796 * able to use a lower-element blocklist. We'll need to redetermine
797 * what the slab->curBlocklistIndex is if the current blocklist was
798 * changed or if a lower element one was changed. We must ensure we
799 * use the list with the fullest block(s).
800 */
802 {
804
805 /*
806 * We know there must be a block with at least 1 unused chunk as
807 * we just pfree'd one. Ensure curBlocklistIndex reflects this.
808 */
809 Assert(slab->curBlocklistIndex > 0);
810 }
811 }
812
813 /* Handle when a block becomes completely empty */
814 if (unlikely(block->nfree == slab->chunksPerBlock))
815 {
816 /* remove the block */
818
819 /*
820 * To avoid thrashing malloc/free, we keep a list of empty blocks that
821 * we can reuse again instead of having to malloc a new one.
822 */
824 dclist_push_head(&slab->emptyblocks, &block->node);
825 else
826 {
827 /*
828 * When we have enough empty blocks stored already, we actually
829 * free the block.
830 */
831#ifdef CLOBBER_FREED_MEMORY
832 wipe_mem(block, slab->blockSize);
833#endif
834
835 /* As in aset.c, free block-header vchunks explicitly */
836 VALGRIND_MEMPOOL_FREE(slab, block);
837
838 free(block);
839 slab->header.mem_allocated -= slab->blockSize;
840 }
841
842 /*
843 * Check if we need to reset the blocklist index. This is required
844 * when the blocklist this block is on has become completely empty.
845 */
846 if (slab->curBlocklistIndex == newBlocklistIdx &&
849 }
850}
static uint32 dclist_count(const dclist_head *head)
Definition ilist.h:932
static void dclist_push_head(dclist_head *head, dlist_node *node)
Definition ilist.h:693
#define Slab_CHUNKHDRSZ
Definition slab.c:157
#define SlabBlockIsValid(block)
Definition slab.c:202
#define SLAB_MAXIMUM_EMPTY_BLOCKS
Definition slab.c:98
MemoryChunk * freehead
Definition slab.c:151
SlabContext * slab
Definition slab.c:148
uint32 fullChunkSize
Definition slab.c:108
MemoryContextData header
Definition slab.c:105
uint32 blockSize
Definition slab.c:109
dclist_head emptyblocks
Definition slab.c:120

References Assert, SlabContext::blocklist, SlabContext::blockSize, SlabContext::chunkSize, SlabContext::chunksPerBlock, SlabContext::curBlocklistIndex, dclist_count(), dclist_push_head(), dlist_delete_from(), dlist_is_empty(), dlist_push_head(), elog, SlabContext::emptyblocks, ERROR, fb(), free, SlabBlock::freehead, SlabContext::fullChunkSize, SlabContext::header, InvalidAllocSize, MemoryContextData::mem_allocated, MemoryChunkGetBlock(), MemoryContextData::name, SlabBlock::nfree, SlabBlock::node, PointerGetMemoryChunk, SlabBlock::slab, Slab_CHUNKHDRSZ, SLAB_MAXIMUM_EMPTY_BLOCKS, SlabBlockIsValid, SlabBlocklistIndex(), SlabFindNextBlockListIndex(), unlikely, VALGRIND_MAKE_MEM_DEFINED, VALGRIND_MEMPOOL_FREE, and WARNING.

◆ SlabGetChunkContext()

MemoryContext SlabGetChunkContext ( void pointer)
extern

Definition at line 903 of file slab.c.

904{
905 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
906 SlabBlock *block;
907
908 /* Allow access to the chunk header. */
910
911 block = MemoryChunkGetBlock(chunk);
912
913 /* Disallow access to the chunk header. */
915
916 Assert(SlabBlockIsValid(block));
917
918 return &block->slab->header;
919}

References Assert, SlabContext::header, MemoryChunkGetBlock(), PointerGetMemoryChunk, SlabBlock::slab, Slab_CHUNKHDRSZ, SlabBlockIsValid, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ SlabGetChunkSpace()

Size SlabGetChunkSpace ( void pointer)
extern

Definition at line 927 of file slab.c.

928{
929 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
930 SlabBlock *block;
931 SlabContext *slab;
932
933 /* Allow access to the chunk header. */
935
936 block = MemoryChunkGetBlock(chunk);
937
938 /* Disallow access to the chunk header. */
940
941 Assert(SlabBlockIsValid(block));
942 slab = block->slab;
943
944 return slab->fullChunkSize;
945}

References Assert, SlabContext::fullChunkSize, MemoryChunkGetBlock(), PointerGetMemoryChunk, SlabBlock::slab, Slab_CHUNKHDRSZ, SlabBlockIsValid, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ SlabIsEmpty()

bool SlabIsEmpty ( MemoryContext  context)
extern

Definition at line 952 of file slab.c.

953{
954 Assert(SlabIsValid((SlabContext *) context));
955
956 return (context->mem_allocated == 0);
957}

References Assert, MemoryContextData::mem_allocated, and SlabIsValid.

◆ SlabRealloc()

void * SlabRealloc ( void pointer,
Size  size,
int  flags 
)
extern

Definition at line 866 of file slab.c.

867{
868 MemoryChunk *chunk = PointerGetMemoryChunk(pointer);
869 SlabBlock *block;
870 SlabContext *slab;
871
872 /* Allow access to the chunk header. */
874
875 block = MemoryChunkGetBlock(chunk);
876
877 /* Disallow access to the chunk header. */
879
880 /*
881 * Try to verify that we have a sane block pointer: the block header
882 * should reference a slab context. (We use a test-and-elog, not just
883 * Assert, because it seems highly likely that we're here in error in the
884 * first place.)
885 */
886 if (!SlabBlockIsValid(block))
887 elog(ERROR, "could not find block containing chunk %p", chunk);
888 slab = block->slab;
889
890 /* can't do actual realloc with slab, but let's try to be gentle */
891 if (size == slab->chunkSize)
892 return pointer;
893
894 elog(ERROR, "slab allocator does not support realloc()");
895 return NULL; /* keep compiler quiet */
896}

References SlabContext::chunkSize, elog, ERROR, fb(), MemoryChunkGetBlock(), PointerGetMemoryChunk, SlabBlock::slab, Slab_CHUNKHDRSZ, SlabBlockIsValid, VALGRIND_MAKE_MEM_DEFINED, and VALGRIND_MAKE_MEM_NOACCESS.

◆ SlabReset()

void SlabReset ( MemoryContext  context)
extern

Definition at line 436 of file slab.c.

437{
438 SlabContext *slab = (SlabContext *) context;
440 int i;
441
442 Assert(SlabIsValid(slab));
443
444#ifdef MEMORY_CONTEXT_CHECKING
445 /* Check for corruption and leaks before freeing */
446 SlabCheck(context);
447#endif
448
449 /* release any retained empty blocks */
451 {
452 SlabBlock *block = dlist_container(SlabBlock, node, miter.cur);
453
455
456#ifdef CLOBBER_FREED_MEMORY
457 wipe_mem(block, slab->blockSize);
458#endif
459
460 /* As in aset.c, free block-header vchunks explicitly */
461 VALGRIND_MEMPOOL_FREE(slab, block);
462
463 free(block);
464 context->mem_allocated -= slab->blockSize;
465 }
466
467 /* walk over blocklist and free the blocks */
468 for (i = 0; i < SLAB_BLOCKLIST_COUNT; i++)
469 {
471 {
472 SlabBlock *block = dlist_container(SlabBlock, node, miter.cur);
473
474 dlist_delete(miter.cur);
475
476#ifdef CLOBBER_FREED_MEMORY
477 wipe_mem(block, slab->blockSize);
478#endif
479
480 /* As in aset.c, free block-header vchunks explicitly */
481 VALGRIND_MEMPOOL_FREE(slab, block);
482
483 free(block);
484 context->mem_allocated -= slab->blockSize;
485 }
486 }
487
488 /*
489 * Instruct Valgrind to throw away all the vchunks associated with this
490 * context, except for the one covering the SlabContext. This gets rid of
491 * the vchunks for whatever user data is getting discarded by the context
492 * reset.
493 */
494 VALGRIND_MEMPOOL_TRIM(slab, slab, sizeof(SlabContext));
495
496 slab->curBlocklistIndex = 0;
497
498 Assert(context->mem_allocated == 0);
499}
static void dlist_delete(dlist_node *node)
Definition ilist.h:405
static void dclist_delete_from(dclist_head *head, dlist_node *node)
Definition ilist.h:763
#define dclist_foreach_modify(iter, lhead)
Definition ilist.h:973
int i
Definition isn.c:77
#define SLAB_BLOCKLIST_COUNT
Definition slab.c:95

References Assert, SlabContext::blocklist, SlabContext::blockSize, SlabContext::curBlocklistIndex, dclist_delete_from(), dclist_foreach_modify, dlist_container, dlist_delete(), dlist_foreach_modify, SlabContext::emptyblocks, fb(), free, i, MemoryContextData::mem_allocated, SLAB_BLOCKLIST_COUNT, SlabIsValid, VALGRIND_MEMPOOL_FREE, and VALGRIND_MEMPOOL_TRIM.

Referenced by SlabDelete().

◆ SlabStats()

void SlabStats ( MemoryContext  context,
MemoryStatsPrintFunc  printfunc,
void passthru,
MemoryContextCounters totals,
bool  print_to_stderr 
)
extern

Definition at line 969 of file slab.c.

973{
974 SlabContext *slab = (SlabContext *) context;
975 Size nblocks = 0;
976 Size freechunks = 0;
977 Size totalspace;
978 Size freespace = 0;
979 int i;
980
981 Assert(SlabIsValid(slab));
982
983 /* Include context header in totalspace */
984 totalspace = Slab_CONTEXT_HDRSZ(slab->chunksPerBlock);
985
986 /* Add the space consumed by blocks in the emptyblocks list */
987 totalspace += dclist_count(&slab->emptyblocks) * slab->blockSize;
988
989 for (i = 0; i < SLAB_BLOCKLIST_COUNT; i++)
990 {
991 dlist_iter iter;
992
993 dlist_foreach(iter, &slab->blocklist[i])
994 {
995 SlabBlock *block = dlist_container(SlabBlock, node, iter.cur);
996
997 nblocks++;
998 totalspace += slab->blockSize;
999 freespace += slab->fullChunkSize * block->nfree;
1000 freechunks += block->nfree;
1001 }
1002 }
1003
1004 if (printfunc)
1005 {
1006 char stats_string[200];
1007
1008 /* XXX should we include free chunks on empty blocks? */
1010 "%zu total in %zu blocks; %u empty blocks; %zu free (%zu chunks); %zu used",
1011 totalspace, nblocks, dclist_count(&slab->emptyblocks),
1012 freespace, freechunks, totalspace - freespace);
1014 }
1015
1016 if (totals)
1017 {
1018 totals->nblocks += nblocks;
1019 totals->freechunks += freechunks;
1020 totals->totalspace += totalspace;
1021 totals->freespace += freespace;
1022 }
1023}
#define Slab_CONTEXT_HDRSZ(chunksPerBlock)
Definition slab.c:88

References Assert, SlabContext::blocklist, SlabContext::blockSize, SlabContext::chunksPerBlock, dlist_iter::cur, dclist_count(), dlist_container, dlist_foreach, SlabContext::emptyblocks, fb(), SlabContext::fullChunkSize, i, SlabBlock::nfree, SLAB_BLOCKLIST_COUNT, Slab_CONTEXT_HDRSZ, SlabIsValid, and snprintf.