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

Generated by: LCOV version 2.0-1