LCOV - differential code coverage report
Current view: top level - src/backend/utils/misc - injection_point.c (source / functions) Coverage Total Hit UNC GNC
Current: Differential Code Coverage 16@8cea358b128 vs 17@8cea358b128 Lines: 90.9 % 77 70 7 70
Current Date: 2024-04-14 14:21:10 Functions: 100.0 % 8 8 8
Baseline: 16@8cea358b128 Branches: 52.4 % 42 22 20 22
Baseline Date: 2024-04-14 14:21:09 Line coverage date bins:
Legend: Lines: hit not hit | Branches: + taken - not taken # not executed (60,120] days: 90.9 % 77 70 7 70
Function coverage date bins:
(60,120] days: 100.0 % 8 8 8
Branch coverage date bins:
(60,120] days: 52.4 % 42 22 20 22

 Age         Owner                    Branch data    TLA  Line data    Source code
                                  1                 :                : /*-------------------------------------------------------------------------
                                  2                 :                :  *
                                  3                 :                :  * injection_point.c
                                  4                 :                :  *    Routines to control and run injection points in the code.
                                  5                 :                :  *
                                  6                 :                :  * Injection points can be used to run arbitrary code by attaching callbacks
                                  7                 :                :  * that would be executed in place of the named injection point.
                                  8                 :                :  *
                                  9                 :                :  * Portions Copyright (c) 1996-2024, 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 <sys/stat.h>
                                 21                 :                : 
                                 22                 :                : #include "fmgr.h"
                                 23                 :                : #include "miscadmin.h"
                                 24                 :                : #include "port/pg_bitutils.h"
                                 25                 :                : #include "storage/fd.h"
                                 26                 :                : #include "storage/lwlock.h"
                                 27                 :                : #include "storage/shmem.h"
                                 28                 :                : #include "utils/hsearch.h"
                                 29                 :                : #include "utils/injection_point.h"
                                 30                 :                : #include "utils/memutils.h"
                                 31                 :                : 
                                 32                 :                : #ifdef USE_INJECTION_POINTS
                                 33                 :                : 
                                 34                 :                : /*
                                 35                 :                :  * Hash table for storing injection points.
                                 36                 :                :  *
                                 37                 :                :  * InjectionPointHash is used to find an injection point by name.
                                 38                 :                :  */
                                 39                 :                : static HTAB *InjectionPointHash;    /* find points from names */
                                 40                 :                : 
                                 41                 :                : /* Field sizes */
                                 42                 :                : #define INJ_NAME_MAXLEN     64
                                 43                 :                : #define INJ_LIB_MAXLEN      128
                                 44                 :                : #define INJ_FUNC_MAXLEN     128
                                 45                 :                : 
                                 46                 :                : /* Single injection point stored in InjectionPointHash */
                                 47                 :                : typedef struct InjectionPointEntry
                                 48                 :                : {
                                 49                 :                :     char        name[INJ_NAME_MAXLEN];  /* hash key */
                                 50                 :                :     char        library[INJ_LIB_MAXLEN];    /* library */
                                 51                 :                :     char        function[INJ_FUNC_MAXLEN];  /* function */
                                 52                 :                : } InjectionPointEntry;
                                 53                 :                : 
                                 54                 :                : #define INJECTION_POINT_HASH_INIT_SIZE  16
                                 55                 :                : #define INJECTION_POINT_HASH_MAX_SIZE   128
                                 56                 :                : 
                                 57                 :                : /*
                                 58                 :                :  * Backend local cache of injection callbacks already loaded, stored in
                                 59                 :                :  * TopMemoryContext.
                                 60                 :                :  */
                                 61                 :                : typedef struct InjectionPointCacheEntry
                                 62                 :                : {
                                 63                 :                :     char        name[INJ_NAME_MAXLEN];
                                 64                 :                :     InjectionPointCallback callback;
                                 65                 :                : } InjectionPointCacheEntry;
                                 66                 :                : 
                                 67                 :                : static HTAB *InjectionPointCache = NULL;
                                 68                 :                : 
                                 69                 :                : /*
                                 70                 :                :  * injection_point_cache_add
                                 71                 :                :  *
                                 72                 :                :  * Add an injection point to the local cache.
                                 73                 :                :  */
                                 74                 :                : static void
   83 michael@paquier.xyz        75                 :GNC          16 : injection_point_cache_add(const char *name,
                                 76                 :                :                           InjectionPointCallback callback)
                                 77                 :                : {
                                 78                 :                :     InjectionPointCacheEntry *entry;
                                 79                 :                :     bool        found;
                                 80                 :                : 
                                 81                 :                :     /* If first time, initialize */
                                 82         [ +  + ]:             16 :     if (InjectionPointCache == NULL)
                                 83                 :                :     {
                                 84                 :                :         HASHCTL     hash_ctl;
                                 85                 :                : 
                                 86                 :              8 :         hash_ctl.keysize = sizeof(char[INJ_NAME_MAXLEN]);
                                 87                 :              8 :         hash_ctl.entrysize = sizeof(InjectionPointCacheEntry);
                                 88                 :              8 :         hash_ctl.hcxt = TopMemoryContext;
                                 89                 :                : 
                                 90                 :              8 :         InjectionPointCache = hash_create("InjectionPoint cache hash",
                                 91                 :                :                                           INJECTION_POINT_HASH_MAX_SIZE,
                                 92                 :                :                                           &hash_ctl,
                                 93                 :                :                                           HASH_ELEM | HASH_STRINGS | HASH_CONTEXT);
                                 94                 :                :     }
                                 95                 :                : 
                                 96                 :                :     entry = (InjectionPointCacheEntry *)
                                 97                 :             16 :         hash_search(InjectionPointCache, name, HASH_ENTER, &found);
                                 98                 :                : 
                                 99         [ -  + ]:             16 :     Assert(!found);
      heikki.linnakangas@i      100                 :             16 :     strlcpy(entry->name, name, sizeof(entry->name));
      michael@paquier.xyz       101                 :             16 :     entry->callback = callback;
                                102                 :             16 : }
                                103                 :                : 
                                104                 :                : /*
                                105                 :                :  * injection_point_cache_remove
                                106                 :                :  *
                                107                 :                :  * Remove entry from the local cache.  Note that this leaks a callback
                                108                 :                :  * loaded but removed later on, which should have no consequence from
                                109                 :                :  * a testing perspective.
                                110                 :                :  */
                                111                 :                : static void
                                112                 :           2402 : injection_point_cache_remove(const char *name)
                                113                 :                : {
                                114                 :                :     /* leave if no cache */
                                115         [ +  + ]:           2402 :     if (InjectionPointCache == NULL)
                                116                 :           1791 :         return;
                                117                 :                : 
                                118                 :            611 :     (void) hash_search(InjectionPointCache, name, HASH_REMOVE, NULL);
                                119                 :                : }
                                120                 :                : 
                                121                 :                : /*
                                122                 :                :  * injection_point_cache_get
                                123                 :                :  *
                                124                 :                :  * Retrieve an injection point from the local cache, if any.
                                125                 :                :  */
                                126                 :                : static InjectionPointCallback
                                127                 :             20 : injection_point_cache_get(const char *name)
                                128                 :                : {
                                129                 :                :     bool        found;
                                130                 :                :     InjectionPointCacheEntry *entry;
                                131                 :                : 
                                132                 :                :     /* no callback if no cache yet */
                                133         [ +  + ]:             20 :     if (InjectionPointCache == NULL)
                                134                 :              8 :         return NULL;
                                135                 :                : 
                                136                 :                :     entry = (InjectionPointCacheEntry *)
                                137                 :             12 :         hash_search(InjectionPointCache, name, HASH_FIND, &found);
                                138                 :                : 
                                139         [ +  + ]:             12 :     if (found)
                                140                 :              4 :         return entry->callback;
                                141                 :                : 
                                142                 :              8 :     return NULL;
                                143                 :                : }
                                144                 :                : #endif                          /* USE_INJECTION_POINTS */
                                145                 :                : 
                                146                 :                : /*
                                147                 :                :  * Return the space for dynamic shared hash table.
                                148                 :                :  */
                                149                 :                : Size
                                150                 :           1679 : InjectionPointShmemSize(void)
                                151                 :                : {
                                152                 :                : #ifdef USE_INJECTION_POINTS
                                153                 :           1679 :     Size        sz = 0;
                                154                 :                : 
                                155                 :           1679 :     sz = add_size(sz, hash_estimate_size(INJECTION_POINT_HASH_MAX_SIZE,
                                156                 :                :                                          sizeof(InjectionPointEntry)));
                                157                 :           1679 :     return sz;
                                158                 :                : #else
                                159                 :                :     return 0;
                                160                 :                : #endif
                                161                 :                : }
                                162                 :                : 
                                163                 :                : /*
                                164                 :                :  * Allocate shmem space for dynamic shared hash.
                                165                 :                :  */
                                166                 :                : void
                                167                 :            898 : InjectionPointShmemInit(void)
                                168                 :                : {
                                169                 :                : #ifdef USE_INJECTION_POINTS
                                170                 :                :     HASHCTL     info;
                                171                 :                : 
                                172                 :                :     /* key is a NULL-terminated string */
                                173                 :            898 :     info.keysize = sizeof(char[INJ_NAME_MAXLEN]);
                                174                 :            898 :     info.entrysize = sizeof(InjectionPointEntry);
                                175                 :            898 :     InjectionPointHash = ShmemInitHash("InjectionPoint hash",
                                176                 :                :                                        INJECTION_POINT_HASH_INIT_SIZE,
                                177                 :                :                                        INJECTION_POINT_HASH_MAX_SIZE,
                                178                 :                :                                        &info,
                                179                 :                :                                        HASH_ELEM | HASH_FIXED_SIZE | HASH_STRINGS);
                                180                 :                : #endif
                                181                 :            898 : }
                                182                 :                : 
                                183                 :                : /*
                                184                 :                :  * Attach a new injection point.
                                185                 :                :  */
                                186                 :                : void
                                187                 :             14 : InjectionPointAttach(const char *name,
                                188                 :                :                      const char *library,
                                189                 :                :                      const char *function)
                                190                 :                : {
                                191                 :                : #ifdef USE_INJECTION_POINTS
                                192                 :                :     InjectionPointEntry *entry_by_name;
                                193                 :                :     bool        found;
                                194                 :                : 
                                195         [ -  + ]:             14 :     if (strlen(name) >= INJ_NAME_MAXLEN)
   83 michael@paquier.xyz       196         [ #  # ]:UNC           0 :         elog(ERROR, "injection point name %s too long (maximum of %u)",
                                197                 :                :              name, INJ_NAME_MAXLEN);
   83 michael@paquier.xyz       198         [ -  + ]:GNC          14 :     if (strlen(library) >= INJ_LIB_MAXLEN)
   83 michael@paquier.xyz       199         [ #  # ]:UNC           0 :         elog(ERROR, "injection point library %s too long (maximum of %u)",
                                200                 :                :              library, INJ_LIB_MAXLEN);
   83 michael@paquier.xyz       201         [ -  + ]:GNC          14 :     if (strlen(function) >= INJ_FUNC_MAXLEN)
   83 michael@paquier.xyz       202         [ #  # ]:UNC           0 :         elog(ERROR, "injection point function %s too long (maximum of %u)",
                                203                 :                :              function, INJ_FUNC_MAXLEN);
                                204                 :                : 
                                205                 :                :     /*
                                206                 :                :      * Allocate and register a new injection point.  A new point should not
                                207                 :                :      * exist.  For testing purposes this should be fine.
                                208                 :                :      */
   83 michael@paquier.xyz       209                 :GNC          14 :     LWLockAcquire(InjectionPointLock, LW_EXCLUSIVE);
                                210                 :                :     entry_by_name = (InjectionPointEntry *)
                                211                 :             14 :         hash_search(InjectionPointHash, name,
                                212                 :                :                     HASH_ENTER, &found);
                                213         [ -  + ]:             14 :     if (found)
                                214                 :                :     {
   83 michael@paquier.xyz       215                 :UNC           0 :         LWLockRelease(InjectionPointLock);
                                216         [ #  # ]:              0 :         elog(ERROR, "injection point \"%s\" already defined", name);
                                217                 :                :     }
                                218                 :                : 
                                219                 :                :     /* Save the entry */
   83 heikki.linnakangas@i      220                 :GNC          14 :     strlcpy(entry_by_name->name, name, sizeof(entry_by_name->name));
      michael@paquier.xyz       221                 :             14 :     entry_by_name->name[INJ_NAME_MAXLEN - 1] = '\0';
      heikki.linnakangas@i      222                 :             14 :     strlcpy(entry_by_name->library, library, sizeof(entry_by_name->library));
      michael@paquier.xyz       223                 :             14 :     entry_by_name->library[INJ_LIB_MAXLEN - 1] = '\0';
      heikki.linnakangas@i      224                 :             14 :     strlcpy(entry_by_name->function, function, sizeof(entry_by_name->function));
      michael@paquier.xyz       225                 :             14 :     entry_by_name->function[INJ_FUNC_MAXLEN - 1] = '\0';
                                226                 :                : 
                                227                 :             14 :     LWLockRelease(InjectionPointLock);
                                228                 :                : 
                                229                 :                : #else
                                230                 :                :     elog(ERROR, "injection points are not supported by this build");
                                231                 :                : #endif
                                232                 :             14 : }
                                233                 :                : 
                                234                 :                : /*
                                235                 :                :  * Detach an existing injection point.
                                236                 :                :  */
                                237                 :                : void
                                238                 :             11 : InjectionPointDetach(const char *name)
                                239                 :                : {
                                240                 :                : #ifdef USE_INJECTION_POINTS
                                241                 :                :     bool        found;
                                242                 :                : 
                                243                 :             11 :     LWLockAcquire(InjectionPointLock, LW_EXCLUSIVE);
                                244                 :             11 :     hash_search(InjectionPointHash, name, HASH_REMOVE, &found);
                                245                 :             11 :     LWLockRelease(InjectionPointLock);
                                246                 :                : 
                                247         [ +  + ]:             11 :     if (!found)
                                248         [ +  - ]:              1 :         elog(ERROR, "injection point \"%s\" not found", name);
                                249                 :                : 
                                250                 :                : #else
                                251                 :                :     elog(ERROR, "Injection points are not supported by this build");
                                252                 :                : #endif
                                253                 :             10 : }
                                254                 :                : 
                                255                 :                : /*
                                256                 :                :  * Execute an injection point, if defined.
                                257                 :                :  *
                                258                 :                :  * Check first the shared hash table, and adapt the local cache depending
                                259                 :                :  * on that as it could be possible that an entry to run has been removed.
                                260                 :                :  */
                                261                 :                : void
                                262                 :           2422 : InjectionPointRun(const char *name)
                                263                 :                : {
                                264                 :                : #ifdef USE_INJECTION_POINTS
                                265                 :                :     InjectionPointEntry *entry_by_name;
                                266                 :                :     bool        found;
                                267                 :                :     InjectionPointCallback injection_callback;
                                268                 :                : 
                                269                 :           2422 :     LWLockAcquire(InjectionPointLock, LW_SHARED);
                                270                 :                :     entry_by_name = (InjectionPointEntry *)
                                271                 :           2422 :         hash_search(InjectionPointHash, name,
                                272                 :                :                     HASH_FIND, &found);
                                273                 :           2422 :     LWLockRelease(InjectionPointLock);
                                274                 :                : 
                                275                 :                :     /*
                                276                 :                :      * If not found, do nothing and remove it from the local cache if it
                                277                 :                :      * existed there.
                                278                 :                :      */
                                279         [ +  + ]:           2422 :     if (!found)
                                280                 :                :     {
                                281                 :           2402 :         injection_point_cache_remove(name);
                                282                 :           2402 :         return;
                                283                 :                :     }
                                284                 :                : 
                                285                 :                :     /*
                                286                 :                :      * Check if the callback exists in the local cache, to avoid unnecessary
                                287                 :                :      * external loads.
                                288                 :                :      */
                                289                 :             20 :     injection_callback = injection_point_cache_get(name);
                                290         [ +  + ]:             20 :     if (injection_callback == NULL)
                                291                 :                :     {
                                292                 :                :         char        path[MAXPGPATH];
                                293                 :                : 
                                294                 :                :         /* not found in local cache, so load and register */
                                295                 :             16 :         snprintf(path, MAXPGPATH, "%s/%s%s", pkglib_path,
                                296                 :             16 :                  entry_by_name->library, DLSUFFIX);
                                297                 :                : 
                                298         [ -  + ]:             16 :         if (!pg_file_exists(path))
   83 michael@paquier.xyz       299         [ #  # ]:UNC           0 :             elog(ERROR, "could not find library \"%s\" for injection point \"%s\"",
                                300                 :                :                  path, name);
                                301                 :                : 
   83 michael@paquier.xyz       302                 :GNC          16 :         injection_callback = (InjectionPointCallback)
   82                           303                 :             16 :             load_external_function(path, entry_by_name->function, false, NULL);
                                304                 :                : 
   83                           305         [ -  + ]:             16 :         if (injection_callback == NULL)
   83 michael@paquier.xyz       306         [ #  # ]:UNC           0 :             elog(ERROR, "could not find function \"%s\" in library \"%s\" for injection point \"%s\"",
                                307                 :                :                  entry_by_name->function, path, name);
                                308                 :                : 
                                309                 :                :         /* add it to the local cache when found */
   83 michael@paquier.xyz       310                 :GNC          16 :         injection_point_cache_add(name, injection_callback);
                                311                 :                :     }
                                312                 :                : 
                                313                 :             20 :     injection_callback(name);
                                314                 :                : #else
                                315                 :                :     elog(ERROR, "Injection points are not supported by this build");
                                316                 :                : #endif
                                317                 :                : }
        

Generated by: LCOV version 2.1-beta2-3-g6141622