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 8 : injection_stats_flush_cb(PgStat_EntryRef *entry_ref, bool nowait) 72 : { 73 : PgStat_StatInjEntry *localent; 74 : PgStatShared_InjectionPoint *shfuncent; 75 : 76 8 : localent = (PgStat_StatInjEntry *) entry_ref->pending; 77 8 : shfuncent = (PgStatShared_InjectionPoint *) entry_ref->shared_stats; 78 : 79 8 : if (!pgstat_lock_entry(entry_ref, nowait)) 80 0 : return false; 81 : 82 8 : shfuncent->stats.numcalls += localent->numcalls; 83 : 84 8 : pgstat_unlock_entry(entry_ref); 85 : 86 8 : 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 6 : pgstat_fetch_stat_injentry(const char *name) 95 : { 96 6 : PgStat_StatInjEntry *entry = NULL; 97 : 98 6 : 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 6 : entry = (PgStat_StatInjEntry *) pgstat_fetch_entry(PGSTAT_KIND_INJECTION, 103 : InvalidOid, 104 6 : PGSTAT_INJ_IDX(name)); 105 6 : 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 70 : pgstat_create_inj(const char *name) 125 : { 126 : PgStat_EntryRef *entry_ref; 127 : PgStatShared_InjectionPoint *shstatent; 128 : 129 : /* leave if disabled */ 130 70 : if (!inj_stats_loaded || !inj_stats_enabled) 131 68 : return; 132 : 133 2 : entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_INJECTION, InvalidOid, 134 2 : PGSTAT_INJ_IDX(name), NULL); 135 : 136 2 : shstatent = (PgStatShared_InjectionPoint *) entry_ref->shared_stats; 137 : 138 : /* initialize shared memory data */ 139 2 : memset(&shstatent->stats, 0, sizeof(shstatent->stats)); 140 : } 141 : 142 : /* 143 : * Report injection point drop. 144 : */ 145 : void 146 158 : pgstat_drop_inj(const char *name) 147 : { 148 : /* leave if disabled */ 149 158 : if (!inj_stats_loaded || !inj_stats_enabled) 150 158 : 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 82 : 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 82 : if (!inj_stats_loaded || !inj_stats_enabled) 172 76 : return; 173 : 174 6 : entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_INJECTION, InvalidOid, 175 6 : PGSTAT_INJ_IDX(name), NULL); 176 : 177 6 : shstatent = (PgStatShared_InjectionPoint *) entry_ref->shared_stats; 178 6 : statent = &shstatent->stats; 179 : 180 : /* Update the injection point statistics */ 181 6 : statent->numcalls++; 182 : } 183 : 184 : /* 185 : * SQL function returning the number of times an injection point 186 : * has been called. 187 : */ 188 46 : PG_FUNCTION_INFO_V1(injection_points_stats_numcalls); 189 : Datum 190 6 : injection_points_stats_numcalls(PG_FUNCTION_ARGS) 191 : { 192 6 : char *name = text_to_cstring(PG_GETARG_TEXT_PP(0)); 193 6 : PgStat_StatInjEntry *entry = pgstat_fetch_stat_injentry(name); 194 : 195 6 : if (entry == NULL) 196 2 : PG_RETURN_NULL(); 197 : 198 4 : PG_RETURN_INT64(entry->numcalls); 199 : }