LCOV - code coverage report
Current view: top level - contrib/pg_buffercache - pg_buffercache_pages.c (source / functions) Hit Total Coverage
Test: PostgreSQL 13devel Lines: 0 83 0.0 %
Date: 2019-11-13 22:07:24 Functions: 0 3 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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             : 
      21           0 : PG_MODULE_MAGIC;
      22             : 
      23             : /*
      24             :  * Record structure holding the to be exposed cache data.
      25             :  */
      26             : typedef struct
      27             : {
      28             :     uint32      bufferid;
      29             :     Oid         relfilenode;
      30             :     Oid         reltablespace;
      31             :     Oid         reldatabase;
      32             :     ForkNumber  forknum;
      33             :     BlockNumber blocknum;
      34             :     bool        isvalid;
      35             :     bool        isdirty;
      36             :     uint16      usagecount;
      37             : 
      38             :     /*
      39             :      * An int32 is sufficiently large, as MAX_BACKENDS prevents a buffer from
      40             :      * being pinned by too many backends and each backend will only pin once
      41             :      * because of bufmgr.c's PrivateRefCount infrastructure.
      42             :      */
      43             :     int32       pinning_backends;
      44             : } BufferCachePagesRec;
      45             : 
      46             : 
      47             : /*
      48             :  * Function context for data persisting over repeated calls.
      49             :  */
      50             : typedef struct
      51             : {
      52             :     TupleDesc   tupdesc;
      53             :     BufferCachePagesRec *record;
      54             : } BufferCachePagesContext;
      55             : 
      56             : 
      57             : /*
      58             :  * Function returning data from the shared buffer cache - buffer number,
      59             :  * relation node/tablespace/database/blocknum and dirty indicator.
      60             :  */
      61           0 : PG_FUNCTION_INFO_V1(pg_buffercache_pages);
      62             : 
      63             : Datum
      64           0 : pg_buffercache_pages(PG_FUNCTION_ARGS)
      65             : {
      66             :     FuncCallContext *funcctx;
      67             :     Datum       result;
      68             :     MemoryContext oldcontext;
      69             :     BufferCachePagesContext *fctx;  /* User function context. */
      70             :     TupleDesc   tupledesc;
      71             :     TupleDesc   expected_tupledesc;
      72             :     HeapTuple   tuple;
      73             : 
      74           0 :     if (SRF_IS_FIRSTCALL())
      75             :     {
      76             :         int         i;
      77             : 
      78           0 :         funcctx = SRF_FIRSTCALL_INIT();
      79             : 
      80             :         /* Switch context when allocating stuff to be used in later calls */
      81           0 :         oldcontext = MemoryContextSwitchTo(funcctx->multi_call_memory_ctx);
      82             : 
      83             :         /* Create a user function context for cross-call persistence */
      84           0 :         fctx = (BufferCachePagesContext *) palloc(sizeof(BufferCachePagesContext));
      85             : 
      86             :         /*
      87             :          * To smoothly support upgrades from version 1.0 of this extension
      88             :          * transparently handle the (non-)existence of the pinning_backends
      89             :          * column. We unfortunately have to get the result type for that... -
      90             :          * we can't use the result type determined by the function definition
      91             :          * without potentially crashing when somebody uses the old (or even
      92             :          * wrong) function definition though.
      93             :          */
      94           0 :         if (get_call_result_type(fcinfo, NULL, &expected_tupledesc) != TYPEFUNC_COMPOSITE)
      95           0 :             elog(ERROR, "return type must be a row type");
      96             : 
      97           0 :         if (expected_tupledesc->natts < NUM_BUFFERCACHE_PAGES_MIN_ELEM ||
      98           0 :             expected_tupledesc->natts > NUM_BUFFERCACHE_PAGES_ELEM)
      99           0 :             elog(ERROR, "incorrect number of output arguments");
     100             : 
     101             :         /* Construct a tuple descriptor for the result rows. */
     102           0 :         tupledesc = CreateTemplateTupleDesc(expected_tupledesc->natts);
     103           0 :         TupleDescInitEntry(tupledesc, (AttrNumber) 1, "bufferid",
     104             :                            INT4OID, -1, 0);
     105           0 :         TupleDescInitEntry(tupledesc, (AttrNumber) 2, "relfilenode",
     106             :                            OIDOID, -1, 0);
     107           0 :         TupleDescInitEntry(tupledesc, (AttrNumber) 3, "reltablespace",
     108             :                            OIDOID, -1, 0);
     109           0 :         TupleDescInitEntry(tupledesc, (AttrNumber) 4, "reldatabase",
     110             :                            OIDOID, -1, 0);
     111           0 :         TupleDescInitEntry(tupledesc, (AttrNumber) 5, "relforknumber",
     112             :                            INT2OID, -1, 0);
     113           0 :         TupleDescInitEntry(tupledesc, (AttrNumber) 6, "relblocknumber",
     114             :                            INT8OID, -1, 0);
     115           0 :         TupleDescInitEntry(tupledesc, (AttrNumber) 7, "isdirty",
     116             :                            BOOLOID, -1, 0);
     117           0 :         TupleDescInitEntry(tupledesc, (AttrNumber) 8, "usage_count",
     118             :                            INT2OID, -1, 0);
     119             : 
     120           0 :         if (expected_tupledesc->natts == NUM_BUFFERCACHE_PAGES_ELEM)
     121           0 :             TupleDescInitEntry(tupledesc, (AttrNumber) 9, "pinning_backends",
     122             :                                INT4OID, -1, 0);
     123             : 
     124           0 :         fctx->tupdesc = BlessTupleDesc(tupledesc);
     125             : 
     126             :         /* Allocate NBuffers worth of BufferCachePagesRec records. */
     127           0 :         fctx->record = (BufferCachePagesRec *)
     128           0 :             MemoryContextAllocHuge(CurrentMemoryContext,
     129             :                                    sizeof(BufferCachePagesRec) * NBuffers);
     130             : 
     131             :         /* Set max calls and remember the user function context. */
     132           0 :         funcctx->max_calls = NBuffers;
     133           0 :         funcctx->user_fctx = fctx;
     134             : 
     135             :         /* Return to original context when allocating transient memory */
     136           0 :         MemoryContextSwitchTo(oldcontext);
     137             : 
     138             :         /*
     139             :          * Scan through all the buffers, saving the relevant fields in the
     140             :          * fctx->record structure.
     141             :          *
     142             :          * We don't hold the partition locks, so we don't get a consistent
     143             :          * snapshot across all buffers, but we do grab the buffer header
     144             :          * locks, so the information of each buffer is self-consistent.
     145             :          */
     146           0 :         for (i = 0; i < NBuffers; i++)
     147             :         {
     148             :             BufferDesc *bufHdr;
     149             :             uint32      buf_state;
     150             : 
     151           0 :             bufHdr = GetBufferDescriptor(i);
     152             :             /* Lock each buffer header before inspecting. */
     153           0 :             buf_state = LockBufHdr(bufHdr);
     154             : 
     155           0 :             fctx->record[i].bufferid = BufferDescriptorGetBuffer(bufHdr);
     156           0 :             fctx->record[i].relfilenode = bufHdr->tag.rnode.relNode;
     157           0 :             fctx->record[i].reltablespace = bufHdr->tag.rnode.spcNode;
     158           0 :             fctx->record[i].reldatabase = bufHdr->tag.rnode.dbNode;
     159           0 :             fctx->record[i].forknum = bufHdr->tag.forkNum;
     160           0 :             fctx->record[i].blocknum = bufHdr->tag.blockNum;
     161           0 :             fctx->record[i].usagecount = BUF_STATE_GET_USAGECOUNT(buf_state);
     162           0 :             fctx->record[i].pinning_backends = BUF_STATE_GET_REFCOUNT(buf_state);
     163             : 
     164           0 :             if (buf_state & BM_DIRTY)
     165           0 :                 fctx->record[i].isdirty = true;
     166             :             else
     167           0 :                 fctx->record[i].isdirty = false;
     168             : 
     169             :             /* Note if the buffer is valid, and has storage created */
     170           0 :             if ((buf_state & BM_VALID) && (buf_state & BM_TAG_VALID))
     171           0 :                 fctx->record[i].isvalid = true;
     172             :             else
     173           0 :                 fctx->record[i].isvalid = false;
     174             : 
     175           0 :             UnlockBufHdr(bufHdr, buf_state);
     176             :         }
     177             :     }
     178             : 
     179           0 :     funcctx = SRF_PERCALL_SETUP();
     180             : 
     181             :     /* Get the saved state */
     182           0 :     fctx = funcctx->user_fctx;
     183             : 
     184           0 :     if (funcctx->call_cntr < funcctx->max_calls)
     185             :     {
     186           0 :         uint32      i = funcctx->call_cntr;
     187             :         Datum       values[NUM_BUFFERCACHE_PAGES_ELEM];
     188             :         bool        nulls[NUM_BUFFERCACHE_PAGES_ELEM];
     189             : 
     190           0 :         values[0] = Int32GetDatum(fctx->record[i].bufferid);
     191           0 :         nulls[0] = false;
     192             : 
     193             :         /*
     194             :          * Set all fields except the bufferid to null if the buffer is unused
     195             :          * or not valid.
     196             :          */
     197           0 :         if (fctx->record[i].blocknum == InvalidBlockNumber ||
     198           0 :             fctx->record[i].isvalid == false)
     199             :         {
     200           0 :             nulls[1] = true;
     201           0 :             nulls[2] = true;
     202           0 :             nulls[3] = true;
     203           0 :             nulls[4] = true;
     204           0 :             nulls[5] = true;
     205           0 :             nulls[6] = true;
     206           0 :             nulls[7] = true;
     207             :             /* unused for v1.0 callers, but the array is always long enough */
     208           0 :             nulls[8] = true;
     209             :         }
     210             :         else
     211             :         {
     212           0 :             values[1] = ObjectIdGetDatum(fctx->record[i].relfilenode);
     213           0 :             nulls[1] = false;
     214           0 :             values[2] = ObjectIdGetDatum(fctx->record[i].reltablespace);
     215           0 :             nulls[2] = false;
     216           0 :             values[3] = ObjectIdGetDatum(fctx->record[i].reldatabase);
     217           0 :             nulls[3] = false;
     218           0 :             values[4] = ObjectIdGetDatum(fctx->record[i].forknum);
     219           0 :             nulls[4] = false;
     220           0 :             values[5] = Int64GetDatum((int64) fctx->record[i].blocknum);
     221           0 :             nulls[5] = false;
     222           0 :             values[6] = BoolGetDatum(fctx->record[i].isdirty);
     223           0 :             nulls[6] = false;
     224           0 :             values[7] = Int16GetDatum(fctx->record[i].usagecount);
     225           0 :             nulls[7] = false;
     226             :             /* unused for v1.0 callers, but the array is always long enough */
     227           0 :             values[8] = Int32GetDatum(fctx->record[i].pinning_backends);
     228           0 :             nulls[8] = false;
     229             :         }
     230             : 
     231             :         /* Build and return the tuple. */
     232           0 :         tuple = heap_form_tuple(fctx->tupdesc, values, nulls);
     233           0 :         result = HeapTupleGetDatum(tuple);
     234             : 
     235           0 :         SRF_RETURN_NEXT(funcctx, result);
     236             :     }
     237             :     else
     238           0 :         SRF_RETURN_DONE(funcctx);
     239             : }

Generated by: LCOV version 1.13