Line data Source code
1 : /*-------------------------------------------------------------------------- 2 : * 3 : * injection_stats.c 4 : * Code for statistics of injection points. 5 : * 6 : * Portions Copyright (c) 1996-2025, 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 : .write_to_file = true, 43 : .track_entry_count = true, 44 : 45 : /* Injection points are system-wide */ 46 : .accessed_across_databases = true, 47 : 48 : .shared_size = sizeof(PgStatShared_InjectionPoint), 49 : .shared_data_off = offsetof(PgStatShared_InjectionPoint, stats), 50 : .shared_data_len = sizeof(((PgStatShared_InjectionPoint *) 0)->stats), 51 : .pending_size = sizeof(PgStat_StatInjEntry), 52 : .flush_pending_cb = injection_stats_flush_cb, 53 : }; 54 : 55 : /* 56 : * Compute stats entry idx from point name with an 8-byte hash. 57 : */ 58 : #define PGSTAT_INJ_IDX(name) hash_bytes_extended((const unsigned char *) name, strlen(name), 0) 59 : 60 : /* 61 : * Kind ID reserved for statistics of injection points. 62 : */ 63 : #define PGSTAT_KIND_INJECTION 25 64 : 65 : /* Track if stats are loaded */ 66 : static bool inj_stats_loaded = false; 67 : 68 : /* 69 : * Callback for stats handling 70 : */ 71 : static bool 72 14 : injection_stats_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) 73 : { 74 : PgStat_StatInjEntry *localent; 75 : PgStatShared_InjectionPoint *shfuncent; 76 : 77 14 : localent = (PgStat_StatInjEntry *) entry_ref->pending; 78 14 : shfuncent = (PgStatShared_InjectionPoint *) entry_ref->shared_stats; 79 : 80 14 : if (!pgstat_lock_entry(entry_ref, nowait)) 81 0 : return false; 82 : 83 14 : shfuncent->stats.numcalls += localent->numcalls; 84 : 85 14 : pgstat_unlock_entry(entry_ref); 86 : 87 14 : return true; 88 : } 89 : 90 : /* 91 : * Support function for the SQL-callable pgstat* functions. Returns 92 : * a pointer to the injection point statistics struct. 93 : */ 94 : static PgStat_StatInjEntry * 95 8 : pgstat_fetch_stat_injentry(const char *name) 96 : { 97 8 : PgStat_StatInjEntry *entry = NULL; 98 : 99 8 : if (!inj_stats_loaded || !inj_stats_enabled) 100 0 : return NULL; 101 : 102 : /* Compile the lookup key as a hash of the point name */ 103 8 : entry = (PgStat_StatInjEntry *) pgstat_fetch_entry(PGSTAT_KIND_INJECTION, 104 : InvalidOid, 105 8 : PGSTAT_INJ_IDX(name)); 106 8 : return entry; 107 : } 108 : 109 : /* 110 : * Workhorse to do the registration work, called in _PG_init(). 111 : */ 112 : void 113 10 : pgstat_register_inj(void) 114 : { 115 10 : pgstat_register_kind(PGSTAT_KIND_INJECTION, &injection_stats); 116 : 117 : /* mark stats as loaded */ 118 10 : inj_stats_loaded = true; 119 10 : } 120 : 121 : /* 122 : * Report injection point creation. 123 : */ 124 : void 125 120 : pgstat_create_inj(const char *name) 126 : { 127 : PgStat_EntryRef *entry_ref; 128 : PgStatShared_InjectionPoint *shstatent; 129 : 130 : /* leave if disabled */ 131 120 : if (!inj_stats_loaded || !inj_stats_enabled) 132 116 : return; 133 : 134 4 : entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_INJECTION, InvalidOid, 135 4 : PGSTAT_INJ_IDX(name), NULL); 136 : 137 4 : shstatent = (PgStatShared_InjectionPoint *) entry_ref->shared_stats; 138 : 139 : /* initialize shared memory data */ 140 4 : memset(&shstatent->stats, 0, sizeof(shstatent->stats)); 141 : } 142 : 143 : /* 144 : * Report injection point drop. 145 : */ 146 : void 147 250 : pgstat_drop_inj(const char *name) 148 : { 149 : /* leave if disabled */ 150 250 : if (!inj_stats_loaded || !inj_stats_enabled) 151 250 : return; 152 : 153 0 : if (!pgstat_drop_entry(PGSTAT_KIND_INJECTION, InvalidOid, 154 0 : PGSTAT_INJ_IDX(name))) 155 0 : pgstat_request_entry_refs_gc(); 156 : } 157 : 158 : /* 159 : * Report statistics for injection point. 160 : * 161 : * This is simple because the set of stats to report currently is simple: 162 : * track the number of times a point has been run. 163 : */ 164 : void 165 184 : pgstat_report_inj(const char *name) 166 : { 167 : PgStat_EntryRef *entry_ref; 168 : PgStat_StatInjEntry *pending; 169 : 170 : /* leave if disabled */ 171 184 : if (!inj_stats_loaded || !inj_stats_enabled) 172 174 : return; 173 : 174 10 : entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_INJECTION, InvalidOid, 175 10 : PGSTAT_INJ_IDX(name), NULL); 176 : 177 10 : pending = (PgStat_StatInjEntry *) entry_ref->pending; 178 : 179 : /* Update the injection point pending statistics */ 180 10 : pending->numcalls++; 181 : } 182 : 183 : /* 184 : * SQL function returning the number of times an injection point 185 : * has been called. 186 : */ 187 72 : PG_FUNCTION_INFO_V1(injection_points_stats_numcalls); 188 : Datum 189 8 : injection_points_stats_numcalls(PG_FUNCTION_ARGS) 190 : { 191 8 : char *name = text_to_cstring(PG_GETARG_TEXT_PP(0)); 192 8 : PgStat_StatInjEntry *entry = pgstat_fetch_stat_injentry(name); 193 : 194 8 : if (entry == NULL) 195 4 : PG_RETURN_NULL(); 196 : 197 4 : PG_RETURN_INT64(entry->numcalls); 198 : } 199 : 200 : /* 201 : * SQL function returning the number of entries allocated for injection 202 : * points in the shared hashtable of pgstats. 203 : */ 204 70 : PG_FUNCTION_INFO_V1(injection_points_stats_count); 205 : Datum 206 6 : injection_points_stats_count(PG_FUNCTION_ARGS) 207 : { 208 6 : PG_RETURN_INT64(pgstat_get_entry_count(PGSTAT_KIND_INJECTION)); 209 : } 210 : 211 : /* Only used by injection_points_stats_drop() */ 212 : static bool 213 66 : match_inj_entries(PgStatShared_HashEntry *entry, Datum match_data) 214 : { 215 66 : return entry->key.kind == PGSTAT_KIND_INJECTION; 216 : } 217 : 218 : /* 219 : * SQL function that drops all injection point statistics. 220 : */ 221 66 : PG_FUNCTION_INFO_V1(injection_points_stats_drop); 222 : Datum 223 2 : injection_points_stats_drop(PG_FUNCTION_ARGS) 224 : { 225 2 : pgstat_drop_matching_entries(match_inj_entries, 0); 226 : 227 2 : PG_RETURN_VOID(); 228 : }