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 #define NUM_BUFFERCACHE_USAGE_COUNTS_ELEM 4
22 
24 
25 /*
26  * Record structure holding the to be exposed cache data.
27  */
28 typedef struct
29 {
36  bool isvalid;
37  bool isdirty;
39 
40  /*
41  * An int32 is sufficiently large, as MAX_BACKENDS prevents a buffer from
42  * being pinned by too many backends and each backend will only pin once
43  * because of bufmgr.c's PrivateRefCount infrastructure.
44  */
47 
48 
49 /*
50  * Function context for data persisting over repeated calls.
51  */
52 typedef struct
53 {
57 
58 
59 /*
60  * Function returning data from the shared buffer cache - buffer number,
61  * relation node/tablespace/database/blocknum and dirty indicator.
62  */
67 
68 Datum
70 {
71  FuncCallContext *funcctx;
72  Datum result;
73  MemoryContext oldcontext;
74  BufferCachePagesContext *fctx; /* User function context. */
75  TupleDesc tupledesc;
76  TupleDesc expected_tupledesc;
77  HeapTuple tuple;
78 
79  if (SRF_IS_FIRSTCALL())
80  {
81  int i;
82 
83  funcctx = SRF_FIRSTCALL_INIT();
84 
85  /* Switch context when allocating stuff to be used in later calls */
86  oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
87 
88  /* Create a user function context for cross-call persistence */
90 
91  /*
92  * To smoothly support upgrades from version 1.0 of this extension
93  * transparently handle the (non-)existence of the pinning_backends
94  * column. We unfortunately have to get the result type for that... -
95  * we can't use the result type determined by the function definition
96  * without potentially crashing when somebody uses the old (or even
97  * wrong) function definition though.
98  */
99  if (get_call_result_type(fcinfo, NULL, &expected_tupledesc) != TYPEFUNC_COMPOSITE)
100  elog(ERROR, "return type must be a row type");
101 
102  if (expected_tupledesc->natts < NUM_BUFFERCACHE_PAGES_MIN_ELEM ||
103  expected_tupledesc->natts > NUM_BUFFERCACHE_PAGES_ELEM)
104  elog(ERROR, "incorrect number of output arguments");
105 
106  /* Construct a tuple descriptor for the result rows. */
107  tupledesc = CreateTemplateTupleDesc(expected_tupledesc->natts);
108  TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
109  INT4OID, -1, 0);
110  TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
111  OIDOID, -1, 0);
112  TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
113  OIDOID, -1, 0);
114  TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
115  OIDOID, -1, 0);
116  TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relforknumber",
117  INT2OID, -1, 0);
118  TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relblocknumber",
119  INT8OID, -1, 0);
120  TupleDescInitEntry(tupledesc, (AttrNumber) 7, "isdirty",
121  BOOLOID, -1, 0);
122  TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count",
123  INT2OID, -1, 0);
124 
125  if (expected_tupledesc->natts == NUM_BUFFERCACHE_PAGES_ELEM)
126  TupleDescInitEntry(tupledesc, (AttrNumber) 9, "pinning_backends",
127  INT4OID, -1, 0);
128 
129  fctx->tupdesc = BlessTupleDesc(tupledesc);
130 
131  /* Allocate NBuffers worth of BufferCachePagesRec records. */
132  fctx->record = (BufferCachePagesRec *)
134  sizeof(BufferCachePagesRec) * NBuffers);
135 
136  /* Set max calls and remember the user function context. */
137  funcctx->max_calls = NBuffers;
138  funcctx->user_fctx = fctx;
139 
140  /* Return to original context when allocating transient memory */
141  MemoryContextSwitchTo(oldcontext);
142 
143  /*
144  * Scan through all the buffers, saving the relevant fields in the
145  * fctx->record structure.
146  *
147  * We don't hold the partition locks, so we don't get a consistent
148  * snapshot across all buffers, but we do grab the buffer header
149  * locks, so the information of each buffer is self-consistent.
150  */
151  for (i = 0; i < NBuffers; i++)
152  {
153  BufferDesc *bufHdr;
154  uint32 buf_state;
155 
156  bufHdr = GetBufferDescriptor(i);
157  /* Lock each buffer header before inspecting. */
158  buf_state = LockBufHdr(bufHdr);
159 
160  fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr);
161  fctx->record[i].relfilenumber = BufTagGetRelNumber(&bufHdr->tag);
162  fctx->record[i].reltablespace = bufHdr->tag.spcOid;
163  fctx->record[i].reldatabase = bufHdr->tag.dbOid;
164  fctx->record[i].forknum = BufTagGetForkNum(&bufHdr->tag);
165  fctx->record[i].blocknum = bufHdr->tag.blockNum;
166  fctx->record[i].usagecount = BUF_STATE_GET_USAGECOUNT(buf_state);
167  fctx->record[i].pinning_backends = BUF_STATE_GET_REFCOUNT(buf_state);
168 
169  if (buf_state & BM_DIRTY)
170  fctx->record[i].isdirty = true;
171  else
172  fctx->record[i].isdirty = false;
173 
174  /* Note if the buffer is valid, and has storage created */
175  if ((buf_state & BM_VALID) && (buf_state & BM_TAG_VALID))
176  fctx->record[i].isvalid = true;
177  else
178  fctx->record[i].isvalid = false;
179 
180  UnlockBufHdr(bufHdr, buf_state);
181  }
182  }
183 
184  funcctx = SRF_PERCALL_SETUP();
185 
186  /* Get the saved state */
187  fctx = funcctx->user_fctx;
188 
189  if (funcctx->call_cntr < funcctx->max_calls)
190  {
191  uint32 i = funcctx->call_cntr;
193  bool nulls[NUM_BUFFERCACHE_PAGES_ELEM];
194 
195  values[0] = Int32GetDatum(fctx->record[i].bufferid);
196  nulls[0] = false;
197 
198  /*
199  * Set all fields except the bufferid to null if the buffer is unused
200  * or not valid.
201  */
202  if (fctx->record[i].blocknum == InvalidBlockNumber ||
203  fctx->record[i].isvalid == false)
204  {
205  nulls[1] = true;
206  nulls[2] = true;
207  nulls[3] = true;
208  nulls[4] = true;
209  nulls[5] = true;
210  nulls[6] = true;
211  nulls[7] = true;
212  /* unused for v1.0 callers, but the array is always long enough */
213  nulls[8] = true;
214  }
215  else
216  {
218  nulls[1] = false;
220  nulls[2] = false;
222  nulls[3] = false;
223  values[4] = ObjectIdGetDatum(fctx->record[i].forknum);
224  nulls[4] = false;
225  values[5] = Int64GetDatum((int64) fctx->record[i].blocknum);
226  nulls[5] = false;
227  values[6] = BoolGetDatum(fctx->record[i].isdirty);
228  nulls[6] = false;
229  values[7] = Int16GetDatum(fctx->record[i].usagecount);
230  nulls[7] = false;
231  /* unused for v1.0 callers, but the array is always long enough */
233  nulls[8] = false;
234  }
235 
236  /* Build and return the tuple. */
237  tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
238  result = HeapTupleGetDatum(tuple);
239 
240  SRF_RETURN_NEXT(funcctx, result);
241  }
242  else
243  SRF_RETURN_DONE(funcctx);
244 }
245 
246 Datum
248 {
249  Datum result;
250  TupleDesc tupledesc;
251  HeapTuple tuple;
253  bool nulls[NUM_BUFFERCACHE_SUMMARY_ELEM];
254 
255  int32 buffers_used = 0;
256  int32 buffers_unused = 0;
257  int32 buffers_dirty = 0;
258  int32 buffers_pinned = 0;
259  int64 usagecount_total = 0;
260 
261  if (get_call_result_type(fcinfo, NULL, &tupledesc) != TYPEFUNC_COMPOSITE)
262  elog(ERROR, "return type must be a row type");
263 
264  for (int i = 0; i < NBuffers; i++)
265  {
266  BufferDesc *bufHdr;
267  uint32 buf_state;
268 
269  /*
270  * This function summarizes the state of all headers. Locking the
271  * buffer headers wouldn't provide an improved result as the state of
272  * the buffer can still change after we release the lock and it'd
273  * noticeably increase the cost of the function.
274  */
275  bufHdr = GetBufferDescriptor(i);
276  buf_state = pg_atomic_read_u32(&bufHdr->state);
277 
278  if (buf_state & BM_VALID)
279  {
280  buffers_used++;
281  usagecount_total += BUF_STATE_GET_USAGECOUNT(buf_state);
282 
283  if (buf_state & BM_DIRTY)
284  buffers_dirty++;
285  }
286  else
287  buffers_unused++;
288 
289  if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
290  buffers_pinned++;
291  }
292 
293  memset(nulls, 0, sizeof(nulls));
294  values[0] = Int32GetDatum(buffers_used);
295  values[1] = Int32GetDatum(buffers_unused);
296  values[2] = Int32GetDatum(buffers_dirty);
297  values[3] = Int32GetDatum(buffers_pinned);
298 
299  if (buffers_used != 0)
300  values[4] = Float8GetDatum((double) usagecount_total / buffers_used);
301  else
302  nulls[4] = true;
303 
304  /* Build and return the tuple. */
305  tuple = heap_form_tuple(tupledesc, values, nulls);
306  result = HeapTupleGetDatum(tuple);
307 
308  PG_RETURN_DATUM(result);
309 }
310 
311 Datum
313 {
314  ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
315  int usage_counts[BM_MAX_USAGE_COUNT + 1] = {0};
316  int dirty[BM_MAX_USAGE_COUNT + 1] = {0};
317  int pinned[BM_MAX_USAGE_COUNT + 1] = {0};
319  bool nulls[NUM_BUFFERCACHE_USAGE_COUNTS_ELEM] = {0};
320 
321  InitMaterializedSRF(fcinfo, 0);
322 
323  for (int i = 0; i < NBuffers; i++)
324  {
325  BufferDesc *bufHdr = GetBufferDescriptor(i);
326  uint32 buf_state = pg_atomic_read_u32(&bufHdr->state);
327  int usage_count;
328 
329  usage_count = BUF_STATE_GET_USAGECOUNT(buf_state);
330  usage_counts[usage_count]++;
331 
332  if (buf_state & BM_DIRTY)
333  dirty[usage_count]++;
334 
335  if (BUF_STATE_GET_REFCOUNT(buf_state) > 0)
336  pinned[usage_count]++;
337  }
338 
339  for (int i = 0; i < BM_MAX_USAGE_COUNT + 1; i++)
340  {
341  values[0] = Int32GetDatum(i);
342  values[1] = Int32GetDatum(usage_counts[i]);
343  values[2] = Int32GetDatum(dirty[i]);
344  values[3] = Int32GetDatum(pinned[i]);
345 
346  tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
347  }
348 
349  return (Datum) 0;
350 }
351 
352 /*
353  * Try to evict a shared buffer.
354  */
355 Datum
357 {
359 
360  if (!superuser())
361  ereport(ERROR,
362  (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
363  errmsg("must be superuser to use pg_buffercache_evict function")));
364 
365  if (buf < 1 || buf > NBuffers)
366  elog(ERROR, "bad buffer ID: %d", buf);
367 
369 }
static uint32 pg_atomic_read_u32(volatile pg_atomic_uint32 *ptr)
Definition: atomics.h:234
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:152
int Buffer
Definition: buf.h:23
#define BM_MAX_USAGE_COUNT
Definition: buf_internals.h:78
#define BM_TAG_VALID
Definition: buf_internals.h:63
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:61
#define BUF_STATE_GET_USAGECOUNT(state)
Definition: buf_internals.h:52
#define BUF_STATE_GET_REFCOUNT(state)
Definition: buf_internals.h:51
#define BM_VALID
Definition: buf_internals.h:62
static Buffer BufferDescriptorGetBuffer(const BufferDesc *bdesc)
bool EvictUnpinnedBuffer(Buffer buf)
Definition: bufmgr.c:6023
uint32 LockBufHdr(BufferDesc *desc)
Definition: bufmgr.c:5688
unsigned short uint16
Definition: c.h:505
unsigned int uint32
Definition: c.h:506
signed int int32
Definition: c.h:494
int errcode(int sqlerrcode)
Definition: elog.c:859
int errmsg(const char *fmt,...)
Definition: elog.c:1072
#define ERROR
Definition: elog.h:39
#define elog(elevel,...)
Definition: elog.h:224
#define ereport(elevel,...)
Definition: elog.h:149
TupleDesc BlessTupleDesc(TupleDesc tupdesc)
Definition: execTuples.c:2158
Datum Int64GetDatum(int64 X)
Definition: fmgr.c:1807
Datum Float8GetDatum(float8 X)
Definition: fmgr.c:1816
#define PG_GETARG_INT32(n)
Definition: fmgr.h:269
#define PG_RETURN_DATUM(x)
Definition: fmgr.h:353
#define PG_FUNCTION_ARGS
Definition: fmgr.h:193
#define PG_RETURN_BOOL(x)
Definition: fmgr.h:359
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 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:139
HeapTuple heap_form_tuple(TupleDesc tupleDescriptor, const Datum *values, const bool *isnull)
Definition: heaptuple.c:1116
int i
Definition: isn.c:73
MemoryContext CurrentMemoryContext
Definition: mcxt.c:143
void * MemoryContextAllocHuge(MemoryContext context, Size size)
Definition: mcxt.c:1639
void * palloc(Size size)
Definition: mcxt.c:1316
#define NUM_BUFFERCACHE_USAGE_COUNTS_ELEM
Datum pg_buffercache_evict(PG_FUNCTION_ARGS)
Datum pg_buffercache_summary(PG_FUNCTION_ARGS)
PG_MODULE_MAGIC
PG_FUNCTION_INFO_V1(pg_buffercache_pages)
Datum pg_buffercache_usage_counts(PG_FUNCTION_ARGS)
#define NUM_BUFFERCACHE_SUMMARY_ELEM
Datum pg_buffercache_pages(PG_FUNCTION_ARGS)
#define NUM_BUFFERCACHE_PAGES_MIN_ELEM
#define NUM_BUFFERCACHE_PAGES_ELEM
static char * buf
Definition: pg_test_fsync.c:73
uintptr_t Datum
Definition: postgres.h:64
static Datum Int16GetDatum(int16 X)
Definition: postgres.h:172
static Datum BoolGetDatum(bool X)
Definition: postgres.h:102
static Datum ObjectIdGetDatum(Oid X)
Definition: postgres.h:252
static Datum Int32GetDatum(int32 X)
Definition: postgres.h:212
unsigned int Oid
Definition: postgres_ext.h:31
MemoryContextSwitchTo(old_ctx)
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:98
Oid spcOid
Definition: buf_internals.h:94
Oid dbOid
Definition: buf_internals.h:95
bool superuser(void)
Definition: superuser.c:46
TupleDesc CreateTemplateTupleDesc(int natts)
Definition: tupdesc.c:67
void TupleDescInitEntry(TupleDesc desc, AttrNumber attributeNumber, const char *attributeName, Oid oidtypeid, int32 typmod, int attdim)
Definition: tupdesc.c:651
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition: tuplestore.c:750