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