Age Owner Branch data TLA Line data Source code
1 : : /*-------------------------------------------------------------------------
2 : : *
3 : : * injection_point.c
4 : : * Routines to control and run injection points in the code.
5 : : *
6 : : * Injection points can be used to run arbitrary code by attaching callbacks
7 : : * that would be executed in place of the named injection point.
8 : : *
9 : : * Portions Copyright (c) 1996-2026, PostgreSQL Global Development Group
10 : : * Portions Copyright (c) 1994, Regents of the University of California
11 : : *
12 : : *
13 : : * IDENTIFICATION
14 : : * src/backend/utils/misc/injection_point.c
15 : : *
16 : : *-------------------------------------------------------------------------
17 : : */
18 : : #include "postgres.h"
19 : :
20 : : #include "utils/injection_point.h"
21 : :
22 : : #ifdef USE_INJECTION_POINTS
23 : :
24 : : #include <sys/stat.h>
25 : :
26 : : #include "fmgr.h"
27 : : #include "miscadmin.h"
28 : : #include "storage/fd.h"
29 : : #include "storage/lwlock.h"
30 : : #include "storage/shmem.h"
31 : : #include "storage/subsystems.h"
32 : : #include "utils/hsearch.h"
33 : : #include "utils/memutils.h"
34 : :
35 : : /* Field sizes */
36 : : #define INJ_NAME_MAXLEN 64
37 : : #define INJ_LIB_MAXLEN 128
38 : : #define INJ_FUNC_MAXLEN 128
39 : : #define INJ_PRIVATE_MAXLEN 1024
40 : :
41 : : /* Single injection point stored in shared memory */
42 : : typedef struct InjectionPointEntry
43 : : {
44 : : /*
45 : : * Because injection points need to be usable without LWLocks, we use a
46 : : * generation counter on each entry to allow safe, lock-free reading.
47 : : *
48 : : * To read an entry, first read the current 'generation' value. If it's
49 : : * even, then the slot is currently unused, and odd means it's in use.
50 : : * When reading the other fields, beware that they may change while
51 : : * reading them, if the entry is released and reused! After reading the
52 : : * other fields, read 'generation' again: if its value hasn't changed, you
53 : : * can be certain that the other fields you read are valid. Otherwise,
54 : : * the slot was concurrently recycled, and you should ignore it.
55 : : *
56 : : * When adding an entry, you must store all the other fields first, and
57 : : * then update the generation number, with an appropriate memory barrier
58 : : * in between. In addition to that protocol, you must also hold
59 : : * InjectionPointLock, to prevent two backends from modifying the array at
60 : : * the same time.
61 : : */
62 : : pg_atomic_uint64 generation;
63 : :
64 : : char name[INJ_NAME_MAXLEN]; /* point name */
65 : : char library[INJ_LIB_MAXLEN]; /* library */
66 : : char function[INJ_FUNC_MAXLEN]; /* function */
67 : :
68 : : /*
69 : : * Opaque data area that modules can use to pass some custom data to
70 : : * callbacks, registered when attached.
71 : : */
72 : : char private_data[INJ_PRIVATE_MAXLEN];
73 : : } InjectionPointEntry;
74 : :
75 : : #define MAX_INJECTION_POINTS 128
76 : :
77 : : /*
78 : : * Shared memory array of active injection points.
79 : : *
80 : : * 'max_inuse' is the highest index currently in use, plus one. It's just an
81 : : * optimization to avoid scanning through the whole entry, in the common case
82 : : * that there are no injection points, or only a few.
83 : : */
84 : : typedef struct InjectionPointsCtl
85 : : {
86 : : pg_atomic_uint32 max_inuse;
87 : : InjectionPointEntry entries[MAX_INJECTION_POINTS];
88 : : } InjectionPointsCtl;
89 : :
90 : : NON_EXEC_STATIC InjectionPointsCtl *ActiveInjectionPoints;
91 : :
92 : : /*
93 : : * Backend local cache of injection callbacks already loaded, stored in
94 : : * TopMemoryContext.
95 : : */
96 : : typedef struct InjectionPointCacheEntry
97 : : {
98 : : char name[INJ_NAME_MAXLEN];
99 : : char private_data[INJ_PRIVATE_MAXLEN];
100 : : InjectionPointCallback callback;
101 : :
102 : : /*
103 : : * Shmem slot and copy of its generation number when this cache entry was
104 : : * created. They can be used to validate if the cached entry is still
105 : : * valid.
106 : : */
107 : : int slot_idx;
108 : : uint64 generation;
109 : : } InjectionPointCacheEntry;
110 : :
111 : : static HTAB *InjectionPointCache = NULL;
112 : :
113 : : static void InjectionPointShmemRequest(void *arg);
114 : : static void InjectionPointShmemInit(void *arg);
115 : :
116 : : /*
117 : : * injection_point_cache_add
118 : : *
119 : : * Add an injection point to the local cache.
120 : : */
121 : : static InjectionPointCacheEntry *
859 michael@paquier.xyz 122 :CBC 177 : injection_point_cache_add(const char *name,
123 : : int slot_idx,
124 : : uint64 generation,
125 : : InjectionPointCallback callback,
126 : : const void *private_data)
127 : : {
128 : : InjectionPointCacheEntry *entry;
129 : : bool found;
130 : :
131 : : /* If first time, initialize */
132 [ + + ]: 177 : if (InjectionPointCache == NULL)
133 : : {
134 : : HASHCTL hash_ctl;
135 : :
136 : 117 : hash_ctl.keysize = sizeof(char[INJ_NAME_MAXLEN]);
137 : 117 : hash_ctl.entrysize = sizeof(InjectionPointCacheEntry);
138 : 117 : hash_ctl.hcxt = TopMemoryContext;
139 : :
140 : 117 : InjectionPointCache = hash_create("InjectionPoint cache hash",
141 : : MAX_INJECTION_POINTS,
142 : : &hash_ctl,
143 : : HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
144 : : }
145 : :
146 : : entry = (InjectionPointCacheEntry *)
147 : 177 : hash_search(InjectionPointCache, name, HASH_ENTER, &found);
148 : :
149 [ - + ]: 177 : Assert(!found);
heikki.linnakangas@i 150 : 177 : strlcpy(entry->name, name, sizeof(entry->name));
684 151 : 177 : entry->slot_idx = slot_idx;
152 : 177 : entry->generation = generation;
859 michael@paquier.xyz 153 : 177 : entry->callback = callback;
684 heikki.linnakangas@i 154 : 177 : memcpy(entry->private_data, private_data, INJ_PRIVATE_MAXLEN);
155 : :
156 : 177 : return entry;
157 : : }
158 : :
159 : : /*
160 : : * injection_point_cache_remove
161 : : *
162 : : * Remove entry from the local cache. Note that this leaks a callback
163 : : * loaded but removed later on, which should have no consequence from
164 : : * a testing perspective.
165 : : */
166 : : static void
859 michael@paquier.xyz 167 : 23 : injection_point_cache_remove(const char *name)
168 : : {
169 : : bool found PG_USED_FOR_ASSERTS_ONLY;
170 : :
684 heikki.linnakangas@i 171 : 23 : (void) hash_search(InjectionPointCache, name, HASH_REMOVE, &found);
172 [ - + ]: 23 : Assert(found);
859 michael@paquier.xyz 173 : 23 : }
174 : :
175 : : /*
176 : : * injection_point_cache_load
177 : : *
178 : : * Load an injection point into the local cache.
179 : : */
180 : : static InjectionPointCacheEntry *
684 heikki.linnakangas@i 181 : 177 : injection_point_cache_load(InjectionPointEntry *entry, int slot_idx, uint64 generation)
182 : : {
183 : : char path[MAXPGPATH];
184 : : void *injection_callback_local;
185 : :
694 michael@paquier.xyz 186 : 177 : snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path,
684 heikki.linnakangas@i 187 : 177 : entry->library, DLSUFFIX);
188 : :
694 michael@paquier.xyz 189 [ - + ]: 177 : if (!pg_file_exists(path))
694 michael@paquier.xyz 190 [ # # ]:UBC 0 : elog(ERROR, "could not find library \"%s\" for injection point \"%s\"",
191 : : path, entry->name);
192 : :
193 : : injection_callback_local =
684 heikki.linnakangas@i 194 :CBC 177 : load_external_function(path, entry->function, false, NULL);
195 : :
694 michael@paquier.xyz 196 [ - + ]: 177 : if (injection_callback_local == NULL)
694 michael@paquier.xyz 197 [ # # ]:UBC 0 : elog(ERROR, "could not find function \"%s\" in library \"%s\" for injection point \"%s\"",
198 : : entry->function, path, entry->name);
199 : :
200 : : /* add it to the local cache */
684 heikki.linnakangas@i 201 :CBC 354 : return injection_point_cache_add(entry->name,
202 : : slot_idx,
203 : : generation,
204 : : injection_callback_local,
205 : 177 : entry->private_data);
206 : : }
207 : :
208 : : /*
209 : : * injection_point_cache_get
210 : : *
211 : : * Retrieve an injection point from the local cache, if any.
212 : : */
213 : : static InjectionPointCacheEntry *
694 michael@paquier.xyz 214 : 27161 : injection_point_cache_get(const char *name)
215 : : {
216 : : bool found;
217 : : InjectionPointCacheEntry *entry;
218 : :
219 : : /* no callback if no cache yet */
859 220 [ + + ]: 27161 : if (InjectionPointCache == NULL)
221 : 14767 : return NULL;
222 : :
223 : : entry = (InjectionPointCacheEntry *)
224 : 12394 : hash_search(InjectionPointCache, name, HASH_FIND, &found);
225 : :
226 [ + + ]: 12394 : if (found)
694 227 : 3800 : return entry;
228 : :
859 229 : 8594 : return NULL;
230 : : }
231 : :
232 : : const ShmemCallbacks InjectionPointShmemCallbacks = {
233 : : .request_fn = InjectionPointShmemRequest,
234 : : .init_fn = InjectionPointShmemInit,
235 : : };
236 : :
237 : : /*
238 : : * Reserve space for the dynamic shared hash table
239 : : */
240 : : static void
54 heikki.linnakangas@i 241 :GNC 1251 : InjectionPointShmemRequest(void *arg)
242 : : {
243 : 1251 : ShmemRequestStruct(.name = "InjectionPoint hash",
244 : : .size = sizeof(InjectionPointsCtl),
245 : : .ptr = (void **) &ActiveInjectionPoints,
246 : : );
54 heikki.linnakangas@i 247 :GIC 1251 : }
248 : :
249 : : static void
54 heikki.linnakangas@i 250 :GNC 1248 : InjectionPointShmemInit(void *arg)
251 : : {
252 : 1248 : pg_atomic_init_u32(&ActiveInjectionPoints->max_inuse, 0);
253 [ + + ]: 160992 : for (int i = 0; i < MAX_INJECTION_POINTS; i++)
254 : 159744 : pg_atomic_init_u64(&ActiveInjectionPoints->entries[i].generation, 0);
859 michael@paquier.xyz 255 :CBC 1248 : }
256 : : #endif /* USE_INJECTION_POINTS */
257 : :
258 : : /*
259 : : * Attach a new injection point.
260 : : */
261 : : void
262 : 147 : InjectionPointAttach(const char *name,
263 : : const char *library,
264 : : const char *function,
265 : : const void *private_data,
266 : : int private_data_size)
267 : : {
268 : : #ifdef USE_INJECTION_POINTS
269 : : InjectionPointEntry *entry;
270 : : uint64 generation;
271 : : uint32 max_inuse;
272 : : int free_idx;
273 : :
274 [ + + ]: 147 : if (strlen(name) >= INJ_NAME_MAXLEN)
199 michael@paquier.xyz 275 [ + - ]:GBC 1 : elog(ERROR, "injection point name %s too long (maximum of %u characters)",
276 : : name, INJ_NAME_MAXLEN - 1);
859 michael@paquier.xyz 277 [ + + ]:CBC 146 : if (strlen(library) >= INJ_LIB_MAXLEN)
199 michael@paquier.xyz 278 [ + - ]:GBC 1 : elog(ERROR, "injection point library %s too long (maximum of %u characters)",
279 : : library, INJ_LIB_MAXLEN - 1);
859 michael@paquier.xyz 280 [ + + ]:CBC 145 : if (strlen(function) >= INJ_FUNC_MAXLEN)
199 michael@paquier.xyz 281 [ + - ]:GBC 1 : elog(ERROR, "injection point function %s too long (maximum of %u characters)",
282 : : function, INJ_FUNC_MAXLEN - 1);
199 michael@paquier.xyz 283 [ + + ]:CBC 144 : if (private_data_size > INJ_PRIVATE_MAXLEN)
199 michael@paquier.xyz 284 [ + - ]:GBC 1 : elog(ERROR, "injection point data too long (maximum of %u bytes)",
285 : : INJ_PRIVATE_MAXLEN);
286 : :
287 : : /*
288 : : * Allocate and register a new injection point. A new point should not
289 : : * exist. For testing purposes this should be fine.
290 : : */
859 michael@paquier.xyz 291 :CBC 143 : LWLockAcquire(InjectionPointLock, LW_EXCLUSIVE);
684 heikki.linnakangas@i 292 : 143 : max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
293 : 143 : free_idx = -1;
294 : :
295 [ + + ]: 233 : for (int idx = 0; idx < max_inuse; idx++)
296 : : {
297 : 90 : entry = &ActiveInjectionPoints->entries[idx];
298 : 90 : generation = pg_atomic_read_u64(&entry->generation);
299 [ - + ]: 90 : if (generation % 2 == 0)
300 : : {
301 : : /*
302 : : * Found a free slot where we can add the new entry, but keep
303 : : * going so that we will find out if the entry already exists.
304 : : */
684 heikki.linnakangas@i 305 [ # # ]:UBC 0 : if (free_idx == -1)
306 : 0 : free_idx = idx;
307 : : }
646 noah@leadboat.com 308 [ - + ]:CBC 90 : else if (strcmp(entry->name, name) == 0)
684 heikki.linnakangas@i 309 [ # # ]:UBC 0 : elog(ERROR, "injection point \"%s\" already defined", name);
310 : : }
684 heikki.linnakangas@i 311 [ + - ]:CBC 143 : if (free_idx == -1)
312 : : {
313 [ - + ]: 143 : if (max_inuse == MAX_INJECTION_POINTS)
684 heikki.linnakangas@i 314 [ # # ]:UBC 0 : elog(ERROR, "too many injection points");
684 heikki.linnakangas@i 315 :CBC 143 : free_idx = max_inuse;
316 : : }
317 : 143 : entry = &ActiveInjectionPoints->entries[free_idx];
318 : 143 : generation = pg_atomic_read_u64(&entry->generation);
319 [ - + ]: 143 : Assert(generation % 2 == 0);
320 : :
321 : : /* Save the entry */
322 : 143 : strlcpy(entry->name, name, sizeof(entry->name));
323 : 143 : strlcpy(entry->library, library, sizeof(entry->library));
324 : 143 : strlcpy(entry->function, function, sizeof(entry->function));
50 michael@paquier.xyz 325 : 143 : memset(entry->private_data, 0, INJ_PRIVATE_MAXLEN);
748 326 [ + + ]: 143 : if (private_data != NULL)
684 heikki.linnakangas@i 327 : 122 : memcpy(entry->private_data, private_data, private_data_size);
328 : :
329 : 143 : pg_write_barrier();
330 : 143 : pg_atomic_write_u64(&entry->generation, generation + 1);
331 : :
332 [ + - ]: 143 : if (free_idx + 1 > max_inuse)
333 : 143 : pg_atomic_write_u32(&ActiveInjectionPoints->max_inuse, free_idx + 1);
334 : :
859 michael@paquier.xyz 335 : 143 : LWLockRelease(InjectionPointLock);
336 : :
337 : : #else
338 : : elog(ERROR, "injection points are not supported by this build");
339 : : #endif
340 : 143 : }
341 : :
342 : : /*
343 : : * Detach an existing injection point.
344 : : *
345 : : * Returns true if the injection point was detached, false otherwise.
346 : : */
347 : : bool
348 : 227 : InjectionPointDetach(const char *name)
349 : : {
350 : : #ifdef USE_INJECTION_POINTS
684 heikki.linnakangas@i 351 : 227 : bool found = false;
352 : : int idx;
353 : : int max_inuse;
354 : :
859 michael@paquier.xyz 355 : 227 : LWLockAcquire(InjectionPointLock, LW_EXCLUSIVE);
356 : :
357 : : /* Find it in the shmem array, and mark the slot as unused */
684 heikki.linnakangas@i 358 : 227 : max_inuse = (int) pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
359 [ + + ]: 267 : for (idx = max_inuse - 1; idx >= 0; --idx)
360 : : {
361 : 148 : InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
362 : : uint64 generation;
363 : :
364 : 148 : generation = pg_atomic_read_u64(&entry->generation);
365 [ + + ]: 148 : if (generation % 2 == 0)
366 : 2 : continue; /* empty slot */
367 : :
368 [ + + ]: 146 : if (strcmp(entry->name, name) == 0)
369 : : {
370 [ - + ]: 108 : Assert(!found);
371 : 108 : found = true;
372 : 108 : pg_atomic_write_u64(&entry->generation, generation + 1);
373 : 108 : break;
374 : : }
375 : : }
376 : :
377 : : /* If we just removed the highest-numbered entry, update 'max_inuse' */
378 [ + + + + ]: 227 : if (found && idx == max_inuse - 1)
379 : : {
380 [ + + ]: 196 : for (; idx >= 0; --idx)
381 : : {
382 : 134 : InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
383 : : uint64 generation;
384 : :
385 : 134 : generation = pg_atomic_read_u64(&entry->generation);
386 [ + + ]: 134 : if (generation % 2 != 0)
387 : 26 : break;
388 : : }
389 : 88 : pg_atomic_write_u32(&ActiveInjectionPoints->max_inuse, idx + 1);
390 : : }
391 : 227 : LWLockRelease(InjectionPointLock);
392 : :
393 : 227 : return found;
394 : : #else
395 : : elog(ERROR, "Injection points are not supported by this build");
396 : : return true; /* silence compiler */
397 : : #endif
398 : : }
399 : :
400 : : #ifdef USE_INJECTION_POINTS
401 : : /*
402 : : * Common workhorse of InjectionPointRun() and InjectionPointLoad()
403 : : *
404 : : * Checks if an injection point exists in shared memory, and update
405 : : * the local cache entry accordingly.
406 : : */
407 : : static InjectionPointCacheEntry *
408 : 6864034 : InjectionPointCacheRefresh(const char *name)
409 : : {
410 : : uint32 max_inuse;
411 : : int namelen;
412 : : InjectionPointEntry local_copy;
413 : : InjectionPointCacheEntry *cached;
414 : :
415 : : /*
416 : : * First read the number of in-use slots. More entries can be added or
417 : : * existing ones can be removed while we're reading them. If the entry
418 : : * we're looking for is concurrently added or removed, we might or might
419 : : * not see it. That's OK.
420 : : */
421 : 6864034 : max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
422 [ + + ]: 6864034 : if (max_inuse == 0)
423 : : {
424 [ + + ]: 6844220 : if (InjectionPointCache)
425 : : {
426 : 56 : hash_destroy(InjectionPointCache);
427 : 56 : InjectionPointCache = NULL;
428 : : }
429 : 6844220 : return NULL;
430 : : }
431 : :
432 : : /*
433 : : * If we have this entry in the local cache already, check if the cached
434 : : * entry is still valid.
435 : : */
436 : 19814 : cached = injection_point_cache_get(name);
437 [ + + ]: 19814 : if (cached)
438 : : {
439 : 3785 : int idx = cached->slot_idx;
440 : 3785 : InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
441 : :
442 [ + + ]: 3785 : if (pg_atomic_read_u64(&entry->generation) == cached->generation)
443 : : {
444 : : /* still good */
445 : 3762 : return cached;
446 : : }
694 michael@paquier.xyz 447 : 23 : injection_point_cache_remove(name);
684 heikki.linnakangas@i 448 : 23 : cached = NULL;
449 : : }
450 : :
451 : : /*
452 : : * Search the shared memory array.
453 : : *
454 : : * It's possible that the entry we're looking for is concurrently detached
455 : : * or attached. Or detached *and* re-attached, to the same slot or a
456 : : * different slot. Detach and re-attach is not an atomic operation, so
457 : : * it's OK for us to return the old value, NULL, or the new value in such
458 : : * cases.
459 : : */
460 : 16052 : namelen = strlen(name);
461 [ + + ]: 40864 : for (int idx = 0; idx < max_inuse; idx++)
462 : : {
463 : 24989 : InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
464 : : uint64 generation;
465 : :
466 : : /*
467 : : * Read the generation number so that we can detect concurrent
468 : : * modifications. The read barrier ensures that the generation number
469 : : * is loaded before any of the other fields.
470 : : */
471 : 24989 : generation = pg_atomic_read_u64(&entry->generation);
472 [ + + ]: 24989 : if (generation % 2 == 0)
473 : 155 : continue; /* empty slot */
474 : 24834 : pg_read_barrier();
475 : :
476 : : /* Is this the injection point we're looking for? */
477 [ + + ]: 24834 : if (memcmp(entry->name, name, namelen + 1) != 0)
478 : 24657 : continue;
479 : :
480 : : /*
481 : : * The entry can change at any time, if the injection point is
482 : : * concurrently detached. Copy it to local memory, and re-check the
483 : : * generation. If the generation hasn't changed, we know our local
484 : : * copy is coherent.
485 : : */
486 : 177 : memcpy(&local_copy, entry, sizeof(InjectionPointEntry));
487 : :
488 : 177 : pg_read_barrier();
489 [ - + ]: 177 : if (pg_atomic_read_u64(&entry->generation) != generation)
490 : : {
491 : : /*
492 : : * The entry was concurrently detached.
493 : : *
494 : : * Continue the search, because if the generation number changed,
495 : : * we cannot trust the result of the name comparison we did above.
496 : : * It's theoretically possible that it falsely matched a mixed-up
497 : : * state of the old and new name, if the slot was recycled with a
498 : : * different name.
499 : : */
684 heikki.linnakangas@i 500 :UBC 0 : continue;
501 : : }
502 : :
503 : : /* Success! Load it into the cache and return it */
684 heikki.linnakangas@i 504 :CBC 177 : return injection_point_cache_load(&local_copy, idx, generation);
505 : : }
506 : 15875 : return NULL;
507 : : }
508 : : #endif
509 : :
510 : : /*
511 : : * Load an injection point into the local cache.
512 : : *
513 : : * This is useful to be able to load an injection point before running it,
514 : : * especially if the injection point is called in a code path where memory
515 : : * allocations cannot happen, like critical sections.
516 : : */
517 : : void
518 : 7358 : InjectionPointLoad(const char *name)
519 : : {
520 : : #ifdef USE_INJECTION_POINTS
521 : 7358 : InjectionPointCacheRefresh(name);
522 : : #else
523 : : elog(ERROR, "Injection points are not supported by this build");
524 : : #endif
694 michael@paquier.xyz 525 : 7358 : }
526 : :
527 : : /*
528 : : * Execute an injection point, if defined.
529 : : */
530 : : void
385 531 : 6768323 : InjectionPointRun(const char *name, void *arg)
532 : : {
533 : : #ifdef USE_INJECTION_POINTS
534 : : InjectionPointCacheEntry *cache_entry;
535 : :
684 heikki.linnakangas@i 536 : 6768323 : cache_entry = InjectionPointCacheRefresh(name);
537 [ + + ]: 6768323 : if (cache_entry)
385 michael@paquier.xyz 538 : 3904 : cache_entry->callback(name, cache_entry->private_data, arg);
539 : : #else
540 : : elog(ERROR, "Injection points are not supported by this build");
541 : : #endif
859 542 : 6768304 : }
543 : :
544 : : /*
545 : : * Execute an injection point directly from the cache, if defined.
546 : : */
547 : : void
385 548 : 7347 : InjectionPointCached(const char *name, void *arg)
549 : : {
550 : : #ifdef USE_INJECTION_POINTS
551 : : InjectionPointCacheEntry *cache_entry;
552 : :
681 553 : 7347 : cache_entry = injection_point_cache_get(name);
554 [ + + ]: 7347 : if (cache_entry)
385 555 : 15 : cache_entry->callback(name, cache_entry->private_data, arg);
556 : : #else
557 : : elog(ERROR, "Injection points are not supported by this build");
558 : : #endif
681 559 : 7347 : }
560 : :
561 : : /*
562 : : * Test if an injection point is defined.
563 : : */
564 : : bool
673 heikki.linnakangas@i 565 : 88353 : IsInjectionPointAttached(const char *name)
566 : : {
567 : : #ifdef USE_INJECTION_POINTS
568 : 88353 : return InjectionPointCacheRefresh(name) != NULL;
569 : : #else
570 : : elog(ERROR, "Injection points are not supported by this build");
571 : : return false; /* silence compiler */
572 : : #endif
573 : : }
574 : :
575 : : /*
576 : : * Retrieve a list of all the injection points currently attached.
577 : : *
578 : : * This list is palloc'd in the current memory context.
579 : : */
580 : : List *
331 michael@paquier.xyz 581 :GNC 3 : InjectionPointList(void)
582 : : {
583 : : #ifdef USE_INJECTION_POINTS
584 : 3 : List *inj_points = NIL;
585 : : uint32 max_inuse;
586 : :
587 : 3 : LWLockAcquire(InjectionPointLock, LW_SHARED);
588 : :
589 : 3 : max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
590 : :
591 [ + + ]: 7 : for (uint32 idx = 0; idx < max_inuse; idx++)
592 : : {
593 : : InjectionPointEntry *entry;
594 : : InjectionPointData *inj_point;
595 : : uint64 generation;
596 : :
597 : 4 : entry = &ActiveInjectionPoints->entries[idx];
598 : 4 : generation = pg_atomic_read_u64(&entry->generation);
599 : :
600 : : /* skip free slots */
601 [ - + ]: 4 : if (generation % 2 == 0)
331 michael@paquier.xyz 602 :UNC 0 : continue;
603 : :
171 michael@paquier.xyz 604 :GNC 4 : inj_point = palloc0_object(InjectionPointData);
331 605 : 4 : inj_point->name = pstrdup(entry->name);
606 : 4 : inj_point->library = pstrdup(entry->library);
607 : 4 : inj_point->function = pstrdup(entry->function);
608 : 4 : inj_points = lappend(inj_points, inj_point);
609 : : }
610 : :
611 : 3 : LWLockRelease(InjectionPointLock);
612 : :
613 : 3 : return inj_points;
614 : :
615 : : #else
616 : : elog(ERROR, "Injection points are not supported by this build");
617 : : return NIL; /* keep compiler quiet */
618 : : #endif
619 : : }
|