LCOV - code coverage report
Current view: top level - src/backend/utils/misc - injection_point.c (source / functions) Coverage Total Hit
Test: PostgreSQL 19devel Lines: 95.1 % 162 154
Test Date: 2026-04-28 15:16:26 Functions: 100.0 % 14 14
Legend: Lines:     hit not hit

            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 *
     122          162 : 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          162 :     if (InjectionPointCache == NULL)
     133              :     {
     134              :         HASHCTL     hash_ctl;
     135              : 
     136          108 :         hash_ctl.keysize = sizeof(char[INJ_NAME_MAXLEN]);
     137          108 :         hash_ctl.entrysize = sizeof(InjectionPointCacheEntry);
     138          108 :         hash_ctl.hcxt = TopMemoryContext;
     139              : 
     140          108 :         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          162 :         hash_search(InjectionPointCache, name, HASH_ENTER, &found);
     148              : 
     149              :     Assert(!found);
     150          162 :     strlcpy(entry->name, name, sizeof(entry->name));
     151          162 :     entry->slot_idx = slot_idx;
     152          162 :     entry->generation = generation;
     153          162 :     entry->callback = callback;
     154          162 :     memcpy(entry->private_data, private_data, INJ_PRIVATE_MAXLEN);
     155              : 
     156          162 :     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
     167           21 : injection_point_cache_remove(const char *name)
     168              : {
     169              :     bool        found PG_USED_FOR_ASSERTS_ONLY;
     170              : 
     171           21 :     (void) hash_search(InjectionPointCache, name, HASH_REMOVE, &found);
     172              :     Assert(found);
     173           21 : }
     174              : 
     175              : /*
     176              :  * injection_point_cache_load
     177              :  *
     178              :  * Load an injection point into the local cache.
     179              :  */
     180              : static InjectionPointCacheEntry *
     181          162 : injection_point_cache_load(InjectionPointEntry *entry, int slot_idx, uint64 generation)
     182              : {
     183              :     char        path[MAXPGPATH];
     184              :     void       *injection_callback_local;
     185              : 
     186          162 :     snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path,
     187          162 :              entry->library, DLSUFFIX);
     188              : 
     189          162 :     if (!pg_file_exists(path))
     190            0 :         elog(ERROR, "could not find library \"%s\" for injection point \"%s\"",
     191              :              path, entry->name);
     192              : 
     193              :     injection_callback_local =
     194          162 :         load_external_function(path, entry->function, false, NULL);
     195              : 
     196          162 :     if (injection_callback_local == NULL)
     197            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 */
     201          324 :     return injection_point_cache_add(entry->name,
     202              :                                      slot_idx,
     203              :                                      generation,
     204              :                                      injection_callback_local,
     205          162 :                                      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 *
     214        24109 : injection_point_cache_get(const char *name)
     215              : {
     216              :     bool        found;
     217              :     InjectionPointCacheEntry *entry;
     218              : 
     219              :     /* no callback if no cache yet */
     220        24109 :     if (InjectionPointCache == NULL)
     221        14566 :         return NULL;
     222              : 
     223              :     entry = (InjectionPointCacheEntry *)
     224         9543 :         hash_search(InjectionPointCache, name, HASH_FIND, &found);
     225              : 
     226         9543 :     if (found)
     227         2807 :         return entry;
     228              : 
     229         6736 :     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
     241         1238 : InjectionPointShmemRequest(void *arg)
     242              : {
     243         1238 :     ShmemRequestStruct(.name = "InjectionPoint hash",
     244              :                        .size = sizeof(InjectionPointsCtl),
     245              :                        .ptr = (void **) &ActiveInjectionPoints,
     246              :         );
     247         1238 : }
     248              : 
     249              : static void
     250         1235 : InjectionPointShmemInit(void *arg)
     251              : {
     252         1235 :     pg_atomic_init_u32(&ActiveInjectionPoints->max_inuse, 0);
     253       159315 :     for (int i = 0; i < MAX_INJECTION_POINTS; i++)
     254       158080 :         pg_atomic_init_u64(&ActiveInjectionPoints->entries[i].generation, 0);
     255         1235 : }
     256              : #endif                          /* USE_INJECTION_POINTS */
     257              : 
     258              : /*
     259              :  * Attach a new injection point.
     260              :  */
     261              : void
     262          132 : 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          132 :     if (strlen(name) >= INJ_NAME_MAXLEN)
     275            1 :         elog(ERROR, "injection point name %s too long (maximum of %u characters)",
     276              :              name, INJ_NAME_MAXLEN - 1);
     277          131 :     if (strlen(library) >= INJ_LIB_MAXLEN)
     278            1 :         elog(ERROR, "injection point library %s too long (maximum of %u characters)",
     279              :              library, INJ_LIB_MAXLEN - 1);
     280          130 :     if (strlen(function) >= INJ_FUNC_MAXLEN)
     281            1 :         elog(ERROR, "injection point function %s too long (maximum of %u characters)",
     282              :              function, INJ_FUNC_MAXLEN - 1);
     283          129 :     if (private_data_size > INJ_PRIVATE_MAXLEN)
     284            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              :      */
     291          128 :     LWLockAcquire(InjectionPointLock, LW_EXCLUSIVE);
     292          128 :     max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
     293          128 :     free_idx = -1;
     294              : 
     295          212 :     for (int idx = 0; idx < max_inuse; idx++)
     296              :     {
     297           84 :         entry = &ActiveInjectionPoints->entries[idx];
     298           84 :         generation = pg_atomic_read_u64(&entry->generation);
     299           84 :         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              :              */
     305            0 :             if (free_idx == -1)
     306            0 :                 free_idx = idx;
     307              :         }
     308           84 :         else if (strcmp(entry->name, name) == 0)
     309            0 :             elog(ERROR, "injection point \"%s\" already defined", name);
     310              :     }
     311          128 :     if (free_idx == -1)
     312              :     {
     313          128 :         if (max_inuse == MAX_INJECTION_POINTS)
     314            0 :             elog(ERROR, "too many injection points");
     315          128 :         free_idx = max_inuse;
     316              :     }
     317          128 :     entry = &ActiveInjectionPoints->entries[free_idx];
     318          128 :     generation = pg_atomic_read_u64(&entry->generation);
     319              :     Assert(generation % 2 == 0);
     320              : 
     321              :     /* Save the entry */
     322          128 :     strlcpy(entry->name, name, sizeof(entry->name));
     323          128 :     strlcpy(entry->library, library, sizeof(entry->library));
     324          128 :     strlcpy(entry->function, function, sizeof(entry->function));
     325          128 :     memset(entry->private_data, 0, INJ_PRIVATE_MAXLEN);
     326          128 :     if (private_data != NULL)
     327          113 :         memcpy(entry->private_data, private_data, private_data_size);
     328              : 
     329          128 :     pg_write_barrier();
     330          128 :     pg_atomic_write_u64(&entry->generation, generation + 1);
     331              : 
     332          128 :     if (free_idx + 1 > max_inuse)
     333          128 :         pg_atomic_write_u32(&ActiveInjectionPoints->max_inuse, free_idx + 1);
     334              : 
     335          128 :     LWLockRelease(InjectionPointLock);
     336              : 
     337              : #else
     338              :     elog(ERROR, "injection points are not supported by this build");
     339              : #endif
     340          128 : }
     341              : 
     342              : /*
     343              :  * Detach an existing injection point.
     344              :  *
     345              :  * Returns true if the injection point was detached, false otherwise.
     346              :  */
     347              : bool
     348          220 : InjectionPointDetach(const char *name)
     349              : {
     350              : #ifdef USE_INJECTION_POINTS
     351          220 :     bool        found = false;
     352              :     int         idx;
     353              :     int         max_inuse;
     354              : 
     355          220 :     LWLockAcquire(InjectionPointLock, LW_EXCLUSIVE);
     356              : 
     357              :     /* Find it in the shmem array, and mark the slot as unused */
     358          220 :     max_inuse = (int) pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
     359          260 :     for (idx = max_inuse - 1; idx >= 0; --idx)
     360              :     {
     361          141 :         InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
     362              :         uint64      generation;
     363              : 
     364          141 :         generation = pg_atomic_read_u64(&entry->generation);
     365          141 :         if (generation % 2 == 0)
     366            2 :             continue;           /* empty slot */
     367              : 
     368          139 :         if (strcmp(entry->name, name) == 0)
     369              :         {
     370              :             Assert(!found);
     371          101 :             found = true;
     372          101 :             pg_atomic_write_u64(&entry->generation, generation + 1);
     373          101 :             break;
     374              :         }
     375              :     }
     376              : 
     377              :     /* If we just removed the highest-numbered entry, update 'max_inuse' */
     378          220 :     if (found && idx == max_inuse - 1)
     379              :     {
     380          182 :         for (; idx >= 0; --idx)
     381              :         {
     382          124 :             InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
     383              :             uint64      generation;
     384              : 
     385          124 :             generation = pg_atomic_read_u64(&entry->generation);
     386          124 :             if (generation % 2 != 0)
     387           23 :                 break;
     388              :         }
     389           81 :         pg_atomic_write_u32(&ActiveInjectionPoints->max_inuse, idx + 1);
     390              :     }
     391          220 :     LWLockRelease(InjectionPointLock);
     392              : 
     393          220 :     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      7344081 : 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      7344081 :     max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
     422      7344081 :     if (max_inuse == 0)
     423              :     {
     424      7327297 :         if (InjectionPointCache)
     425              :         {
     426           55 :             hash_destroy(InjectionPointCache);
     427           55 :             InjectionPointCache = NULL;
     428              :         }
     429      7327297 :         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        16784 :     cached = injection_point_cache_get(name);
     437        16784 :     if (cached)
     438              :     {
     439         2792 :         int         idx = cached->slot_idx;
     440         2792 :         InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
     441              : 
     442         2792 :         if (pg_atomic_read_u64(&entry->generation) == cached->generation)
     443              :         {
     444              :             /* still good */
     445         2771 :             return cached;
     446              :         }
     447           21 :         injection_point_cache_remove(name);
     448           21 :         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        14013 :     namelen = strlen(name);
     461        35351 :     for (int idx = 0; idx < max_inuse; idx++)
     462              :     {
     463        21500 :         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        21500 :         generation = pg_atomic_read_u64(&entry->generation);
     472        21500 :         if (generation % 2 == 0)
     473          155 :             continue;           /* empty slot */
     474        21345 :         pg_read_barrier();
     475              : 
     476              :         /* Is this the injection point we're looking for? */
     477        21345 :         if (memcmp(entry->name, name, namelen + 1) != 0)
     478        21183 :             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          162 :         memcpy(&local_copy, entry, sizeof(InjectionPointEntry));
     487              : 
     488          162 :         pg_read_barrier();
     489          162 :         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              :              */
     500            0 :             continue;
     501              :         }
     502              : 
     503              :         /* Success! Load it into the cache and return it */
     504          162 :         return injection_point_cache_load(&local_copy, idx, generation);
     505              :     }
     506        13851 :     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         7326 : InjectionPointLoad(const char *name)
     519              : {
     520              : #ifdef USE_INJECTION_POINTS
     521         7326 :     InjectionPointCacheRefresh(name);
     522              : #else
     523              :     elog(ERROR, "Injection points are not supported by this build");
     524              : #endif
     525         7326 : }
     526              : 
     527              : /*
     528              :  * Execute an injection point, if defined.
     529              :  */
     530              : void
     531      7248754 : InjectionPointRun(const char *name, void *arg)
     532              : {
     533              : #ifdef USE_INJECTION_POINTS
     534              :     InjectionPointCacheEntry *cache_entry;
     535              : 
     536      7248754 :     cache_entry = InjectionPointCacheRefresh(name);
     537      7248754 :     if (cache_entry)
     538         2904 :         cache_entry->callback(name, cache_entry->private_data, arg);
     539              : #else
     540              :     elog(ERROR, "Injection points are not supported by this build");
     541              : #endif
     542      7248740 : }
     543              : 
     544              : /*
     545              :  * Execute an injection point directly from the cache, if defined.
     546              :  */
     547              : void
     548         7325 : InjectionPointCached(const char *name, void *arg)
     549              : {
     550              : #ifdef USE_INJECTION_POINTS
     551              :     InjectionPointCacheEntry *cache_entry;
     552              : 
     553         7325 :     cache_entry = injection_point_cache_get(name);
     554         7325 :     if (cache_entry)
     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
     559         7325 : }
     560              : 
     561              : /*
     562              :  * Test if an injection point is defined.
     563              :  */
     564              : bool
     565        88001 : IsInjectionPointAttached(const char *name)
     566              : {
     567              : #ifdef USE_INJECTION_POINTS
     568        88001 :     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 *
     581            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)
     602            0 :             continue;
     603              : 
     604            4 :         inj_point = palloc0_object(InjectionPointData);
     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              : }
        

Generated by: LCOV version 2.0-1