PostgreSQL Source Code  git master
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 "catalog/pg_type.h"
13 #include "funcapi.h"
14 #include "storage/buf_internals.h"
15 #include "storage/bufmgr.h"
16 
17 
18 #define NUM_BUFFERCACHE_PAGES_MIN_ELEM 8
19 #define NUM_BUFFERCACHE_PAGES_ELEM 9
20 #define NUM_BUFFERCACHE_SUMMARY_ELEM 5
21 
23 
24 /*
25  * Record structure holding the to be exposed cache data.
26  */
27 typedef struct
28 {
35  bool isvalid;
36  bool isdirty;
38 
39  /*
40  * An int32 is sufficiently large, as MAX_BACKENDS prevents a buffer from
41  * being pinned by too many backends and each backend will only pin once
42  * because of bufmgr.c's PrivateRefCount infrastructure.
43  */
46 
47 
48 /*
49  * Function context for data persisting over repeated calls.
50  */
51 typedef struct
52 {
56 
57 
58 /*
59  * Function returning data from the shared buffer cache - buffer number,
60  * relation node/tablespace/database/blocknum and dirty indicator.
61  */
64 
65 Datum
67 {
68  FuncCallContext *funcctx;
69  Datum result;
70  MemoryContext oldcontext;
71  BufferCachePagesContext *fctx; /* User function context. */
72  TupleDesc tupledesc;
73  TupleDesc expected_tupledesc;
74  HeapTuple tuple;
75 
76  if (SRF_IS_FIRSTCALL())
77  {
78  int i;
79 
80  funcctx = SRF_FIRSTCALL_INIT();
81 
82  /* Switch context when allocating stuff to be used in later calls */
83  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
84 
85  /* Create a user function context for cross-call persistence */
87 
88  /*
89  * To smoothly support upgrades from version 1.0 of this extension
90  * transparently handle the (non-)existence of the pinning_backends
91  * column. We unfortunately have to get the result type for that... -
92  * we can't use the result type determined by the function definition
93  * without potentially crashing when somebody uses the old (or even
94  * wrong) function definition though.
95  */
96  if (get_call_result_type(fcinfo, NULL, &expected_tupledesc) != TYPEFUNC_COMPOSITE)
97  elog(ERROR, "return type must be a row type");
98 
99  if (expected_tupledesc->natts < NUM_BUFFERCACHE_PAGES_MIN_ELEM ||
100  expected_tupledesc->natts > NUM_BUFFERCACHE_PAGES_ELEM)
101  elog(ERROR, "incorrect number of output arguments");
102 
103  /* Construct a tuple descriptor for the result rows. */
104  tupledesc = CreateTemplateTupleDesc(expected_tupledesc->natts);
105  TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
106  INT4OID, -1, 0);
107  TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
108  OIDOID, -1, 0);
109  TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
110  OIDOID, -1, 0);
111  TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
112  OIDOID, -1, 0);
113  TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relforknumber",
114  INT2OID, -1, 0);
115  TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relblocknumber",
116  INT8OID, -1, 0);
117  TupleDescInitEntry(tupledesc, (AttrNumber) 7, "isdirty",
118  BOOLOID, -1, 0);
119  TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count",
120  INT2OID, -1, 0);
121 
122  if (expected_tupledesc->natts == NUM_BUFFERCACHE_PAGES_ELEM)
123  TupleDescInitEntry(tupledesc, (AttrNumber) 9, "pinning_backends",
124  INT4OID, -1, 0);
125 
126  fctx->tupdesc = BlessTupleDesc(tupledesc);
127 
128  /* Allocate NBuffers worth of BufferCachePagesRec records. */
129  fctx->record = (BufferCachePagesRec *)
131  sizeof(BufferCachePagesRec) * NBuffers);
132 
133  /* Set max calls and remember the user function context. */
134  funcctx->max_calls = NBuffers;
135  funcctx->user_fctx = fctx;
136 
137  /* Return to original context when allocating transient memory */
138  MemoryContextSwitchTo(oldcontext);
139 
140  /*
141  * Scan through all the buffers, saving the relevant fields in the
142  * fctx->record structure.
143  *
144  * We don't hold the partition locks, so we don't get a consistent
145  * snapshot across all buffers, but we do grab the buffer header
146  * locks, so the information of each buffer is self-consistent.
147  */
148  for (i = 0; i < NBuffers; i++)
149  {
150  BufferDesc *bufHdr;
151  uint32 buf_state;
152 
153  bufHdr = GetBufferDescriptor(i);
154  /* Lock each buffer header before inspecting. */
155  buf_state = LockBufHdr(bufHdr);
156 
157  fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr);
158  fctx->record[i].relfilenumber = BufTagGetRelNumber(&bufHdr->tag);
159  fctx->record[i].reltablespace = bufHdr->tag.spcOid;
160  fctx->record[i].reldatabase = bufHdr->tag.dbOid;
161  fctx->record[i].forknum = BufTagGetForkNum(&bufHdr->tag);
162  fctx->record[i].blocknum = bufHdr->tag.blockNum;
163  fctx->record[i].usagecount = BUF_STATE_GET_USAGECOUNT(buf_state);
164  fctx->record[i].pinning_backends = BUF_STATE_GET_REFCOUNT(buf_state);
165 
166  if (buf_state & BM_DIRTY)
167  fctx->record[i].isdirty = true;
168  else
169  fctx->record[i].isdirty = false;
170 
171  /* Note if the buffer is valid, and has storage created */
172  if ((buf_state & BM_VALID) && (buf_state & BM_TAG_VALID))
173  fctx->record[i].isvalid = true;
174  else
175  fctx->record[i].isvalid = false;
176 
177  UnlockBufHdr(bufHdr, buf_state);
178  }
179  }
180 
181  funcctx = SRF_PERCALL_SETUP();
182 
183  /* Get the saved state */
184  fctx = funcctx->user_fctx;
185 
186  if (funcctx->call_cntr < funcctx->max_calls)
187  {
188  uint32 i = funcctx->call_cntr;
190  bool nulls[NUM_BUFFERCACHE_PAGES_ELEM];
191 
192  values[0] = Int32GetDatum(fctx->record[i].bufferid);
193  nulls[0] = false;
194 
195  /*
196  * Set all fields except the bufferid to null if the buffer is unused
197  * or not valid.
198  */
199  if (fctx->record[i].blocknum == InvalidBlockNumber ||
200  fctx->record[i].isvalid == false)
201  {
202  nulls[1] = true;
203  nulls[2] = true;
204  nulls[3] = true;
205  nulls[4] = true;
206  nulls[5] = true;
207  nulls[6] = true;
208  nulls[7] = true;
209  /* unused for v1.0 callers, but the array is always long enough */
210  nulls[8] = true;
211  }
212  else
213  {
215  nulls[1] = false;
217  nulls[2] = false;
219  nulls[3] = false;
220  values[4] = ObjectIdGetDatum(fctx->record[i].forknum);
221  nulls[4] = false;
222  values[5] = Int64GetDatum((int64) fctx->record[i].blocknum);
223  nulls[5] = false;
224  values[6] = BoolGetDatum(fctx->record[i].isdirty);
225  nulls[6] = false;
226  values[7] = Int16GetDatum(fctx->record[i].usagecount);
227  nulls[7] = false;
228  /* unused for v1.0 callers, but the array is always long enough */
230  nulls[8] = false;
231  }
232 
233  /* Build and return the tuple. */
234  tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
235  result = HeapTupleGetDatum(tuple);
236 
237  SRF_RETURN_NEXT(funcctx, result);
238  }
239  else
240  SRF_RETURN_DONE(funcctx);
241 }
242 
243 Datum
245 {
246  Datum result;
247  TupleDesc tupledesc;
248  HeapTuple tuple;
250  bool nulls[NUM_BUFFERCACHE_SUMMARY_ELEM];
251 
252  int32 buffers_used = 0;
253  int32 buffers_unused = 0;
254  int32 buffers_dirty = 0;
255  int32 buffers_pinned = 0;
256  int64 usagecount_total = 0;
257 
258  if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
259  elog(ERROR, "return type must be a row type");
260 
261  for (int i = 0; i < NBuffers; i++)
262  {
263  BufferDesc *bufHdr;
264  uint32 buf_state;
265 
266  /*
267  * This function summarizes the state of all headers. Locking the
268  * buffer headers wouldn't provide an improved result as the state of
269  * the buffer can still change after we release the lock and it'd
270  * noticeably increase the cost of the function.
271  */
272  bufHdr = GetBufferDescriptor(i);
273  buf_state = pg_atomic_read_u32(&bufHdr->state);
274 
275  if (buf_state & BM_VALID)
276  {
277  buffers_used++;
278  usagecount_total += BUF_STATE_GET_USAGECOUNT(buf_state);
279 
280  if (buf_state & BM_DIRTY)
281  buffers_dirty++;
282  }
283  else
284  buffers_unused++;
285 
286  if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
287  buffers_pinned++;
288  }
289 
290  memset(nulls, 0, sizeof(nulls));
291  values[0] = Int32GetDatum(buffers_used);
292  values[1] = Int32GetDatum(buffers_unused);
293  values[2] = Int32GetDatum(buffers_dirty);
294  values[3] = Int32GetDatum(buffers_pinned);
295 
296  if (buffers_used != 0)
297  values[4] = Float8GetDatum((double) usagecount_total / buffers_used);
298  else
299  nulls[4] = true;
300 
301  /* Build and return the tuple. */
302  tuple = heap_form_tuple(tupledesc, values, nulls);
303  result = HeapTupleGetDatum(tuple);
304 
305  PG_RETURN_DATUM(result);
306 }
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition: atomics.h:236
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:156
#define BM_TAG_VALID
Definition: buf_internals.h:61
static ForkNumber BufTagGetForkNum(const BufferTag *tag)
static BufferDesc * GetBufferDescriptor(uint32 id)
static void UnlockBufHdr(BufferDesc *desc, uint32 buf_state)
static RelFileNumber BufTagGetRelNumber(const BufferTag *tag)
#define BM_DIRTY
Definition: buf_internals.h:59
#define BUF_STATE_GET_USAGECOUNT(state)
Definition: buf_internals.h:50
#define BUF_STATE_GET_REFCOUNT(state)
Definition: buf_internals.h:49
#define BM_VALID
Definition: buf_internals.h:60
static Buffer BufferDescriptorGetBuffer(const BufferDesc *bdesc)
uint32 LockBufHdr(BufferDesc *desc)
Definition: bufmgr.c:4755
unsigned short uint16
Definition: c.h:441
unsigned int uint32
Definition: c.h:442
signed int int32
Definition: c.h:430
#define ERROR
Definition: elog.h:35
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2071
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1683
Datum Float8GetDatum(float8 X)
Definition: fmgr.c:1692
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
TypeFuncClass get_call_result_type(FunctionCallInfo fcinfo, Oid *resultTypeId, TupleDesc *resultTupleDesc)
Definition: funcapi.c:276
#define SRF_IS_FIRSTCALL()
Definition: funcapi.h:303
#define SRF_PERCALL_SETUP()
Definition: funcapi.h:307
@ TYPEFUNC_COMPOSITE
Definition: funcapi.h:149
#define SRF_RETURN_NEXT(_funcctx, _result)
Definition: funcapi.h:309
#define SRF_FIRSTCALL_INIT()
Definition: funcapi.h:305
static Datum HeapTupleGetDatum(const HeapTupleData *tuple)
Definition: funcapi.h:230
#define SRF_RETURN_DONE(_funcctx)
Definition: funcapi.h:327
int NBuffers
Definition: globals.c:136
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, Datum *values, bool *isnull)
Definition: heaptuple.c:1020
int i
Definition: isn.c:73
MemoryContext CurrentMemoryContext
Definition: mcxt.c:124
void * MemoryContextAllocHuge(MemoryContext context, Size size)
Definition: mcxt.c:1425
void * palloc(Size size)
Definition: mcxt.c:1199
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition: palloc.h:135
Datum pg_buffercache_summary(PG_FUNCTION_ARGS)
PG_MODULE_MAGIC
PG_FUNCTION_INFO_V1(pg_buffercache_pages)
#define NUM_BUFFERCACHE_SUMMARY_ELEM
Datum pg_buffercache_pages(PG_FUNCTION_ARGS)
#define NUM_BUFFERCACHE_PAGES_MIN_ELEM
#define NUM_BUFFERCACHE_PAGES_ELEM
uintptr_t Datum
Definition: postgres.h:412
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:520
static Datum BoolGetDatum(bool X)
Definition: postgres.h:450
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:600
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:560
unsigned int Oid
Definition: postgres_ext.h:31
Oid RelFileNumber
Definition: relpath.h:25
ForkNumber
Definition: relpath.h:48
BufferCachePagesRec * record
BufferTag tag
pg_atomic_uint32 state
void * user_fctx
Definition: funcapi.h:82
uint64 max_calls
Definition: funcapi.h:74
uint64 call_cntr
Definition: funcapi.h:65
MemoryContext multi_call_memory_ctx
Definition: funcapi.h:101
BlockNumber blockNum
Definition: buf_internals.h:96
Oid spcOid
Definition: buf_internals.h:92
Oid dbOid
Definition: buf_internals.h:93
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:45
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:583