1/*--------------------------------------------------------------------------
4 * Code for testing injection points.
6 * Injection points are able to trigger user-defined callbacks in pre-defined
9 * Portions Copyright (c) 1996-2025, PostgreSQL Global Development Group
10 * Portions Copyright (c) 1994, Regents of the University of California
13 * src/test/modules/injection_points/injection_points.c
15 * -------------------------------------------------------------------------
39/* Maximum number of waits usable in injection points at once */
40 #define INJ_MAX_WAIT 8
41 #define INJ_NAME_MAXLEN 64
44 * Conditions related to injection points. This tracks in shared memory the
45 * runtime conditions under which an injection point is allowed to run,
46 * stored as private_data when an injection point is attached, and passed as
47 * argument to the callback.
49 * If more types of runtime conditions need to be tracked, this structure
60 /* Type of the condition */
63 /* ID of the process where the injection point is allowed to run */
68 * List of injection points stored in TopMemoryContext attached
69 * locally to this process.
74 * Shared state information for injection points.
76 * This state data can be initialized in two ways: dynamically with a DSM
77 * or when loading the module.
81 /* Protects access to other fields */
84 /* Counters advancing when injection_points_wakeup() is called */
87 /* Names of injection points attached to wait counters */
90 /* Condition variable used for waits and wakeups */
94/* Pointer to shared-memory state. */
98 const void *private_data,
101 const void *private_data,
104 const void *private_data,
107/* track if injection points attached in this process are linked to it */
113 * This GUC is useful to control if statistics should be enabled or not
114 * during a test with injection points, like for example if a test relies
115 * on a callback run in a critical section where no allocation should happen.
119/* Shared memory init callbacks */
124 * Routine for shared memory area initialization, used as a callback
125 * when initializing dynamically with a DSM or when loading the module.
133 memset(
state->wait_counts, 0,
sizeof(
state->wait_counts));
138/* Shared memory initialization when loading module */
159 /* Create or attach to the shared memory state */
169 * First time through, so initialize. This is shared with the dynamic
170 * initialization using a DSM.
179 * Initialize shared memory area for this module through DSM.
196 * Check runtime conditions associated to an injection point.
198 * Returns true if the named injection point is allowed to run, and false
206 switch (condition->
type)
220 * before_shmem_exit callback to remove injection points linked to a
228 /* Leave if nothing is tracked locally */
232 /* Detach all the local points */
239 /* Remove stats entry */
244/* Set of callbacks available to be attached to an injection point. */
249 char *argstr = (
char *)
arg;
257 elog(
ERROR,
"error triggered for injection point %s (%s)",
267 char *argstr = (
char *)
arg;
275 elog(
NOTICE,
"notice triggered for injection point %s (%s)",
281/* Wait on a condition variable, awaken by injection_points_wakeup() */
285 uint32 old_wait_counts = 0;
287 uint32 injection_wait_event = 0;
299 * Use the injection point name for this custom wait event. Note that
300 * this custom wait event name is not released, but we don't care much for
301 * testing as this should be short-lived.
306 * Find a free slot to wait for, and register this injection point's name.
322 elog(
ERROR,
"could not find free slot for wait of injection point %s ",
335 if (old_wait_counts != new_wait_counts)
341 /* Remove this injection point from the waiters. */
348 * SQL function for creating an injection point.
359 if (strcmp(
action,
"error") == 0)
361 else if (strcmp(
action,
"notice") == 0)
363 else if (strcmp(
action,
"wait") == 0)
366 elog(
ERROR,
"incorrect action \"%s\" for injection point creation",
action);
382 /* Local injection point, so track it for automated cleanup */
388 /* Add entry for stats */
395 * SQL function for loading an injection point.
413 * SQL function for triggering an injection point.
436 * SQL function for triggering an injection point from cache.
459 * SQL function for waking up an injection point waiting in injection_wait().
471 /* First bump the wait counter for the injection point to wake up */
484 elog(
ERROR,
"could not find injection point %s to wake up",
name);
489 /* And broadcast the change to the waiters */
495 * injection_points_set_local
497 * Track if any injection point created in this process ought to run only
498 * in this process. Such injection points are detached automatically when
499 * this process exits. This is useful to make test suites concurrent-safe.
505 /* Enable flag to add a runtime condition based on this process ID */
512 * Register a before_shmem_exit callback to remove any injection points
513 * linked to this process.
521 * SQL function for dropping an injection point.
531 elog(
ERROR,
"could not detach injection point \"%s\"",
name);
533 /* Remove point from local list, if required */
543 /* Remove stats entry */
550 * SQL function for listing all the injection points attached.
556#define NUM_INJECTION_POINTS_LIST 3
561 /* Build a tuplestore to return our results in */
566 foreach(lc, inj_points)
573 memset(nulls, 0,
sizeof(nulls));
579 /* shove row into tuplestore */
584#undef NUM_INJECTION_POINTS_LIST
595 "Enables statistics for injection points.",
607 /* Shared memory initialization */
static Datum values[MAXATTR]
bool ConditionVariableCancelSleep(void)
void ConditionVariableBroadcast(ConditionVariable *cv)
void ConditionVariablePrepareToSleep(ConditionVariable *cv)
void ConditionVariableInit(ConditionVariable *cv)
void ConditionVariableSleep(ConditionVariable *cv, uint32 wait_event_info)
void * GetNamedDSMSegment(const char *name, size_t size, void(*init_callback)(void *ptr), bool *found)
#define PG_GETARG_TEXT_PP(n)
void InitMaterializedSRF(FunctionCallInfo fcinfo, bits32 flags)
void DefineCustomBoolVariable(const char *name, const char *short_desc, const char *long_desc, bool *valueAddr, bool bootValue, GucContext context, int flags, GucBoolCheckHook check_hook, GucBoolAssignHook assign_hook, GucShowHook show_hook)
void MarkGUCPrefixReserved(const char *className)
bool InjectionPointDetach(const char *name)
List * InjectionPointList(void)
void InjectionPointAttach(const char *name, const char *library, const char *function, const void *private_data, int private_data_size)
#define INJECTION_POINT(name, arg)
#define INJECTION_POINT_CACHED(name, arg)
#define INJECTION_POINT_LOAD(name)
Datum injection_points_detach(PG_FUNCTION_ARGS)
static bool injection_point_local
static void injection_shmem_request(void)
PG_FUNCTION_INFO_V1(injection_points_attach)
PGDLLEXPORT void injection_wait(const char *name, const void *private_data, void *arg)
InjectionPointConditionType
static void injection_init_shmem(void)
Datum injection_points_cached(PG_FUNCTION_ARGS)
Datum injection_points_attach(PG_FUNCTION_ARGS)
struct InjectionPointCondition InjectionPointCondition
Datum injection_points_run(PG_FUNCTION_ARGS)
static List * inj_list_local
Datum injection_points_set_local(PG_FUNCTION_ARGS)
static shmem_startup_hook_type prev_shmem_startup_hook
static bool injection_point_allowed(InjectionPointCondition *condition)
static shmem_request_hook_type prev_shmem_request_hook
static void injection_points_cleanup(int code, Datum arg)
static void injection_shmem_startup(void)
PGDLLEXPORT void injection_error(const char *name, const void *private_data, void *arg)
struct InjectionPointSharedState InjectionPointSharedState
#define NUM_INJECTION_POINTS_LIST
Datum injection_points_wakeup(PG_FUNCTION_ARGS)
PGDLLEXPORT void injection_notice(const char *name, const void *private_data, void *arg)
Datum injection_points_list(PG_FUNCTION_ARGS)
static void injection_point_init_state(void *ptr)
static InjectionPointSharedState * inj_state
Datum injection_points_load(PG_FUNCTION_ARGS)
void pgstat_report_inj(const char *name)
void pgstat_register_inj(void)
void pgstat_create_inj(const char *name)
void pgstat_drop_inj(const char *name)
void pgstat_register_inj_fixed(void)
void pgstat_report_inj_fixed(uint32 numattach, uint32 numdetach, uint32 numrun, uint32 numcached, uint32 numloaded)
void before_shmem_exit(pg_on_exit_callback function, Datum arg)
void(* shmem_startup_hook_type)(void)
shmem_startup_hook_type shmem_startup_hook
void RequestAddinShmemSpace(Size size)
List * lappend(List *list, void *datum)
List * list_delete(List *list, void *datum)
bool LWLockAcquire(LWLock *lock, LWLockMode mode)
void LWLockRelease(LWLock *lock)
char * pstrdup(const char *in)
MemoryContext TopMemoryContext
void(* shmem_request_hook_type)(void)
shmem_request_hook_type shmem_request_hook
bool process_shared_preload_libraries_in_progress
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
on_exit_nicely_callback function
size_t strlcpy(char *dst, const char *src, size_t siz)
static Datum PointerGetDatum(const void *X)
void * ShmemInitStruct(const char *name, Size size, bool *foundPtr)
#define SpinLockInit(lock)
#define SpinLockRelease(lock)
#define SpinLockAcquire(lock)
InjectionPointConditionType type
uint32 wait_counts[INJ_MAX_WAIT]
char name[INJ_MAX_WAIT][INJ_NAME_MAXLEN]
ConditionVariable wait_point
Tuplestorestate * setResult
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
String * makeString(char *str)
text * cstring_to_text(const char *s)
char * text_to_cstring(const text *t)
uint32 WaitEventInjectionPointNew(const char *wait_event_name)