Header And Logo

PostgreSQL
| The world's most advanced open source database.

pg_buffercache_pages.c

Go to the documentation of this file.
00001 /*-------------------------------------------------------------------------
00002  *
00003  * pg_buffercache_pages.c
00004  *    display some contents of the buffer cache
00005  *
00006  *    contrib/pg_buffercache/pg_buffercache_pages.c
00007  *-------------------------------------------------------------------------
00008  */
00009 #include "postgres.h"
00010 
00011 #include "access/htup_details.h"
00012 #include "catalog/pg_type.h"
00013 #include "funcapi.h"
00014 #include "storage/buf_internals.h"
00015 #include "storage/bufmgr.h"
00016 
00017 
00018 #define NUM_BUFFERCACHE_PAGES_ELEM  8
00019 
00020 PG_MODULE_MAGIC;
00021 
00022 Datum       pg_buffercache_pages(PG_FUNCTION_ARGS);
00023 
00024 
00025 /*
00026  * Record structure holding the to be exposed cache data.
00027  */
00028 typedef struct
00029 {
00030     uint32      bufferid;
00031     Oid         relfilenode;
00032     Oid         reltablespace;
00033     Oid         reldatabase;
00034     ForkNumber  forknum;
00035     BlockNumber blocknum;
00036     bool        isvalid;
00037     bool        isdirty;
00038     uint16      usagecount;
00039 } BufferCachePagesRec;
00040 
00041 
00042 /*
00043  * Function context for data persisting over repeated calls.
00044  */
00045 typedef struct
00046 {
00047     TupleDesc   tupdesc;
00048     BufferCachePagesRec *record;
00049 } BufferCachePagesContext;
00050 
00051 
00052 /*
00053  * Function returning data from the shared buffer cache - buffer number,
00054  * relation node/tablespace/database/blocknum and dirty indicator.
00055  */
00056 PG_FUNCTION_INFO_V1(pg_buffercache_pages);
00057 
00058 Datum
00059 pg_buffercache_pages(PG_FUNCTION_ARGS)
00060 {
00061     FuncCallContext *funcctx;
00062     Datum       result;
00063     MemoryContext oldcontext;
00064     BufferCachePagesContext *fctx;      /* User function context. */
00065     TupleDesc   tupledesc;
00066     HeapTuple   tuple;
00067 
00068     if (SRF_IS_FIRSTCALL())
00069     {
00070         int         i;
00071         volatile BufferDesc *bufHdr;
00072 
00073         funcctx = SRF_FIRSTCALL_INIT();
00074 
00075         /* Switch context when allocating stuff to be used in later calls */
00076         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
00077 
00078         /* Create a user function context for cross-call persistence */
00079         fctx = (BufferCachePagesContext *) palloc(sizeof(BufferCachePagesContext));
00080 
00081         /* Construct a tuple descriptor for the result rows. */
00082         tupledesc = CreateTemplateTupleDesc(NUM_BUFFERCACHE_PAGES_ELEM, false);
00083         TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
00084                            INT4OID, -1, 0);
00085         TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
00086                            OIDOID, -1, 0);
00087         TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
00088                            OIDOID, -1, 0);
00089         TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
00090                            OIDOID, -1, 0);
00091         TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relforknumber",
00092                            INT2OID, -1, 0);
00093         TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relblocknumber",
00094                            INT8OID, -1, 0);
00095         TupleDescInitEntry(tupledesc, (AttrNumber) 7, "isdirty",
00096                            BOOLOID, -1, 0);
00097         TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count",
00098                            INT2OID, -1, 0);
00099 
00100         fctx->tupdesc = BlessTupleDesc(tupledesc);
00101 
00102         /* Allocate NBuffers worth of BufferCachePagesRec records. */
00103         fctx->record = (BufferCachePagesRec *) palloc(sizeof(BufferCachePagesRec) * NBuffers);
00104 
00105         /* Set max calls and remember the user function context. */
00106         funcctx->max_calls = NBuffers;
00107         funcctx->user_fctx = fctx;
00108 
00109         /* Return to original context when allocating transient memory */
00110         MemoryContextSwitchTo(oldcontext);
00111 
00112         /*
00113          * To get a consistent picture of the buffer state, we must lock all
00114          * partitions of the buffer map.  Needless to say, this is horrible
00115          * for concurrency.  Must grab locks in increasing order to avoid
00116          * possible deadlocks.
00117          */
00118         for (i = 0; i < NUM_BUFFER_PARTITIONS; i++)
00119             LWLockAcquire(FirstBufMappingLock + i, LW_SHARED);
00120 
00121         /*
00122          * Scan though all the buffers, saving the relevant fields in the
00123          * fctx->record structure.
00124          */
00125         for (i = 0, bufHdr = BufferDescriptors; i < NBuffers; i++, bufHdr++)
00126         {
00127             /* Lock each buffer header before inspecting. */
00128             LockBufHdr(bufHdr);
00129 
00130             fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr);
00131             fctx->record[i].relfilenode = bufHdr->tag.rnode.relNode;
00132             fctx->record[i].reltablespace = bufHdr->tag.rnode.spcNode;
00133             fctx->record[i].reldatabase = bufHdr->tag.rnode.dbNode;
00134             fctx->record[i].forknum = bufHdr->tag.forkNum;
00135             fctx->record[i].blocknum = bufHdr->tag.blockNum;
00136             fctx->record[i].usagecount = bufHdr->usage_count;
00137 
00138             if (bufHdr->flags & BM_DIRTY)
00139                 fctx->record[i].isdirty = true;
00140             else
00141                 fctx->record[i].isdirty = false;
00142 
00143             /* Note if the buffer is valid, and has storage created */
00144             if ((bufHdr->flags & BM_VALID) && (bufHdr->flags & BM_TAG_VALID))
00145                 fctx->record[i].isvalid = true;
00146             else
00147                 fctx->record[i].isvalid = false;
00148 
00149             UnlockBufHdr(bufHdr);
00150         }
00151 
00152         /*
00153          * And release locks.  We do this in reverse order for two reasons:
00154          * (1) Anyone else who needs more than one of the locks will be trying
00155          * to lock them in increasing order; we don't want to release the
00156          * other process until it can get all the locks it needs. (2) This
00157          * avoids O(N^2) behavior inside LWLockRelease.
00158          */
00159         for (i = NUM_BUFFER_PARTITIONS; --i >= 0;)
00160             LWLockRelease(FirstBufMappingLock + i);
00161     }
00162 
00163     funcctx = SRF_PERCALL_SETUP();
00164 
00165     /* Get the saved state */
00166     fctx = funcctx->user_fctx;
00167 
00168     if (funcctx->call_cntr < funcctx->max_calls)
00169     {
00170         uint32      i = funcctx->call_cntr;
00171         Datum       values[NUM_BUFFERCACHE_PAGES_ELEM];
00172         bool        nulls[NUM_BUFFERCACHE_PAGES_ELEM];
00173 
00174         values[0] = Int32GetDatum(fctx->record[i].bufferid);
00175         nulls[0] = false;
00176 
00177         /*
00178          * Set all fields except the bufferid to null if the buffer is unused
00179          * or not valid.
00180          */
00181         if (fctx->record[i].blocknum == InvalidBlockNumber ||
00182             fctx->record[i].isvalid == false)
00183         {
00184             nulls[1] = true;
00185             nulls[2] = true;
00186             nulls[3] = true;
00187             nulls[4] = true;
00188             nulls[5] = true;
00189             nulls[6] = true;
00190             nulls[7] = true;
00191         }
00192         else
00193         {
00194             values[1] = ObjectIdGetDatum(fctx->record[i].relfilenode);
00195             nulls[1] = false;
00196             values[2] = ObjectIdGetDatum(fctx->record[i].reltablespace);
00197             nulls[2] = false;
00198             values[3] = ObjectIdGetDatum(fctx->record[i].reldatabase);
00199             nulls[3] = false;
00200             values[4] = ObjectIdGetDatum(fctx->record[i].forknum);
00201             nulls[4] = false;
00202             values[5] = Int64GetDatum((int64) fctx->record[i].blocknum);
00203             nulls[5] = false;
00204             values[6] = BoolGetDatum(fctx->record[i].isdirty);
00205             nulls[6] = false;
00206             values[7] = Int16GetDatum(fctx->record[i].usagecount);
00207             nulls[7] = false;
00208         }
00209 
00210         /* Build and return the tuple. */
00211         tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
00212         result = HeapTupleGetDatum(tuple);
00213 
00214         SRF_RETURN_NEXT(funcctx, result);
00215     }
00216     else
00217         SRF_RETURN_DONE(funcctx);
00218 }