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