Line data Source code
1 : /*-------------------------------------------------------------------------- 2 : * 3 : * injection_stats.c 4 : * Code for statistics of injection points. 5 : * 6 : * Portions Copyright (c) 1996-2024, PostgreSQL Global Development Group 7 : * Portions Copyright (c) 1994, Regents of the University of California 8 : * 9 : * IDENTIFICATION 10 : * src/test/modules/injection_points/injection_stats.c 11 : * 12 : * ------------------------------------------------------------------------- 13 : */ 14 : 15 : #include "postgres.h" 16 : 17 : #include "fmgr.h" 18 : 19 : #include "common/hashfn.h" 20 : #include "injection_stats.h" 21 : #include "pgstat.h" 22 : #include "utils/builtins.h" 23 : #include "utils/pgstat_internal.h" 24 : 25 : /* Structures for statistics of injection points */ 26 : typedef struct PgStat_StatInjEntry 27 : { 28 : PgStat_Counter numcalls; /* number of times point has been run */ 29 : } PgStat_StatInjEntry; 30 : 31 : typedef struct PgStatShared_InjectionPoint 32 : { 33 : PgStatShared_Common header; 34 : PgStat_StatInjEntry stats; 35 : } PgStatShared_InjectionPoint; 36 : 37 : static bool injection_stats_flush_cb(PgStat_EntryRef *entry_ref, bool nowait); 38 : 39 : static const PgStat_KindInfo injection_stats = { 40 : .name = "injection_points", 41 : .fixed_amount = false, /* Bounded by the number of points */ 42 : 43 : /* Injection points are system-wide */ 44 : .accessed_across_databases = true, 45 : 46 : .shared_size = sizeof(PgStatShared_InjectionPoint), 47 : .shared_data_off = offsetof(PgStatShared_InjectionPoint, stats), 48 : .shared_data_len = sizeof(((PgStatShared_InjectionPoint *) 0)->stats), 49 : .pending_size = sizeof(PgStat_StatInjEntry), 50 : .flush_pending_cb = injection_stats_flush_cb, 51 : }; 52 : 53 : /* 54 : * Compute stats entry idx from point name with an 8-byte hash. 55 : */ 56 : #define PGSTAT_INJ_IDX(name) hash_bytes_extended((const unsigned char *) name, strlen(name), 0) 57 : 58 : /* 59 : * Kind ID reserved for statistics of injection points. 60 : */ 61 : #define PGSTAT_KIND_INJECTION 129 62 : 63 : /* Track if stats are loaded */ 64 : static bool inj_stats_loaded = false; 65 : 66 : /* 67 : * Callback for stats handling 68 : */ 69 : static bool 70 0 : injection_stats_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) 71 : { 72 : PgStat_StatInjEntry *localent; 73 : PgStatShared_InjectionPoint *shfuncent; 74 : 75 0 : localent = (PgStat_StatInjEntry *) entry_ref->pending; 76 0 : shfuncent = (PgStatShared_InjectionPoint *) entry_ref->shared_stats; 77 : 78 0 : if (!pgstat_lock_entry(entry_ref, nowait)) 79 0 : return false; 80 : 81 0 : shfuncent->stats.numcalls += localent->numcalls; 82 0 : return true; 83 : } 84 : 85 : /* 86 : * Support function for the SQL-callable pgstat* functions. Returns 87 : * a pointer to the injection point statistics struct. 88 : */ 89 : static PgStat_StatInjEntry * 90 6 : pgstat_fetch_stat_injentry(const char *name) 91 : { 92 6 : PgStat_StatInjEntry *entry = NULL; 93 : 94 6 : if (!inj_stats_loaded || !inj_stats_enabled) 95 0 : return NULL; 96 : 97 : /* Compile the lookup key as a hash of the point name */ 98 6 : entry = (PgStat_StatInjEntry *) pgstat_fetch_entry(PGSTAT_KIND_INJECTION, 99 : InvalidOid, 100 6 : PGSTAT_INJ_IDX(name)); 101 6 : return entry; 102 : } 103 : 104 : /* 105 : * Workhorse to do the registration work, called in _PG_init(). 106 : */ 107 : void 108 8 : pgstat_register_inj(void) 109 : { 110 8 : pgstat_register_kind(PGSTAT_KIND_INJECTION, &injection_stats); 111 : 112 : /* mark stats as loaded */ 113 8 : inj_stats_loaded = true; 114 8 : } 115 : 116 : /* 117 : * Report injection point creation. 118 : */ 119 : void 120 70 : pgstat_create_inj(const char *name) 121 : { 122 : PgStat_EntryRef *entry_ref; 123 : PgStatShared_InjectionPoint *shstatent; 124 : 125 : /* leave if disabled */ 126 70 : if (!inj_stats_loaded || !inj_stats_enabled) 127 68 : return; 128 : 129 2 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INJECTION, InvalidOid, 130 2 : PGSTAT_INJ_IDX(name), false); 131 2 : shstatent = (PgStatShared_InjectionPoint *) entry_ref->shared_stats; 132 : 133 : /* initialize shared memory data */ 134 2 : memset(&shstatent->stats, 0, sizeof(shstatent->stats)); 135 2 : pgstat_unlock_entry(entry_ref); 136 : } 137 : 138 : /* 139 : * Report injection point drop. 140 : */ 141 : void 142 158 : pgstat_drop_inj(const char *name) 143 : { 144 : /* leave if disabled */ 145 158 : if (!inj_stats_loaded || !inj_stats_enabled) 146 158 : return; 147 : 148 0 : if (!pgstat_drop_entry(PGSTAT_KIND_INJECTION, InvalidOid, 149 0 : PGSTAT_INJ_IDX(name))) 150 0 : pgstat_request_entry_refs_gc(); 151 : } 152 : 153 : /* 154 : * Report statistics for injection point. 155 : * 156 : * This is simple because the set of stats to report currently is simple: 157 : * track the number of times a point has been run. 158 : */ 159 : void 160 84 : pgstat_report_inj(const char *name) 161 : { 162 : PgStat_EntryRef *entry_ref; 163 : PgStatShared_InjectionPoint *shstatent; 164 : PgStat_StatInjEntry *statent; 165 : 166 : /* leave if disabled */ 167 84 : if (!inj_stats_loaded || !inj_stats_enabled) 168 78 : return; 169 : 170 6 : entry_ref = pgstat_get_entry_ref_locked(PGSTAT_KIND_INJECTION, InvalidOid, 171 6 : PGSTAT_INJ_IDX(name), false); 172 : 173 6 : shstatent = (PgStatShared_InjectionPoint *) entry_ref->shared_stats; 174 6 : statent = &shstatent->stats; 175 : 176 : /* Update the injection point statistics */ 177 6 : statent->numcalls++; 178 : 179 6 : pgstat_unlock_entry(entry_ref); 180 : } 181 : 182 : /* 183 : * SQL function returning the number of times an injection point 184 : * has been called. 185 : */ 186 44 : PG_FUNCTION_INFO_V1(injection_points_stats_numcalls); 187 : Datum 188 6 : injection_points_stats_numcalls(PG_FUNCTION_ARGS) 189 : { 190 6 : char *name = text_to_cstring(PG_GETARG_TEXT_PP(0)); 191 6 : PgStat_StatInjEntry *entry = pgstat_fetch_stat_injentry(name); 192 : 193 6 : if (entry == NULL) 194 2 : PG_RETURN_NULL(); 195 : 196 4 : PG_RETURN_INT64(entry->numcalls); 197 : }