PostgreSQL Source Code git master
Loading...
Searching...
No Matches
injection_points.c
Go to the documentation of this file.
1/*--------------------------------------------------------------------------
2 *
3 * injection_points.c
4 * Code for testing injection points.
5 *
6 * Injection points are able to trigger user-defined callbacks in pre-defined
7 * code paths.
8 *
9 * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
10 * Portions Copyright (c) 1994, Regents of the University of California
11 *
12 * IDENTIFICATION
13 * src/test/modules/injection_points/injection_points.c
14 *
15 * -------------------------------------------------------------------------
16 */
17
18#include "postgres.h"
19
20#include "fmgr.h"
21#include "funcapi.h"
22#include "injection_points.h"
23#include "miscadmin.h"
24#include "nodes/pg_list.h"
25#include "nodes/value.h"
28#include "storage/ipc.h"
29#include "storage/lwlock.h"
30#include "storage/shmem.h"
31#include "utils/builtins.h"
32#include "utils/guc.h"
34#include "utils/memutils.h"
35#include "utils/tuplestore.h"
36#include "utils/wait_event.h"
37
39
40/* Maximum number of waits usable in injection points at once */
41#define INJ_MAX_WAIT 8
42#define INJ_NAME_MAXLEN 64
43
44/*
45 * List of injection points stored in TopMemoryContext attached
46 * locally to this process.
47 */
49
50/*
51 * Shared state information for injection points.
52 *
53 * This state data can be initialized in two ways: dynamically with a DSM
54 * or when loading the module.
55 */
57{
58 /* Protects access to other fields */
60
61 /* Counters advancing when injection_points_wakeup() is called */
63
64 /* Names of injection points attached to wait counters */
66
67 /* Condition variable used for waits and wakeups */
70
71/* Pointer to shared-memory state. */
73
74extern PGDLLEXPORT void injection_error(const char *name,
75 const void *private_data,
76 void *arg);
77extern PGDLLEXPORT void injection_notice(const char *name,
78 const void *private_data,
79 void *arg);
80extern PGDLLEXPORT void injection_wait(const char *name,
81 const void *private_data,
82 void *arg);
83
84/* track if injection points attached in this process are linked to it */
85static bool injection_point_local = false;
86
87static void injection_shmem_request(void *arg);
88static void injection_shmem_init(void *arg);
89
94
95/*
96 * Routine for shared memory area initialization, used as a callback
97 * when initializing dynamically with a DSM or when loading the module.
98 */
99static void
101{
103
104 SpinLockInit(&state->lock);
105 memset(state->wait_counts, 0, sizeof(state->wait_counts));
106 memset(state->name, 0, sizeof(state->name));
107 ConditionVariableInit(&state->wait_point);
108}
109
110static void
112{
113 ShmemRequestStruct(.name = "injection_points",
114 .size = sizeof(InjectionPointSharedState),
115 .ptr = (void **) &inj_state,
116 );
117}
118
119static void
121{
122 /*
123 * First time through, so initialize. This is shared with the dynamic
124 * initialization using a DSM.
125 */
127}
128
129/*
130 * Initialize shared memory area for this module through DSM.
131 */
132static void
134{
135 bool found;
136
137 if (inj_state != NULL)
138 return;
139
140 inj_state = GetNamedDSMSegment("injection_points",
143 &found, NULL);
144}
145
146/*
147 * Check runtime conditions associated to an injection point.
148 *
149 * Returns true if the named injection point is allowed to run, and false
150 * otherwise.
151 */
152static bool
154{
155 bool result = true;
156
157 switch (condition->type)
158 {
160 if (MyProcPid != condition->pid)
161 result = false;
162 break;
164 break;
165 }
166
167 return result;
168}
169
170/*
171 * before_shmem_exit callback to remove injection points linked to a
172 * specific process.
173 */
174static void
176{
177 ListCell *lc;
178
179 /* Leave if nothing is tracked locally */
181 return;
182
183 /* Detach all the local points */
184 foreach(lc, inj_list_local)
185 {
186 char *name = strVal(lfirst(lc));
187
189 }
190}
191
192/* Set of callbacks available to be attached to an injection point. */
193void
194injection_error(const char *name, const void *private_data, void *arg)
195{
196 const InjectionPointCondition *condition = private_data;
197 char *argstr = arg;
198
199 if (!injection_point_allowed(condition))
200 return;
201
202 if (argstr)
203 elog(ERROR, "error triggered for injection point %s (%s)",
204 name, argstr);
205 else
206 elog(ERROR, "error triggered for injection point %s", name);
207}
208
209void
210injection_notice(const char *name, const void *private_data, void *arg)
211{
212 const InjectionPointCondition *condition = private_data;
213 char *argstr = arg;
214
215 if (!injection_point_allowed(condition))
216 return;
217
218 if (argstr)
219 elog(NOTICE, "notice triggered for injection point %s (%s)",
220 name, argstr);
221 else
222 elog(NOTICE, "notice triggered for injection point %s", name);
223}
224
225/* Wait on a condition variable, awaken by injection_points_wakeup() */
226void
227injection_wait(const char *name, const void *private_data, void *arg)
228{
230 int index = -1;
232 const InjectionPointCondition *condition = private_data;
233
234 if (inj_state == NULL)
236
237 if (!injection_point_allowed(condition))
238 return;
239
240 /*
241 * Use the injection point name for this custom wait event. Note that
242 * this custom wait event name is not released, but we don't care much for
243 * testing as this should be short-lived.
244 */
246
247 /*
248 * Find a free slot to wait for, and register this injection point's name.
249 */
251 for (int i = 0; i < INJ_MAX_WAIT; i++)
252 {
253 if (inj_state->name[i][0] == '\0')
254 {
255 index = i;
258 break;
259 }
260 }
262
263 if (index < 0)
264 elog(ERROR, "could not find free slot for wait of injection point %s ",
265 name);
266
267 /* And sleep.. */
269 for (;;)
270 {
272
276
278 break;
280 }
282
283 /* Remove this injection point from the waiters. */
285 inj_state->name[index][0] = '\0';
287}
288
289/*
290 * SQL function for creating an injection point.
291 */
293Datum
295{
297 char *action = text_to_cstring(PG_GETARG_TEXT_PP(1));
298 char *function;
299 InjectionPointCondition condition = {0};
300
301 if (strcmp(action, "error") == 0)
302 function = "injection_error";
303 else if (strcmp(action, "notice") == 0)
304 function = "injection_notice";
305 else if (strcmp(action, "wait") == 0)
306 function = "injection_wait";
307 else
308 elog(ERROR, "incorrect action \"%s\" for injection point creation", action);
309
311 {
312 condition.type = INJ_CONDITION_PID;
313 condition.pid = MyProcPid;
314 }
315
316 InjectionPointAttach(name, "injection_points", function, &condition,
318
320 {
322
323 /* Local injection point, so track it for automated cleanup */
327 }
328
330}
331
332/*
333 * SQL function for creating an injection point with library name, function
334 * name and private data.
335 */
337Datum
339{
340 char *name;
341 char *lib_name;
342 char *function;
343 bytea *private_data = NULL;
344 int private_data_size = 0;
345
346 if (PG_ARGISNULL(0))
347 elog(ERROR, "injection point name cannot be NULL");
348 if (PG_ARGISNULL(1))
349 elog(ERROR, "injection point library cannot be NULL");
350 if (PG_ARGISNULL(2))
351 elog(ERROR, "injection point function cannot be NULL");
352
356
357 if (!PG_ARGISNULL(3))
358 {
359 private_data = PG_GETARG_BYTEA_PP(3);
360 private_data_size = VARSIZE_ANY_EXHDR(private_data);
361 }
362
363 if (private_data != NULL)
366 else
368 0);
370}
371
372/*
373 * SQL function for loading an injection point.
374 */
376Datum
388
389/*
390 * SQL function for triggering an injection point.
391 */
393Datum
395{
396 char *name;
397 char *arg = NULL;
398
399 if (PG_ARGISNULL(0))
402
403 if (!PG_ARGISNULL(1))
405
407
409}
410
411/*
412 * SQL function for triggering an injection point from cache.
413 */
415Datum
432
433/*
434 * SQL function for waking up an injection point waiting in injection_wait().
435 */
437Datum
439{
441 int index = -1;
442
443 if (inj_state == NULL)
445
446 /* First bump the wait counter for the injection point to wake up */
448 for (int i = 0; i < INJ_MAX_WAIT; i++)
449 {
450 if (strcmp(name, inj_state->name[i]) == 0)
451 {
452 index = i;
453 break;
454 }
455 }
456 if (index < 0)
457 {
459 elog(ERROR, "could not find injection point %s to wake up", name);
460 }
463
464 /* And broadcast the change to the waiters */
467}
468
469/*
470 * injection_points_set_local
471 *
472 * Track if any injection point created in this process ought to run only
473 * in this process. Such injection points are detached automatically when
474 * this process exits. This is useful to make test suites concurrent-safe.
475 */
477Datum
479{
480 /* Enable flag to add a runtime condition based on this process ID */
482
483 if (inj_state == NULL)
485
486 /*
487 * Register a before_shmem_exit callback to remove any injection points
488 * linked to this process.
489 */
491
493}
494
495/*
496 * SQL function for dropping an injection point.
497 */
499Datum
501{
503
505 elog(ERROR, "could not detach injection point \"%s\"", name);
506
507 /* Remove point from local list, if required */
508 if (inj_list_local != NIL)
509 {
511
515 }
516
518}
519
520/*
521 * SQL function for listing all the injection points attached.
522 */
524Datum
526{
527#define NUM_INJECTION_POINTS_LIST 3
528 ReturnSetInfo *rsinfo = (ReturnSetInfo *) fcinfo->resultinfo;
530 ListCell *lc;
531
532 /* Build a tuplestore to return our results in */
533 InitMaterializedSRF(fcinfo, 0);
534
536
537 foreach(lc, inj_points)
538 {
540 bool nulls[NUM_INJECTION_POINTS_LIST];
542
543 memset(values, 0, sizeof(values));
544 memset(nulls, 0, sizeof(nulls));
545
549
550 /* shove row into tuplestore */
551 tuplestore_putvalues(rsinfo->setResult, rsinfo->setDesc, values, nulls);
552 }
553
554 return (Datum) 0;
555#undef NUM_INJECTION_POINTS_LIST
556}
557
558void
static Datum values[MAXATTR]
Definition bootstrap.c:190
#define PGDLLEXPORT
Definition c.h:1436
uint32_t uint32
Definition c.h:624
uint32 result
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, void *arg), bool *found, void *arg)
Datum arg
Definition elog.c:1323
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define NOTICE
Definition elog.h:36
#define PG_RETURN_VOID()
Definition fmgr.h:350
#define PG_GETARG_BYTEA_PP(n)
Definition fmgr.h:309
#define PG_GETARG_TEXT_PP(n)
Definition fmgr.h:310
#define PG_ARGISNULL(n)
Definition fmgr.h:209
#define PG_FUNCTION_INFO_V1(funcname)
Definition fmgr.h:417
#define PG_FUNCTION_ARGS
Definition fmgr.h:193
void InitMaterializedSRF(FunctionCallInfo fcinfo, uint32 flags)
Definition funcapi.c:76
int MyProcPid
Definition globals.c:49
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
#define INJ_MAX_WAIT
static void injection_point_init_state(void *ptr, void *arg)
void _PG_init(void)
PGDLLEXPORT void injection_wait(const char *name, const void *private_data, void *arg)
static void injection_init_shmem(void)
Datum injection_points_cached(PG_FUNCTION_ARGS)
PG_MODULE_MAGIC
Datum injection_points_attach(PG_FUNCTION_ARGS)
Datum injection_points_run(PG_FUNCTION_ARGS)
static List * inj_list_local
static bool injection_point_allowed(const InjectionPointCondition *condition)
Datum injection_points_set_local(PG_FUNCTION_ARGS)
static void injection_shmem_init(void *arg)
#define INJ_NAME_MAXLEN
static void injection_points_cleanup(int code, Datum arg)
PGDLLEXPORT void injection_error(const char *name, const void *private_data, void *arg)
static const ShmemCallbacks injection_shmem_callbacks
static void injection_shmem_request(void *arg)
Datum injection_points_attach_func(PG_FUNCTION_ARGS)
#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 InjectionPointSharedState * inj_state
Datum injection_points_load(PG_FUNCTION_ARGS)
@ INJ_CONDITION_PID
@ INJ_CONDITION_ALWAYS
void before_shmem_exit(pg_on_exit_callback function, Datum arg)
Definition ipc.c:344
int i
Definition isn.c:77
List * lappend(List *list, void *datum)
Definition list.c:339
List * list_delete(List *list, void *datum)
Definition list.c:853
char * pstrdup(const char *in)
Definition mcxt.c:1910
MemoryContext TopMemoryContext
Definition mcxt.c:167
bool process_shared_preload_libraries_in_progress
Definition miscinit.c:1788
static MemoryContext MemoryContextSwitchTo(MemoryContext context)
Definition palloc.h:138
on_exit_nicely_callback function
#define lfirst(lc)
Definition pg_list.h:172
#define NIL
Definition pg_list.h:68
size_t strlcpy(char *dst, const char *src, size_t siz)
Definition strlcpy.c:45
uint64_t Datum
Definition postgres.h:70
#define PointerGetDatum(X)
Definition postgres.h:354
static int fb(int x)
void RegisterShmemCallbacks(const ShmemCallbacks *callbacks)
Definition shmem.c:873
#define ShmemRequestStruct(...)
Definition shmem.h:176
static void SpinLockRelease(volatile slock_t *lock)
Definition spin.h:62
static void SpinLockAcquire(volatile slock_t *lock)
Definition spin.h:56
static void SpinLockInit(volatile slock_t *lock)
Definition spin.h:50
InjectionPointConditionType type
uint32 wait_counts[INJ_MAX_WAIT]
char name[INJ_MAX_WAIT][INJ_NAME_MAXLEN]
ConditionVariable wait_point
Definition pg_list.h:54
ShmemRequestCallback request_fn
Definition shmem.h:133
Definition type.h:97
Definition c.h:776
void tuplestore_putvalues(Tuplestorestate *state, TupleDesc tdesc, const Datum *values, const bool *isnull)
Definition tuplestore.c:785
String * makeString(char *str)
Definition value.c:63
#define strVal(v)
Definition value.h:82
static Size VARSIZE_ANY_EXHDR(const void *PTR)
Definition varatt.h:472
static char * VARDATA_ANY(const void *PTR)
Definition varatt.h:486
text * cstring_to_text(const char *s)
Definition varlena.c:184
char * text_to_cstring(const text *t)
Definition varlena.c:217
uint32 WaitEventInjectionPointNew(const char *wait_event_name)
Definition wait_event.c:155
const char * name