PostgreSQL Source Code git master
Loading...
Searching...
No Matches
pg_buffercache_pages.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * pg_buffercache_pages.c
4 * display some contents of the buffer cache
5 *
6 * contrib/pg_buffercache/pg_buffercache_pages.c
7 *-------------------------------------------------------------------------
8 */
9#include "postgres.h"
10
11#include "access/htup_details.h"
12#include "access/relation.h"
13#include "catalog/pg_type.h"
14#include "funcapi.h"
15#include "port/pg_numa.h"
17#include "storage/bufmgr.h"
18#include "utils/rel.h"
19#include "utils/tuplestore.h"
20
21
22#define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8
23#define NUM_BUFFERCACHE_PAGES_ELEM 9
24#define NUM_BUFFERCACHE_SUMMARY_ELEM 5
25#define NUM_BUFFERCACHE_USAGE_COUNTS_ELEM 4
26#define NUM_BUFFERCACHE_EVICT_ELEM 2
27#define NUM_BUFFERCACHE_EVICT_RELATION_ELEM 3
28#define NUM_BUFFERCACHE_EVICT_ALL_ELEM 3
29#define NUM_BUFFERCACHE_MARK_DIRTY_ELEM 2
30#define NUM_BUFFERCACHE_MARK_DIRTY_RELATION_ELEM 3
31#define NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM 3
32
33#define NUM_BUFFERCACHE_OS_PAGES_ELEM 3
34
36 .name = "pg_buffercache",
37 .version = PG_VERSION
38);
39
40/*
41 * Record structure holding the to be exposed cache data for OS pages. This
42 * structure is used by pg_buffercache_os_pages(), where NUMA information may
43 * or may not be included.
44 */
51
52/*
53 * Function context for data persisting over repeated calls.
54 */
61
63
64
65/*
66 * Function returning data from the shared buffer cache - buffer number,
67 * relation node/tablespace/database/blocknum and dirty indicator.
68 */
80
81
82/* Only need to touch memory once per backend process lifetime */
83static bool firstNumaTouch = true;
84
85
88{
89 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
92 MemoryContext oldcontext;
93 int i;
94
95 /*
96 * To smoothly support upgrades from version 1.0 of this extension
97 * transparently handle the (non-)existence of the pinning_backends
98 * column. We unfortunately have to get the result type for that... - we
99 * can't use the result type determined by the function definition without
100 * potentially crashing when somebody uses the old (or even wrong)
101 * function definition though.
102 */
104 elog(ERROR, "return type must be a row type");
105
108 elog(ERROR, "incorrect number of output arguments");
109
110 InitMaterializedSRF(fcinfo, 0);
111
112 oldcontext = MemoryContextSwitchTo(rsinfo->econtext->ecxt_per_query_memory);
114 MemoryContextSwitchTo(oldcontext);
115
116 /*
117 * Override the caller-supplied descriptor with the tuple descriptor that
118 * matches the values we actually return, so executor-side
119 * tupledesc_match() can verify the caller's row definition.
120 *
121 * Do not free the previous rsinfo->setDesc here: for RECORD results it
122 * can alias rsinfo->expectedDesc, which the executor still needs to
123 * reference.
124 */
125 rsinfo->setDesc = actual_tupledesc;
126
127 /*
128 * Scan through all the buffers, adding one row for each of the buffers to
129 * the tuplestore.
130 *
131 * We don't hold the partition locks, so we don't get a consistent
132 * snapshot across all buffers, but we do grab the buffer header locks, so
133 * the information of each buffer is self-consistent.
134 */
135 for (i = 0; i < NBuffers; i++)
136 {
139 uint32 bufferid;
140 RelFileNumber relfilenumber;
141 Oid reltablespace;
143 ForkNumber forknum;
144 BlockNumber blocknum;
145 bool isvalid;
146 bool isdirty;
150 bool nulls[NUM_BUFFERCACHE_PAGES_ELEM];
151
153
155 /* Lock each buffer header before inspecting. */
157
159 relfilenumber = BufTagGetRelNumber(&bufHdr->tag);
160 reltablespace = bufHdr->tag.spcOid;
161 reldatabase = bufHdr->tag.dbOid;
162 forknum = BufTagGetForkNum(&bufHdr->tag);
163 blocknum = bufHdr->tag.blockNum;
166
167 if (buf_state & BM_DIRTY)
168 isdirty = true;
169 else
170 isdirty = false;
171
172 /* Note if the buffer is valid, and has storage created */
174 isvalid = true;
175 else
176 isvalid = false;
177
179
180 /* Build the tuple and add it to tuplestore */
181 values[0] = Int32GetDatum(bufferid);
182 nulls[0] = false;
183
184 /*
185 * Set all fields except the bufferid to null if the buffer is unused
186 * or not valid.
187 */
188 if (blocknum == InvalidBlockNumber || isvalid == false)
189 {
190 nulls[1] = true;
191 nulls[2] = true;
192 nulls[3] = true;
193 nulls[4] = true;
194 nulls[5] = true;
195 nulls[6] = true;
196 nulls[7] = true;
197 /* unused for v1.0 callers, but the array is always long enough */
198 nulls[8] = true;
199 }
200 else
201 {
202 values[1] = ObjectIdGetDatum(relfilenumber);
203 nulls[1] = false;
204 values[2] = ObjectIdGetDatum(reltablespace);
205 nulls[2] = false;
207 nulls[3] = false;
208 values[4] = Int16GetDatum(forknum);
209 nulls[4] = false;
210 values[5] = Int64GetDatum((int64) blocknum);
211 nulls[5] = false;
213 nulls[6] = false;
215 nulls[7] = false;
216 /* unused for v1.0 callers, but the array is always long enough */
218 nulls[8] = false;
219 }
220
221 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
222 }
223
224 return (Datum) 0;
225}
226
227static TupleDesc
229{
230 TupleDesc tupledesc;
231
232 tupledesc = CreateTemplateTupleDesc(natts);
233 TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
234 INT4OID, -1, 0);
235 TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
236 OIDOID, -1, 0);
237 TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
238 OIDOID, -1, 0);
239 TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
240 OIDOID, -1, 0);
241 TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relforknumber",
242 INT2OID, -1, 0);
243 TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relblocknumber",
244 INT8OID, -1, 0);
245 TupleDescInitEntry(tupledesc, (AttrNumber) 7, "isdirty",
246 BOOLOID, -1, 0);
247 TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usagecount",
248 INT2OID, -1, 0);
249
250 if (natts == NUM_BUFFERCACHE_PAGES_ELEM)
251 TupleDescInitEntry(tupledesc, (AttrNumber) 9, "pinning_backends",
252 INT4OID, -1, 0);
253
254 TupleDescFinalize(tupledesc);
255
256 return BlessTupleDesc(tupledesc);
257}
258
259/*
260 * Inquire about OS pages mappings for shared buffers, with NUMA information,
261 * optionally.
262 *
263 * When "include_numa" is false, this routines ignores everything related
264 * to NUMA (returned as NULL values), returning mapping information between
265 * shared buffers and OS pages.
266 *
267 * When "include_numa" is true, NUMA is initialized and numa_node values
268 * are generated. In order to get reliable results we also need to touch
269 * memory pages, so that the inquiry about NUMA memory node does not return
270 * -2, indicating unmapped/unallocated pages.
271 *
272 * Buffers may be smaller or larger than OS memory pages. For each buffer we
273 * return one entry for each memory page used by the buffer (if the buffer is
274 * smaller, it only uses a part of one memory page).
275 *
276 * We expect both sizes (for buffers and memory pages) to be a power-of-2, so
277 * one is always a multiple of the other.
278 *
279 */
280static Datum
282{
284 MemoryContext oldcontext;
285 BufferCacheOsPagesContext *fctx; /* User function context. */
286 TupleDesc tupledesc;
288 HeapTuple tuple;
290
291 if (SRF_IS_FIRSTCALL())
292 {
293 int i,
294 idx;
297 int *os_page_status = NULL;
299 int max_entries;
300 char *startptr,
301 *endptr;
302
303 /* If NUMA information is requested, initialize NUMA support. */
304 if (include_numa && pg_numa_init() == -1)
305 elog(ERROR, "libnuma initialization failed or NUMA is not supported on this platform");
306
307 /*
308 * The database block size and OS memory page size are unlikely to be
309 * the same. The block size is 1-32KB, the memory page size depends on
310 * platform. On x86 it's usually 4KB, on ARM it's 4KB or 64KB, but
311 * there are also features like THP etc. Moreover, we don't quite know
312 * how the pages and buffers "align" in memory - the buffers may be
313 * shifted in some way, using more memory pages than necessary.
314 *
315 * So we need to be careful about mapping buffers to memory pages. We
316 * calculate the maximum number of pages a buffer might use, so that
317 * we allocate enough space for the entries. And then we count the
318 * actual number of entries as we scan the buffers.
319 *
320 * This information is needed before calling move_pages() for NUMA
321 * node id inquiry.
322 */
324
325 /*
326 * The pages and block size is expected to be 2^k, so one divides the
327 * other (we don't know in which direction). This does not say
328 * anything about relative alignment of pages/buffers.
329 */
330 Assert((os_page_size % BLCKSZ == 0) || (BLCKSZ % os_page_size == 0));
331
332 if (include_numa)
333 {
334 void **os_page_ptrs = NULL;
335
336 /*
337 * How many addresses we are going to query? Simply get the page
338 * for the first buffer, and first page after the last buffer, and
339 * count the pages from that.
340 */
341 startptr = (char *) TYPEALIGN_DOWN(os_page_size,
342 BufferGetBlock(1));
343 endptr = (char *) TYPEALIGN(os_page_size,
344 (char *) BufferGetBlock(NBuffers) + BLCKSZ);
345 os_page_count = (endptr - startptr) / os_page_size;
346
347 /* Used to determine the NUMA node for all OS pages at once */
350
351 /*
352 * Fill pointers for all the memory pages. This loop stores and
353 * touches (if needed) addresses into os_page_ptrs[] as input to
354 * one big move_pages(2) inquiry system call, as done in
355 * pg_numa_query_pages().
356 */
357 idx = 0;
358 for (char *ptr = startptr; ptr < endptr; ptr += os_page_size)
359 {
360 os_page_ptrs[idx++] = ptr;
361
362 /* Only need to touch memory once per backend process lifetime */
363 if (firstNumaTouch)
365 }
366
368
369 elog(DEBUG1, "NUMA: NBuffers=%d os_page_count=" UINT64_FORMAT " "
370 "os_page_size=%zu", NBuffers, os_page_count, os_page_size);
371
372 /*
373 * If we ever get 0xff back from kernel inquiry, then we probably
374 * have bug in our buffers to OS page mapping code here.
375 */
376 memset(os_page_status, 0xff, sizeof(int) * os_page_count);
377
378 /* Query NUMA status for all the pointers */
380 elog(ERROR, "failed NUMA pages inquiry: %m");
381 }
382
383 /* Initialize the multi-call context, load entries about buffers */
384
386
387 /* Switch context when allocating stuff to be used in later calls */
388 oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
389
390 /* Create a user function context for cross-call persistence */
392
394 elog(ERROR, "return type must be a row type");
395
397 elog(ERROR, "incorrect number of output arguments");
398
399 /* Construct a tuple descriptor for the result rows. */
401 TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
402 INT4OID, -1, 0);
403 TupleDescInitEntry(tupledesc, (AttrNumber) 2, "os_page_num",
404 INT8OID, -1, 0);
405 TupleDescInitEntry(tupledesc, (AttrNumber) 3, "numa_node",
406 INT4OID, -1, 0);
407
408 TupleDescFinalize(tupledesc);
409 fctx->tupdesc = BlessTupleDesc(tupledesc);
410 fctx->include_numa = include_numa;
411
412 /*
413 * Each buffer needs at least one entry, but it might be offset in
414 * some way, and use one extra entry. So we allocate space for the
415 * maximum number of entries we might need, and then count the exact
416 * number as we're walking buffers. That way we can do it in one pass,
417 * without reallocating memory.
418 */
421
422 /* Allocate entries for BufferCacheOsPagesRec records. */
423 fctx->record = (BufferCacheOsPagesRec *)
426
427 /* Return to original context when allocating transient memory */
428 MemoryContextSwitchTo(oldcontext);
429
430 if (include_numa && firstNumaTouch)
431 elog(DEBUG1, "NUMA: page-faulting the buffercache for proper NUMA readouts");
432
433 /*
434 * Scan through all the buffers, saving the relevant fields in the
435 * fctx->record structure.
436 *
437 * We don't hold the partition locks, so we don't get a consistent
438 * snapshot across all buffers, but we do grab the buffer header
439 * locks, so the information of each buffer is self-consistent.
440 */
441 startptr = (char *) TYPEALIGN_DOWN(os_page_size, (char *) BufferGetBlock(1));
442 idx = 0;
443 for (i = 0; i < NBuffers; i++)
444 {
445 char *buffptr = (char *) BufferGetBlock(i + 1);
447 uint32 bufferid;
448 int32 page_num;
449 char *startptr_buff,
451
453
455
456 /* Lock each buffer header before inspecting. */
460
461 /* start of the first page of this buffer */
463
464 /* end of the buffer (no need to align to memory page) */
466
468
469 /* calculate ID of the first page for this buffer */
470 page_num = (startptr_buff - startptr) / os_page_size;
471
472 /* Add an entry for each OS page overlapping with this buffer. */
473 for (char *ptr = startptr_buff; ptr < endptr_buff; ptr += os_page_size)
474 {
475 fctx->record[idx].bufferid = bufferid;
476 fctx->record[idx].page_num = page_num;
477 fctx->record[idx].numa_node = include_numa ? os_page_status[page_num] : -1;
478
479 /* advance to the next entry/page */
480 ++idx;
481 ++page_num;
482 }
483 }
484
486
487 if (include_numa)
489
490 /* Set max calls and remember the user function context. */
491 funcctx->max_calls = idx;
492 funcctx->user_fctx = fctx;
493
494 /* Remember this backend touched the pages (only relevant for NUMA) */
495 if (include_numa)
496 firstNumaTouch = false;
497 }
498
500
501 /* Get the saved state */
502 fctx = funcctx->user_fctx;
503
504 if (funcctx->call_cntr < funcctx->max_calls)
505 {
506 uint32 i = funcctx->call_cntr;
509
510 values[0] = Int32GetDatum(fctx->record[i].bufferid);
511 nulls[0] = false;
512
513 values[1] = Int64GetDatum(fctx->record[i].page_num);
514 nulls[1] = false;
515
516 if (fctx->include_numa)
517 {
518 /* status is valid node number */
519 if (fctx->record[i].numa_node >= 0)
520 {
521 values[2] = Int32GetDatum(fctx->record[i].numa_node);
522 nulls[2] = false;
523 }
524 else
525 {
526 /* some kind of error (e.g. pages moved to swap) */
527 values[2] = (Datum) 0;
528 nulls[2] = true;
529 }
530 }
531 else
532 {
533 values[2] = (Datum) 0;
534 nulls[2] = true;
535 }
536
537 /* Build and return the tuple. */
538 tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
539 result = HeapTupleGetDatum(tuple);
540
542 }
543 else
545}
546
547/*
548 * pg_buffercache_os_pages
549 *
550 * Retrieve information about OS pages, with or without NUMA information.
551 */
552Datum
554{
555 bool include_numa;
556
557 /* Get the boolean parameter that controls the NUMA behavior. */
558 include_numa = PG_GETARG_BOOL(0);
559
560 return pg_buffercache_os_pages_internal(fcinfo, include_numa);
561}
562
563/* Backward-compatible wrapper for v1.6. */
564Datum
566{
567 /* Call internal function with include_numa=true */
568 return pg_buffercache_os_pages_internal(fcinfo, true);
569}
570
571Datum
573{
575 TupleDesc tupledesc;
576 HeapTuple tuple;
579
585
586 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
587 elog(ERROR, "return type must be a row type");
588
589 for (int i = 0; i < NBuffers; i++)
590 {
593
595
596 /*
597 * This function summarizes the state of all headers. Locking the
598 * buffer headers wouldn't provide an improved result as the state of
599 * the buffer can still change after we release the lock and it'd
600 * noticeably increase the cost of the function.
601 */
604
605 if (buf_state & BM_VALID)
606 {
607 buffers_used++;
609
610 if (buf_state & BM_DIRTY)
612 }
613 else
615
618 }
619
620 memset(nulls, 0, sizeof(nulls));
625
626 if (buffers_used != 0)
628 else
629 nulls[4] = true;
630
631 /* Build and return the tuple. */
632 tuple = heap_form_tuple(tupledesc, values, nulls);
633 result = HeapTupleGetDatum(tuple);
634
636}
637
638Datum
640{
641 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
642 int usage_counts[BM_MAX_USAGE_COUNT + 1] = {0};
643 int dirty[BM_MAX_USAGE_COUNT + 1] = {0};
644 int pinned[BM_MAX_USAGE_COUNT + 1] = {0};
646 bool nulls[NUM_BUFFERCACHE_USAGE_COUNTS_ELEM] = {0};
647
648 InitMaterializedSRF(fcinfo, 0);
649
650 for (int i = 0; i < NBuffers; i++)
651 {
654 int usage_count;
655
657
660
661 if (buf_state & BM_DIRTY)
662 dirty[usage_count]++;
663
665 pinned[usage_count]++;
666 }
667
668 for (int i = 0; i < BM_MAX_USAGE_COUNT + 1; i++)
669 {
670 values[0] = Int32GetDatum(i);
672 values[2] = Int32GetDatum(dirty[i]);
673 values[3] = Int32GetDatum(pinned[i]);
674
675 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
676 }
677
678 return (Datum) 0;
679}
680
681/*
682 * Helper function to check if the user has superuser privileges.
683 */
684static void
686{
687 if (!superuser())
690 errmsg("must be superuser to use %s()",
691 func_name)));
692}
693
694/*
695 * Try to evict a shared buffer.
696 */
697Datum
699{
701 TupleDesc tupledesc;
702 HeapTuple tuple;
704 bool nulls[NUM_BUFFERCACHE_EVICT_ELEM] = {0};
705
707 bool buffer_flushed;
708
709 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
710 elog(ERROR, "return type must be a row type");
711
712 pg_buffercache_superuser_check("pg_buffercache_evict");
713
715 elog(ERROR, "bad buffer ID: %d", buf);
716
719
720 tuple = heap_form_tuple(tupledesc, values, nulls);
721 result = HeapTupleGetDatum(tuple);
722
724}
725
726/*
727 * Try to evict specified relation.
728 */
729Datum
731{
733 TupleDesc tupledesc;
734 HeapTuple tuple;
736 bool nulls[NUM_BUFFERCACHE_EVICT_RELATION_ELEM] = {0};
737
738 Oid relOid;
739 Relation rel;
740
744
745 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
746 elog(ERROR, "return type must be a row type");
747
748 pg_buffercache_superuser_check("pg_buffercache_evict_relation");
749
750 relOid = PG_GETARG_OID(0);
751
752 rel = relation_open(relOid, AccessShareLock);
753
757 errmsg("relation uses local buffers, %s() is intended to be used for shared buffers only",
758 "pg_buffercache_evict_relation")));
759
762
764
768
769 tuple = heap_form_tuple(tupledesc, values, nulls);
770 result = HeapTupleGetDatum(tuple);
771
773}
774
775
776/*
777 * Try to evict all shared buffers.
778 */
779Datum
781{
783 TupleDesc tupledesc;
784 HeapTuple tuple;
786 bool nulls[NUM_BUFFERCACHE_EVICT_ALL_ELEM] = {0};
787
791
792 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
793 elog(ERROR, "return type must be a row type");
794
795 pg_buffercache_superuser_check("pg_buffercache_evict_all");
796
799
803
804 tuple = heap_form_tuple(tupledesc, values, nulls);
805 result = HeapTupleGetDatum(tuple);
806
808}
809
810/*
811 * Try to mark a shared buffer as dirty.
812 */
813Datum
815{
816
818 TupleDesc tupledesc;
819 HeapTuple tuple;
821 bool nulls[NUM_BUFFERCACHE_MARK_DIRTY_ELEM] = {0};
822
825
826 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
827 elog(ERROR, "return type must be a row type");
828
829 pg_buffercache_superuser_check("pg_buffercache_mark_dirty");
830
832 elog(ERROR, "bad buffer ID: %d", buf);
833
836
837 tuple = heap_form_tuple(tupledesc, values, nulls);
838 result = HeapTupleGetDatum(tuple);
839
841}
842
843/*
844 * Try to mark all the shared buffers of a relation as dirty.
845 */
846Datum
848{
850 TupleDesc tupledesc;
851 HeapTuple tuple;
854
855 Oid relOid;
856 Relation rel;
857
861
862 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
863 elog(ERROR, "return type must be a row type");
864
865 pg_buffercache_superuser_check("pg_buffercache_mark_dirty_relation");
866
867 relOid = PG_GETARG_OID(0);
868
869 rel = relation_open(relOid, AccessShareLock);
870
874 errmsg("relation uses local buffers, %s() is intended to be used for shared buffers only",
875 "pg_buffercache_mark_dirty_relation")));
876
879
881
885
886 tuple = heap_form_tuple(tupledesc, values, nulls);
887 result = HeapTupleGetDatum(tuple);
888
890}
891
892/*
893 * Try to mark all the shared buffers as dirty.
894 */
895Datum
897{
899 TupleDesc tupledesc;
900 HeapTuple tuple;
902 bool nulls[NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM] = {0};
903
907
908 if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
909 elog(ERROR, "return type must be a row type");
910
911 pg_buffercache_superuser_check("pg_buffercache_mark_dirty_all");
912
915
919
920 tuple = heap_form_tuple(tupledesc, values, nulls);
921 result = HeapTupleGetDatum(tuple);
922
924}
Datum idx(PG_FUNCTION_ARGS)
Definition _int_op.c:263
static uint64 pg_atomic_read_u64(volatile pg_atomic_uint64 *ptr)
Definition atomics.h:467
int16 AttrNumber
Definition attnum.h:21
uint32 BlockNumber
Definition block.h:31
#define InvalidBlockNumber
Definition block.h:33
static Datum values[MAXATTR]
Definition bootstrap.c:190
int Buffer
Definition buf.h:23
#define BM_MAX_USAGE_COUNT
#define BM_TAG_VALID
static ForkNumber BufTagGetForkNum(const BufferTag *tag)
static RelFileNumber BufTagGetRelNumber(const BufferTag *tag)
static void UnlockBufHdr(BufferDesc *desc)
#define BM_DIRTY
#define BUF_STATE_GET_USAGECOUNT(state)
#define BUF_STATE_GET_REFCOUNT(state)
#define BM_VALID
static BufferDesc * GetBufferDescriptor(uint32 id)
static Buffer BufferDescriptorGetBuffer(const BufferDesc *bdesc)
void EvictAllUnpinnedBuffers(int32 *buffers_evicted, int32 *buffers_flushed, int32 *buffers_skipped)
Definition bufmgr.c:7991
void EvictRelUnpinnedBuffers(Relation rel, int32 *buffers_evicted, int32 *buffers_flushed, int32 *buffers_skipped)
Definition bufmgr.c:8041
uint64 LockBufHdr(BufferDesc *desc)
Definition bufmgr.c:7527
void MarkDirtyAllUnpinnedBuffers(int32 *buffers_dirtied, int32 *buffers_already_dirty, int32 *buffers_skipped)
Definition bufmgr.c:8241
bool MarkDirtyUnpinnedBuffer(Buffer buf, bool *buffer_already_dirty)
Definition bufmgr.c:8148
bool EvictUnpinnedBuffer(Buffer buf, bool *buffer_flushed)
Definition bufmgr.c:7962
void MarkDirtyRelUnpinnedBuffers(Relation rel, int32 *buffers_dirtied, int32 *buffers_already_dirty, int32 *buffers_skipped)
Definition bufmgr.c:8184
static Block BufferGetBlock(Buffer buffer)
Definition bufmgr.h:435
#define TYPEALIGN(ALIGNVAL, LEN)
Definition c.h:889
#define Max(x, y)
Definition c.h:1085
#define Assert(condition)
Definition c.h:943
int64_t int64
Definition c.h:621
#define UINT64_FORMAT
Definition c.h:635
int32_t int32
Definition c.h:620
uint64_t uint64
Definition c.h:625
uint16_t uint16
Definition c.h:623
uint32_t uint32
Definition c.h:624
size_t Size
Definition c.h:689
#define TYPEALIGN_DOWN(ALIGNVAL, LEN)
Definition c.h:901
uint32 result
int errcode(int sqlerrcode)
Definition elog.c:875
#define DEBUG1
Definition elog.h:31
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define ereport(elevel,...)
Definition elog.h:152
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
#define palloc_object(type)
Definition fe_memutils.h:89
#define palloc_array(type, count)
Definition fe_memutils.h:91
#define palloc0_array(type, count)
Definition fe_memutils.h:92
#define PG_GETARG_OID(n)
Definition fmgr.h:275
#define PG_MODULE_MAGIC_EXT(...)
Definition fmgr.h:540
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define PG_GETARG_INT32(n)
Definition fmgr.h:269
#define PG_GETARG_BOOL(n)
Definition fmgr.h:274
#define PG_RETURN_DATUM(x)
Definition fmgr.h:354
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
void InitMaterializedSRF(FunctionCallInfo fcinfo, uint32 flags)
Definition funcapi.c:76
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition funcapi.c:276
#define SRF_IS_FIRSTCALL()
Definition funcapi.h:304
#define SRF_PERCALL_SETUP()
Definition funcapi.h:308
@ TYPEFUNC_COMPOSITE
Definition funcapi.h:149
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition funcapi.h:310
#define SRF_FIRSTCALL_INIT()
Definition funcapi.h:306
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition funcapi.h:230
#define SRF_RETURN_DONE(_funcctx)
Definition funcapi.h:328
int NBuffers
Definition globals.c:144
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition heaptuple.c:1025
int i
Definition isn.c:77
#define AccessShareLock
Definition lockdefs.h:36
MemoryContext CurrentMemoryContext
Definition mcxt.c:161
void * MemoryContextAllocHuge(MemoryContext context, Size size)
Definition mcxt.c:1854
#define CHECK_FOR_INTERRUPTS()
Definition miscadmin.h:125
static char * errmsg
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
Datum pg_buffercache_os_pages(PG_FUNCTION_ARGS)
Datum pg_buffercache_evict_relation(PG_FUNCTION_ARGS)
#define NUM_BUFFERCACHE_USAGE_COUNTS_ELEM
#define NUM_BUFFERCACHE_OS_PAGES_ELEM
Datum pg_buffercache_evict(PG_FUNCTION_ARGS)
Datum pg_buffercache_mark_dirty_relation(PG_FUNCTION_ARGS)
Datum pg_buffercache_summary(PG_FUNCTION_ARGS)
static void pg_buffercache_superuser_check(char *func_name)
Datum pg_buffercache_usage_counts(PG_FUNCTION_ARGS)
#define NUM_BUFFERCACHE_SUMMARY_ELEM
Datum pg_buffercache_pages(PG_FUNCTION_ARGS)
#define NUM_BUFFERCACHE_EVICT_ELEM
#define NUM_BUFFERCACHE_PAGES_MIN_ELEM
#define NUM_BUFFERCACHE_EVICT_ALL_ELEM
Datum pg_buffercache_evict_all(PG_FUNCTION_ARGS)
#define NUM_BUFFERCACHE_MARK_DIRTY_RELATION_ELEM
#define NUM_BUFFERCACHE_PAGES_ELEM
#define NUM_BUFFERCACHE_MARK_DIRTY_ELEM
#define NUM_BUFFERCACHE_MARK_DIRTY_ALL_ELEM
Datum pg_buffercache_mark_dirty_all(PG_FUNCTION_ARGS)
Datum pg_buffercache_mark_dirty(PG_FUNCTION_ARGS)
static TupleDesc build_buffercache_pages_tupledesc(int natts)
Datum pg_buffercache_numa_pages(PG_FUNCTION_ARGS)
static bool firstNumaTouch
static Datum pg_buffercache_os_pages_internal(FunctionCallInfo fcinfo, bool include_numa)
#define NUM_BUFFERCACHE_EVICT_RELATION_ELEM
#define pg_numa_touch_mem_if_required(ptr)
Definition pg_numa.h:37
PGDLLIMPORT int pg_numa_query_pages(int pid, unsigned long count, void **pages, int *status)
Definition pg_numa.c:132
PGDLLIMPORT int pg_numa_init(void)
Definition pg_numa.c:125
static char buf[DEFAULT_XLOG_SEG_SIZE]
static Datum Int64GetDatum(int64 X)
Definition postgres.h:426
static Datum Int16GetDatum(int16 X)
Definition postgres.h:172
static Datum BoolGetDatum(bool X)
Definition postgres.h:112
static Datum ObjectIdGetDatum(Oid X)
Definition postgres.h:252
uint64_t Datum
Definition postgres.h:70
static Datum Float8GetDatum(float8 X)
Definition postgres.h:515
static Datum Int32GetDatum(int32 X)
Definition postgres.h:212
unsigned int Oid
static int fb(int x)
#define RelationUsesLocalBuffers(relation)
Definition rel.h:648
Oid RelFileNumber
Definition relpath.h:25
ForkNumber
Definition relpath.h:56
Size pg_get_shmem_pagesize(void)
Definition shmem.c:1273
void relation_close(Relation relation, LOCKMODE lockmode)
Definition relation.c:206
Relation relation_open(Oid relationId, LOCKMODE lockmode)
Definition relation.c:48
BufferCacheOsPagesRec * record
bool superuser(void)
Definition superuser.c:47
TupleDesc CreateTemplateTupleDesc(int natts)
Definition tupdesc.c:165
void TupleDescFinalize(TupleDesc tupdesc)
Definition tupdesc.c:511
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition tupdesc.c:909
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition tuplestore.c:785
const char * name