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.0 % 161 153
Test Date: 2026-04-07 14:16:30 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        23740 : injection_point_cache_get(const char *name)
     215              : {
     216              :     bool        found;
     217              :     InjectionPointCacheEntry *entry;
     218              : 
     219              :     /* no callback if no cache yet */
     220        23740 :     if (InjectionPointCache == NULL)
     221        14166 :         return NULL;
     222              : 
     223              :     entry = (InjectionPointCacheEntry *)
     224         9574 :         hash_search(InjectionPointCache, name, HASH_FIND, &found);
     225              : 
     226         9574 :     if (found)
     227         2806 :         return entry;
     228              : 
     229         6768 :     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         1234 : InjectionPointShmemRequest(void *arg)
     242              : {
     243         1234 :     ShmemRequestStruct(.name = "InjectionPoint hash",
     244              :                        .size = sizeof(InjectionPointsCtl),
     245              :                        .ptr = (void **) &ActiveInjectionPoints,
     246              :         );
     247         1234 : }
     248              : 
     249              : static void
     250         1231 : InjectionPointShmemInit(void *arg)
     251              : {
     252         1231 :     pg_atomic_init_u32(&ActiveInjectionPoints->max_inuse, 0);
     253       158799 :     for (int i = 0; i < MAX_INJECTION_POINTS; i++)
     254       157568 :         pg_atomic_init_u64(&ActiveInjectionPoints->entries[i].generation, 0);
     255         1231 : }
     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 :     if (private_data != NULL)
     326          113 :         memcpy(entry->private_data, private_data, private_data_size);
     327              : 
     328          128 :     pg_write_barrier();
     329          128 :     pg_atomic_write_u64(&entry->generation, generation + 1);
     330              : 
     331          128 :     if (free_idx + 1 > max_inuse)
     332          128 :         pg_atomic_write_u32(&ActiveInjectionPoints->max_inuse, free_idx + 1);
     333              : 
     334          128 :     LWLockRelease(InjectionPointLock);
     335              : 
     336              : #else
     337              :     elog(ERROR, "injection points are not supported by this build");
     338              : #endif
     339          128 : }
     340              : 
     341              : /*
     342              :  * Detach an existing injection point.
     343              :  *
     344              :  * Returns true if the injection point was detached, false otherwise.
     345              :  */
     346              : bool
     347          220 : InjectionPointDetach(const char *name)
     348              : {
     349              : #ifdef USE_INJECTION_POINTS
     350          220 :     bool        found = false;
     351              :     int         idx;
     352              :     int         max_inuse;
     353              : 
     354          220 :     LWLockAcquire(InjectionPointLock, LW_EXCLUSIVE);
     355              : 
     356              :     /* Find it in the shmem array, and mark the slot as unused */
     357          220 :     max_inuse = (int) pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
     358          260 :     for (idx = max_inuse - 1; idx >= 0; --idx)
     359              :     {
     360          141 :         InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
     361              :         uint64      generation;
     362              : 
     363          141 :         generation = pg_atomic_read_u64(&entry->generation);
     364          141 :         if (generation % 2 == 0)
     365            2 :             continue;           /* empty slot */
     366              : 
     367          139 :         if (strcmp(entry->name, name) == 0)
     368              :         {
     369              :             Assert(!found);
     370          101 :             found = true;
     371          101 :             pg_atomic_write_u64(&entry->generation, generation + 1);
     372          101 :             break;
     373              :         }
     374              :     }
     375              : 
     376              :     /* If we just removed the highest-numbered entry, update 'max_inuse' */
     377          220 :     if (found && idx == max_inuse - 1)
     378              :     {
     379          182 :         for (; idx >= 0; --idx)
     380              :         {
     381          124 :             InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
     382              :             uint64      generation;
     383              : 
     384          124 :             generation = pg_atomic_read_u64(&entry->generation);
     385          124 :             if (generation % 2 != 0)
     386           23 :                 break;
     387              :         }
     388           81 :         pg_atomic_write_u32(&ActiveInjectionPoints->max_inuse, idx + 1);
     389              :     }
     390          220 :     LWLockRelease(InjectionPointLock);
     391              : 
     392          220 :     return found;
     393              : #else
     394              :     elog(ERROR, "Injection points are not supported by this build");
     395              :     return true;                /* silence compiler */
     396              : #endif
     397              : }
     398              : 
     399              : #ifdef USE_INJECTION_POINTS
     400              : /*
     401              :  * Common workhorse of InjectionPointRun() and InjectionPointLoad()
     402              :  *
     403              :  * Checks if an injection point exists in shared memory, and update
     404              :  * the local cache entry accordingly.
     405              :  */
     406              : static InjectionPointCacheEntry *
     407      7358597 : InjectionPointCacheRefresh(const char *name)
     408              : {
     409              :     uint32      max_inuse;
     410              :     int         namelen;
     411              :     InjectionPointEntry local_copy;
     412              :     InjectionPointCacheEntry *cached;
     413              : 
     414              :     /*
     415              :      * First read the number of in-use slots.  More entries can be added or
     416              :      * existing ones can be removed while we're reading them.  If the entry
     417              :      * we're looking for is concurrently added or removed, we might or might
     418              :      * not see it.  That's OK.
     419              :      */
     420      7358597 :     max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
     421      7358597 :     if (max_inuse == 0)
     422              :     {
     423      7342177 :         if (InjectionPointCache)
     424              :         {
     425           54 :             hash_destroy(InjectionPointCache);
     426           54 :             InjectionPointCache = NULL;
     427              :         }
     428      7342177 :         return NULL;
     429              :     }
     430              : 
     431              :     /*
     432              :      * If we have this entry in the local cache already, check if the cached
     433              :      * entry is still valid.
     434              :      */
     435        16420 :     cached = injection_point_cache_get(name);
     436        16420 :     if (cached)
     437              :     {
     438         2791 :         int         idx = cached->slot_idx;
     439         2791 :         InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
     440              : 
     441         2791 :         if (pg_atomic_read_u64(&entry->generation) == cached->generation)
     442              :         {
     443              :             /* still good */
     444         2770 :             return cached;
     445              :         }
     446           21 :         injection_point_cache_remove(name);
     447           21 :         cached = NULL;
     448              :     }
     449              : 
     450              :     /*
     451              :      * Search the shared memory array.
     452              :      *
     453              :      * It's possible that the entry we're looking for is concurrently detached
     454              :      * or attached.  Or detached *and* re-attached, to the same slot or a
     455              :      * different slot.  Detach and re-attach is not an atomic operation, so
     456              :      * it's OK for us to return the old value, NULL, or the new value in such
     457              :      * cases.
     458              :      */
     459        13650 :     namelen = strlen(name);
     460        34624 :     for (int idx = 0; idx < max_inuse; idx++)
     461              :     {
     462        21136 :         InjectionPointEntry *entry = &ActiveInjectionPoints->entries[idx];
     463              :         uint64      generation;
     464              : 
     465              :         /*
     466              :          * Read the generation number so that we can detect concurrent
     467              :          * modifications.  The read barrier ensures that the generation number
     468              :          * is loaded before any of the other fields.
     469              :          */
     470        21136 :         generation = pg_atomic_read_u64(&entry->generation);
     471        21136 :         if (generation % 2 == 0)
     472          155 :             continue;           /* empty slot */
     473        20981 :         pg_read_barrier();
     474              : 
     475              :         /* Is this the injection point we're looking for? */
     476        20981 :         if (memcmp(entry->name, name, namelen + 1) != 0)
     477        20819 :             continue;
     478              : 
     479              :         /*
     480              :          * The entry can change at any time, if the injection point is
     481              :          * concurrently detached.  Copy it to local memory, and re-check the
     482              :          * generation.  If the generation hasn't changed, we know our local
     483              :          * copy is coherent.
     484              :          */
     485          162 :         memcpy(&local_copy, entry, sizeof(InjectionPointEntry));
     486              : 
     487          162 :         pg_read_barrier();
     488          162 :         if (pg_atomic_read_u64(&entry->generation) != generation)
     489              :         {
     490              :             /*
     491              :              * The entry was concurrently detached.
     492              :              *
     493              :              * Continue the search, because if the generation number changed,
     494              :              * we cannot trust the result of the name comparison we did above.
     495              :              * It's theoretically possible that it falsely matched a mixed-up
     496              :              * state of the old and new name, if the slot was recycled with a
     497              :              * different name.
     498              :              */
     499            0 :             continue;
     500              :         }
     501              : 
     502              :         /* Success! Load it into the cache and return it */
     503          162 :         return injection_point_cache_load(&local_copy, idx, generation);
     504              :     }
     505        13488 :     return NULL;
     506              : }
     507              : #endif
     508              : 
     509              : /*
     510              :  * Load an injection point into the local cache.
     511              :  *
     512              :  * This is useful to be able to load an injection point before running it,
     513              :  * especially if the injection point is called in a code path where memory
     514              :  * allocations cannot happen, like critical sections.
     515              :  */
     516              : void
     517         7325 : InjectionPointLoad(const char *name)
     518              : {
     519              : #ifdef USE_INJECTION_POINTS
     520         7325 :     InjectionPointCacheRefresh(name);
     521              : #else
     522              :     elog(ERROR, "Injection points are not supported by this build");
     523              : #endif
     524         7325 : }
     525              : 
     526              : /*
     527              :  * Execute an injection point, if defined.
     528              :  */
     529              : void
     530      7263391 : InjectionPointRun(const char *name, void *arg)
     531              : {
     532              : #ifdef USE_INJECTION_POINTS
     533              :     InjectionPointCacheEntry *cache_entry;
     534              : 
     535      7263391 :     cache_entry = InjectionPointCacheRefresh(name);
     536      7263391 :     if (cache_entry)
     537         2903 :         cache_entry->callback(name, cache_entry->private_data, arg);
     538              : #else
     539              :     elog(ERROR, "Injection points are not supported by this build");
     540              : #endif
     541      7263377 : }
     542              : 
     543              : /*
     544              :  * Execute an injection point directly from the cache, if defined.
     545              :  */
     546              : void
     547         7320 : InjectionPointCached(const char *name, void *arg)
     548              : {
     549              : #ifdef USE_INJECTION_POINTS
     550              :     InjectionPointCacheEntry *cache_entry;
     551              : 
     552         7320 :     cache_entry = injection_point_cache_get(name);
     553         7320 :     if (cache_entry)
     554           15 :         cache_entry->callback(name, cache_entry->private_data, arg);
     555              : #else
     556              :     elog(ERROR, "Injection points are not supported by this build");
     557              : #endif
     558         7320 : }
     559              : 
     560              : /*
     561              :  * Test if an injection point is defined.
     562              :  */
     563              : bool
     564        87881 : IsInjectionPointAttached(const char *name)
     565              : {
     566              : #ifdef USE_INJECTION_POINTS
     567        87881 :     return InjectionPointCacheRefresh(name) != NULL;
     568              : #else
     569              :     elog(ERROR, "Injection points are not supported by this build");
     570              :     return false;               /* silence compiler */
     571              : #endif
     572              : }
     573              : 
     574              : /*
     575              :  * Retrieve a list of all the injection points currently attached.
     576              :  *
     577              :  * This list is palloc'd in the current memory context.
     578              :  */
     579              : List *
     580            3 : InjectionPointList(void)
     581              : {
     582              : #ifdef USE_INJECTION_POINTS
     583            3 :     List       *inj_points = NIL;
     584              :     uint32      max_inuse;
     585              : 
     586            3 :     LWLockAcquire(InjectionPointLock, LW_SHARED);
     587              : 
     588            3 :     max_inuse = pg_atomic_read_u32(&ActiveInjectionPoints->max_inuse);
     589              : 
     590            7 :     for (uint32 idx = 0; idx < max_inuse; idx++)
     591              :     {
     592              :         InjectionPointEntry *entry;
     593              :         InjectionPointData *inj_point;
     594              :         uint64      generation;
     595              : 
     596            4 :         entry = &ActiveInjectionPoints->entries[idx];
     597            4 :         generation = pg_atomic_read_u64(&entry->generation);
     598              : 
     599              :         /* skip free slots */
     600            4 :         if (generation % 2 == 0)
     601            0 :             continue;
     602              : 
     603            4 :         inj_point = palloc0_object(InjectionPointData);
     604            4 :         inj_point->name = pstrdup(entry->name);
     605            4 :         inj_point->library = pstrdup(entry->library);
     606            4 :         inj_point->function = pstrdup(entry->function);
     607            4 :         inj_points = lappend(inj_points, inj_point);
     608              :     }
     609              : 
     610            3 :     LWLockRelease(InjectionPointLock);
     611              : 
     612            3 :     return inj_points;
     613              : 
     614              : #else
     615              :     elog(ERROR, "Injection points are not supported by this build");
     616              :     return NIL;                 /* keep compiler quiet */
     617              : #endif
     618              : }
        

Generated by: LCOV version 2.0-1