/* ------------------------------------------------------------------------- * * pgstat_slru.c * Implementation of SLRU statistics. * * This file contains the implementation of SLRU statistics. It is kept * separate from pgstat.c to enforce the line between the statistics access / * storage implementation and the details about individual types of * statistics. * * Copyright (c) 2001-2022, PostgreSQL Global Development Group * * IDENTIFICATION * src/backend/utils/activity/pgstat_slru.c * ------------------------------------------------------------------------- */ #include "postgres.h" #include "utils/pgstat_internal.h" #include "utils/timestamp.h" static inline PgStat_SLRUStats *get_slru_entry(int slru_idx); static void pgstat_reset_slru_counter_internal(int index, TimestampTz ts); /* * SLRU statistics counts waiting to be flushed out. We assume this variable * inits to zeroes. Entries are one-to-one with slru_names[]. Changes of * SLRU counters are reported within critical sections so we use static memory * in order to avoid memory allocation. */ static PgStat_SLRUStats pending_SLRUStats[SLRU_NUM_ELEMENTS]; bool have_slrustats = false; /* * Reset counters for a single SLRU. * * Permission checking for this function is managed through the normal * GRANT system. */ void pgstat_reset_slru(const char *name) { TimestampTz ts = GetCurrentTimestamp(); AssertArg(name != NULL); pgstat_reset_slru_counter_internal(pgstat_get_slru_index(name), ts); } /* * SLRU statistics count accumulation functions --- called from slru.c */ void pgstat_count_slru_page_zeroed(int slru_idx) { get_slru_entry(slru_idx)->blocks_zeroed += 1; } void pgstat_count_slru_page_hit(int slru_idx) { get_slru_entry(slru_idx)->blocks_hit += 1; } void pgstat_count_slru_page_exists(int slru_idx) { get_slru_entry(slru_idx)->blocks_exists += 1; } void pgstat_count_slru_page_read(int slru_idx) { get_slru_entry(slru_idx)->blocks_read += 1; } void pgstat_count_slru_page_written(int slru_idx) { get_slru_entry(slru_idx)->blocks_written += 1; } void pgstat_count_slru_flush(int slru_idx) { get_slru_entry(slru_idx)->flush += 1; } void pgstat_count_slru_truncate(int slru_idx) { get_slru_entry(slru_idx)->truncate += 1; } /* * Support function for the SQL-callable pgstat* functions. Returns * a pointer to the slru statistics struct. */ PgStat_SLRUStats * pgstat_fetch_slru(void) { pgstat_snapshot_fixed(PGSTAT_KIND_SLRU); return pgStatLocal.snapshot.slru; } /* * Returns SLRU name for an index. The index may be above SLRU_NUM_ELEMENTS, * in which case this returns NULL. This allows writing code that does not * know the number of entries in advance. */ const char * pgstat_get_slru_name(int slru_idx) { if (slru_idx < 0 || slru_idx >= SLRU_NUM_ELEMENTS) return NULL; return slru_names[slru_idx]; } /* * Determine index of entry for a SLRU with a given name. If there's no exact * match, returns index of the last "other" entry used for SLRUs defined in * external projects. */ int pgstat_get_slru_index(const char *name) { int i; for (i = 0; i < SLRU_NUM_ELEMENTS; i++) { if (strcmp(slru_names[i], name) == 0) return i; } /* return index of the last entry (which is the "other" one) */ return (SLRU_NUM_ELEMENTS - 1); } /* * Flush out locally pending SLRU stats entries * * If nowait is true, this function returns false on lock failure. Otherwise * this function always returns true. * * If nowait is true, this function returns true if the lock could not be * acquired. Otherwise return false. */ bool pgstat_slru_flush(bool nowait) { PgStatShared_SLRU *stats_shmem = &pgStatLocal.shmem->slru; int i; if (!have_slrustats) return false; if (!nowait) LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE); else if (!LWLockConditionalAcquire(&stats_shmem->lock, LW_EXCLUSIVE)) return true; for (i = 0; i < SLRU_NUM_ELEMENTS; i++) { PgStat_SLRUStats *sharedent = &stats_shmem->stats[i]; PgStat_SLRUStats *pendingent = &pending_SLRUStats[i]; #define SLRU_ACC(fld) sharedent->fld += pendingent->fld SLRU_ACC(blocks_zeroed); SLRU_ACC(blocks_hit); SLRU_ACC(blocks_read); SLRU_ACC(blocks_written); SLRU_ACC(blocks_exists); SLRU_ACC(flush); SLRU_ACC(truncate); #undef SLRU_ACC } /* done, clear the pending entry */ MemSet(pending_SLRUStats, 0, sizeof(pending_SLRUStats)); LWLockRelease(&stats_shmem->lock); have_slrustats = false; return false; } void pgstat_slru_reset_all_cb(TimestampTz ts) { for (int i = 0; i < SLRU_NUM_ELEMENTS; i++) pgstat_reset_slru_counter_internal(i, ts); } void pgstat_slru_snapshot_cb(void) { PgStatShared_SLRU *stats_shmem = &pgStatLocal.shmem->slru; LWLockAcquire(&stats_shmem->lock, LW_SHARED); memcpy(pgStatLocal.snapshot.slru, &stats_shmem->stats, sizeof(stats_shmem->stats)); LWLockRelease(&stats_shmem->lock); } /* * Returns pointer to entry with counters for given SLRU (based on the name * stored in SlruCtl as lwlock tranche name). */ static inline PgStat_SLRUStats * get_slru_entry(int slru_idx) { pgstat_assert_is_up(); /* * The postmaster should never register any SLRU statistics counts; if it * did, the counts would be duplicated into child processes via fork(). */ Assert(IsUnderPostmaster || !IsPostmasterEnvironment); Assert((slru_idx >= 0) && (slru_idx < SLRU_NUM_ELEMENTS)); have_slrustats = true; return &pending_SLRUStats[slru_idx]; } static void pgstat_reset_slru_counter_internal(int index, TimestampTz ts) { PgStatShared_SLRU *stats_shmem = &pgStatLocal.shmem->slru; LWLockAcquire(&stats_shmem->lock, LW_EXCLUSIVE); memset(&stats_shmem->stats[index], 0, sizeof(PgStat_SLRUStats)); stats_shmem->stats[index].stat_reset_timestamp = ts; LWLockRelease(&stats_shmem->lock); }