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 *
834 michael@paquier.xyz 122 :CBC 174 : 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 [ + + ]: 174 : if (InjectionPointCache == NULL)
133 : : {
134 : : HASHCTL hash_ctl;
135 : :
136 : 114 : hash_ctl.keysize = sizeof(char[INJ_NAME_MAXLEN]);
137 : 114 : hash_ctl.entrysize = sizeof(InjectionPointCacheEntry);
138 : 114 : hash_ctl.hcxt = TopMemoryContext;
139 : :
140 : 114 : 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 : 174 : hash_search(InjectionPointCache, name, HASH_ENTER, &found);
148 : :
149 [ - + ]: 174 : Assert(!found);
heikki.linnakangas@i 150 : 174 : strlcpy(entry->name, name, sizeof(entry->name));
659 151 : 174 : entry->slot_idx = slot_idx;
152 : 174 : entry->generation = generation;
834 michael@paquier.xyz 153 : 174 : entry->callback = callback;
659 heikki.linnakangas@i 154 : 174 : memcpy(entry->private_data, private_data, INJ_PRIVATE_MAXLEN);
155 : :
156 : 174 : 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
834 michael@paquier.xyz 167 : 23 : injection_point_cache_remove(const char *name)
168 : : {
169 : : bool found PG_USED_FOR_ASSERTS_ONLY;
170 : :
659 heikki.linnakangas@i 171 : 23 : (void) hash_search(InjectionPointCache, name, HASH_REMOVE, &found);
172 [ - + ]: 23 : Assert(found);
834 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 *
659 heikki.linnakangas@i 181 : 174 : injection_point_cache_load(InjectionPointEntry *entry, int slot_idx, uint64 generation)
182 : : {
183 : : char path[MAXPGPATH];
184 : : void *injection_callback_local;
185 : :
669 michael@paquier.xyz 186 : 174 : snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path,
659 heikki.linnakangas@i 187 : 174 : entry->library, DLSUFFIX);
188 : :
669 michael@paquier.xyz 189 [ - + ]: 174 : if (!pg_file_exists(path))
669 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 =
659 heikki.linnakangas@i 194 :CBC 174 : load_external_function(path, entry->function, false, NULL);
195 : :
669 michael@paquier.xyz 196 [ - + ]: 174 : if (injection_callback_local == NULL)
669 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 */
659 heikki.linnakangas@i 201 :CBC 348 : return injection_point_cache_add(entry->name,
202 : : slot_idx,
203 : : generation,
204 : : injection_callback_local,
205 : 174 : 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 *
669 michael@paquier.xyz 214 : 26853 : injection_point_cache_get(const char *name)
215 : : {
216 : : bool found;
217 : : InjectionPointCacheEntry *entry;
218 : :
219 : : /* no callback if no cache yet */
834 220 [ + + ]: 26853 : if (InjectionPointCache == NULL)
221 : 14588 : return NULL;
222 : :
223 : : entry = (InjectionPointCacheEntry *)
224 : 12265 : hash_search(InjectionPointCache, name, HASH_FIND, &found);
225 : :
226 [ + + ]: 12265 : if (found)
669 227 : 3798 : return entry;
228 : :
834 229 : 8467 : 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
29 heikki.linnakangas@i 241 :GNC 1244 : InjectionPointShmemRequest(void *arg)
242 : : {
243 : 1244 : ShmemRequestStruct(.name = "InjectionPoint hash",
244 : : .size = sizeof(InjectionPointsCtl),
245 : : .ptr = (void **) &ActiveInjectionPoints,
246 : : );
29 heikki.linnakangas@i 247 :GIC 1244 : }
248 : :
249 : : static void
29 heikki.linnakangas@i 250 :GNC 1241 : InjectionPointShmemInit(void *arg)
251 : : {
252 : 1241 : pg_atomic_init_u32(&ActiveInjectionPoints->max_inuse, 0);
253 [ + + ]: 160089 : for (int i = 0; i < MAX_INJECTION_POINTS; i++)
254 : 158848 : pg_atomic_init_u64(&ActiveInjectionPoints->entries[i].generation, 0);
834 michael@paquier.xyz 255 :CBC 1241 : }
256 : : #endif /* USE_INJECTION_POINTS */
257 : :
258 : : /*
259 : : * Attach a new injection point.
260 : : */
261 : : void
262 : 145 : 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 [ + + ]: 145 : if (strlen(name) >= INJ_NAME_MAXLEN)
174 michael@paquier.xyz 275 [ + - ]:GBC 1 : elog(ERROR, "injection point name %s too long (maximum of %u characters)",
276 : : name, INJ_NAME_MAXLEN - 1);
834 michael@paquier.xyz 277 [ + + ]:CBC 144 : if (strlen(library) >= INJ_LIB_MAXLEN)
174 michael@paquier.xyz 278 [ + - ]:GBC 1 : elog(ERROR, "injection point library %s too long (maximum of %u characters)",
279 : : library, INJ_LIB_MAXLEN - 1);
834 michael@paquier.xyz 280 [ + + ]:CBC 143 : if (strlen(function) >= INJ_FUNC_MAXLEN)
174 michael@paquier.xyz 281 [ + - ]:GBC 1 : elog(ERROR, "injection point function %s too long (maximum of %u characters)",
282 : : function, INJ_FUNC_MAXLEN - 1);
174 michael@paquier.xyz 283 [ + + ]:CBC 142 : if (private_data_size > INJ_PRIVATE_MAXLEN)
174 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 : : */
834 michael@paquier.xyz 291 :CBC 141 : LWLockAcquire(InjectionPointLock, LW_EXCLUSIVE);
659 heikki.linnakangas@i 292 : 141 : max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
293 : 141 : free_idx = -1;
294 : :
295 [ + + ]: 231 : 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 : : */
659 heikki.linnakangas@i 305 [ # # ]:UBC 0 : if (free_idx == -1)
306 : 0 : free_idx = idx;
307 : : }
621 noah@leadboat.com 308 [ - + ]:CBC 90 : else if (strcmp(entry->name, name) == 0)
659 heikki.linnakangas@i 309 [ # # ]:UBC 0 : elog(ERROR, "injection point \"%s\" already defined", name);
310 : : }
659 heikki.linnakangas@i 311 [ + - ]:CBC 141 : if (free_idx == -1)
312 : : {
313 [ - + ]: 141 : if (max_inuse == MAX_INJECTION_POINTS)
659 heikki.linnakangas@i 314 [ # # ]:UBC 0 : elog(ERROR, "too many injection points");
659 heikki.linnakangas@i 315 :CBC 141 : free_idx = max_inuse;
316 : : }
317 : 141 : entry = &ActiveInjectionPoints->entries[free_idx];
318 : 141 : generation = pg_atomic_read_u64(&entry->generation);
319 [ - + ]: 141 : Assert(generation % 2 == 0);
320 : :
321 : : /* Save the entry */
322 : 141 : strlcpy(entry->name, name, sizeof(entry->name));
323 : 141 : strlcpy(entry->library, library, sizeof(entry->library));
324 : 141 : strlcpy(entry->function, function, sizeof(entry->function));
25 michael@paquier.xyz 325 : 141 : memset(entry->private_data, 0, INJ_PRIVATE_MAXLEN);
723 326 [ + + ]: 141 : if (private_data != NULL)
659 heikki.linnakangas@i 327 : 120 : memcpy(entry->private_data, private_data, private_data_size);
328 : :
329 : 141 : pg_write_barrier();
330 : 141 : pg_atomic_write_u64(&entry->generation, generation + 1);
331 : :
332 [ + - ]: 141 : if (free_idx + 1 > max_inuse)
333 : 141 : pg_atomic_write_u32(&ActiveInjectionPoints->max_inuse, free_idx + 1);
334 : :
834 michael@paquier.xyz 335 : 141 : LWLockRelease(InjectionPointLock);
336 : :
337 : : #else
338 : : elog(ERROR, "injection points are not supported by this build");
339 : : #endif
340 : 141 : }
341 : :
342 : : /*
343 : : * Detach an existing injection point.
344 : : *
345 : : * Returns true if the injection point was detached, false otherwise.
346 : : */
347 : : bool
348 : 223 : InjectionPointDetach(const char *name)
349 : : {
350 : : #ifdef USE_INJECTION_POINTS
659 heikki.linnakangas@i 351 : 223 : bool found = false;
352 : : int idx;
353 : : int max_inuse;
354 : :
834 michael@paquier.xyz 355 : 223 : LWLockAcquire(InjectionPointLock, LW_EXCLUSIVE);
356 : :
357 : : /* Find it in the shmem array, and mark the slot as unused */
659 heikki.linnakangas@i 358 : 223 : max_inuse = (int) pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
359 [ + + ]: 263 : for (idx = max_inuse - 1; idx >= 0; --idx)
360 : : {
361 : 144 : InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
362 : : uint64 generation;
363 : :
364 : 144 : generation = pg_atomic_read_u64(&entry->generation);
365 [ + + ]: 144 : if (generation % 2 == 0)
366 : 2 : continue; /* empty slot */
367 : :
368 [ + + ]: 142 : if (strcmp(entry->name, name) == 0)
369 : : {
370 [ - + ]: 104 : Assert(!found);
371 : 104 : found = true;
372 : 104 : pg_atomic_write_u64(&entry->generation, generation + 1);
373 : 104 : break;
374 : : }
375 : : }
376 : :
377 : : /* If we just removed the highest-numbered entry, update 'max_inuse' */
378 [ + + + + ]: 223 : if (found && idx == max_inuse - 1)
379 : : {
380 [ + + ]: 188 : for (; idx >= 0; --idx)
381 : : {
382 : 130 : InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
383 : : uint64 generation;
384 : :
385 : 130 : generation = pg_atomic_read_u64(&entry->generation);
386 [ + + ]: 130 : if (generation % 2 != 0)
387 : 26 : break;
388 : : }
389 : 84 : pg_atomic_write_u32(&ActiveInjectionPoints->max_inuse, idx + 1);
390 : : }
391 : 223 : LWLockRelease(InjectionPointLock);
392 : :
393 : 223 : 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 : 6840214 : 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 : 6840214 : max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
422 [ + + ]: 6840214 : if (max_inuse == 0)
423 : : {
424 [ + + ]: 6820671 : if (InjectionPointCache)
425 : : {
426 : 56 : hash_destroy(InjectionPointCache);
427 : 56 : InjectionPointCache = NULL;
428 : : }
429 : 6820671 : 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 : 19543 : cached = injection_point_cache_get(name);
437 [ + + ]: 19543 : if (cached)
438 : : {
439 : 3783 : int idx = cached->slot_idx;
440 : 3783 : InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
441 : :
442 [ + + ]: 3783 : if (pg_atomic_read_u64(&entry->generation) == cached->generation)
443 : : {
444 : : /* still good */
445 : 3760 : return cached;
446 : : }
669 michael@paquier.xyz 447 : 23 : injection_point_cache_remove(name);
659 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 : 15783 : namelen = strlen(name);
461 [ + + ]: 40336 : for (int idx = 0; idx < max_inuse; idx++)
462 : : {
463 : 24727 : 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 : 24727 : generation = pg_atomic_read_u64(&entry->generation);
472 [ + + ]: 24727 : if (generation % 2 == 0)
473 : 155 : continue; /* empty slot */
474 : 24572 : pg_read_barrier();
475 : :
476 : : /* Is this the injection point we're looking for? */
477 [ + + ]: 24572 : if (memcmp(entry->name, name, namelen + 1) != 0)
478 : 24398 : 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 : 174 : memcpy(&local_copy, entry, sizeof(InjectionPointEntry));
487 : :
488 : 174 : pg_read_barrier();
489 [ - + ]: 174 : 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 : : */
659 heikki.linnakangas@i 500 :UBC 0 : continue;
501 : : }
502 : :
503 : : /* Success! Load it into the cache and return it */
659 heikki.linnakangas@i 504 :CBC 174 : return injection_point_cache_load(&local_copy, idx, generation);
505 : : }
506 : 15609 : 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 : 7319 : InjectionPointLoad(const char *name)
519 : : {
520 : : #ifdef USE_INJECTION_POINTS
521 : 7319 : InjectionPointCacheRefresh(name);
522 : : #else
523 : : elog(ERROR, "Injection points are not supported by this build");
524 : : #endif
669 michael@paquier.xyz 525 : 7319 : }
526 : :
527 : : /*
528 : : * Execute an injection point, if defined.
529 : : */
530 : : void
360 531 : 6744673 : InjectionPointRun(const char *name, void *arg)
532 : : {
533 : : #ifdef USE_INJECTION_POINTS
534 : : InjectionPointCacheEntry *cache_entry;
535 : :
659 heikki.linnakangas@i 536 : 6744673 : cache_entry = InjectionPointCacheRefresh(name);
537 [ + + ]: 6744673 : if (cache_entry)
360 michael@paquier.xyz 538 : 3898 : cache_entry->callback(name, cache_entry->private_data, arg);
539 : : #else
540 : : elog(ERROR, "Injection points are not supported by this build");
541 : : #endif
834 542 : 6744655 : }
543 : :
544 : : /*
545 : : * Execute an injection point directly from the cache, if defined.
546 : : */
547 : : void
360 548 : 7310 : InjectionPointCached(const char *name, void *arg)
549 : : {
550 : : #ifdef USE_INJECTION_POINTS
551 : : InjectionPointCacheEntry *cache_entry;
552 : :
656 553 : 7310 : cache_entry = injection_point_cache_get(name);
554 [ + + ]: 7310 : if (cache_entry)
360 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
656 559 : 7310 : }
560 : :
561 : : /*
562 : : * Test if an injection point is defined.
563 : : */
564 : : bool
648 heikki.linnakangas@i 565 : 88222 : IsInjectionPointAttached(const char *name)
566 : : {
567 : : #ifdef USE_INJECTION_POINTS
568 : 88222 : 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 *
306 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)
306 michael@paquier.xyz 602 :UNC 0 : continue;
603 : :
146 michael@paquier.xyz 604 :GNC 4 : inj_point = palloc0_object(InjectionPointData);
306 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 : : }
|