LCOV - differential code coverage report
Current view: top level - src/backend/utils/activity - pgstat_function.c (source / functions) Coverage Total Hit UBC GNC CBC DCB
Current: Differential Code Coverage HEAD vs 15 Lines: 93.2 % 59 55 4 20 35 20
Current Date: 2023-04-08 15:15:32 Functions: 100.0 % 7 7 3 4
Baseline: 15
Baseline Date: 2023-04-08 15:09:40
Legend: Lines: hit not hit

           TLA  Line data    Source code
       1                 : /* -------------------------------------------------------------------------
       2                 :  *
       3                 :  * pgstat_function.c
       4                 :  *    Implementation of function statistics.
       5                 :  *
       6                 :  * This file contains the implementation of function statistics. It is kept
       7                 :  * separate from pgstat.c to enforce the line between the statistics access /
       8                 :  * storage implementation and the details about individual types of
       9                 :  * statistics.
      10                 :  *
      11                 :  * Copyright (c) 2001-2023, PostgreSQL Global Development Group
      12                 :  *
      13                 :  * IDENTIFICATION
      14                 :  *    src/backend/utils/activity/pgstat_function.c
      15                 :  * -------------------------------------------------------------------------
      16                 :  */
      17                 : 
      18                 : #include "postgres.h"
      19                 : 
      20                 : #include "fmgr.h"
      21                 : #include "utils/inval.h"
      22                 : #include "utils/pgstat_internal.h"
      23                 : #include "utils/syscache.h"
      24                 : 
      25                 : 
      26                 : /* ----------
      27                 :  * GUC parameters
      28                 :  * ----------
      29                 :  */
      30                 : int         pgstat_track_functions = TRACK_FUNC_OFF;
      31                 : 
      32                 : 
      33                 : /*
      34                 :  * Total time charged to functions so far in the current backend.
      35                 :  * We use this to help separate "self" and "other" time charges.
      36                 :  * (We assume this initializes to zero.)
      37                 :  */
      38                 : static instr_time total_func_time;
      39                 : 
      40                 : 
      41                 : /*
      42                 :  * Ensure that stats are dropped if transaction aborts.
      43                 :  */
      44                 : void
      45 CBC       11168 : pgstat_create_function(Oid proid)
      46                 : {
      47           11168 :     pgstat_create_transactional(PGSTAT_KIND_FUNCTION,
      48                 :                                 MyDatabaseId,
      49                 :                                 proid);
      50           11168 : }
      51                 : 
      52                 : /*
      53                 :  * Ensure that stats are dropped if transaction commits.
      54                 :  *
      55                 :  * NB: This is only reliable because pgstat_init_function_usage() does some
      56                 :  * extra work. If other places start emitting function stats they likely need
      57                 :  * similar logic.
      58                 :  */
      59                 : void
      60            3066 : pgstat_drop_function(Oid proid)
      61                 : {
      62            3066 :     pgstat_drop_transactional(PGSTAT_KIND_FUNCTION,
      63                 :                               MyDatabaseId,
      64                 :                               proid);
      65            3066 : }
      66                 : 
      67                 : /*
      68                 :  * Initialize function call usage data.
      69                 :  * Called by the executor before invoking a function.
      70                 :  */
      71                 : void
      72         8360619 : pgstat_init_function_usage(FunctionCallInfo fcinfo,
      73                 :                            PgStat_FunctionCallUsage *fcu)
      74                 : {
      75                 :     PgStat_EntryRef *entry_ref;
      76                 :     PgStat_FunctionCounts *pending;
      77                 :     bool        created_entry;
      78                 : 
      79         8360619 :     if (pgstat_track_functions <= fcinfo->flinfo->fn_stats)
      80                 :     {
      81                 :         /* stats not wanted */
      82         8360515 :         fcu->fs = NULL;
      83         8360515 :         return;
      84                 :     }
      85                 : 
      86             104 :     entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_FUNCTION,
      87                 :                                           MyDatabaseId,
      88             104 :                                           fcinfo->flinfo->fn_oid,
      89                 :                                           &created_entry);
      90                 : 
      91                 :     /*
      92                 :      * If no shared entry already exists, check if the function has been
      93                 :      * deleted concurrently. This can go unnoticed until here because
      94                 :      * executing a statement that just calls a function, does not trigger
      95                 :      * cache invalidation processing. The reason we care about this case is
      96                 :      * that otherwise we could create a new stats entry for an already dropped
      97                 :      * function (for relations etc this is not possible because emitting stats
      98                 :      * requires a lock for the relation to already have been acquired).
      99                 :      *
     100                 :      * It's somewhat ugly to have a behavioral difference based on
     101                 :      * track_functions being enabled/disabled. But it seems acceptable, given
     102                 :      * that there's already behavioral differences depending on whether the
     103                 :      * function is the caches etc.
     104                 :      *
     105                 :      * For correctness it'd be sufficient to set ->dropped to true. However,
     106                 :      * the accepted invalidation will commonly cause "low level" failures in
     107                 :      * PL code, with an OID in the error message. Making this harder to
     108                 :      * test...
     109                 :      */
     110             104 :     if (created_entry)
     111                 :     {
     112              45 :         AcceptInvalidationMessages();
     113              45 :         if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(fcinfo->flinfo->fn_oid)))
     114                 :         {
     115 UBC           0 :             pgstat_drop_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId,
     116               0 :                               fcinfo->flinfo->fn_oid);
     117               0 :             ereport(ERROR, errcode(ERRCODE_UNDEFINED_FUNCTION),
     118                 :                     errmsg("function call to dropped function"));
     119                 :         }
     120                 :     }
     121                 : 
     122 CBC         104 :     pending = entry_ref->pending;
     123                 : 
     124 GNC         104 :     fcu->fs = pending;
     125                 : 
     126                 :     /* save stats for this function, later used to compensate for recursion */
     127             104 :     fcu->save_f_total_time = pending->total_time;
     128                 : 
     129                 :     /* save current backend-wide total time */
     130 CBC         104 :     fcu->save_total = total_func_time;
     131                 : 
     132                 :     /* get clock time as of function start */
     133 GNC         104 :     INSTR_TIME_SET_CURRENT(fcu->start);
     134                 : }
     135                 : 
     136                 : /*
     137                 :  * Calculate function call usage and update stat counters.
     138                 :  * Called by the executor after invoking a function.
     139                 :  *
     140                 :  * In the case of a set-returning function that runs in value-per-call mode,
     141                 :  * we will see multiple pgstat_init_function_usage/pgstat_end_function_usage
     142                 :  * calls for what the user considers a single call of the function.  The
     143                 :  * finalize flag should be TRUE on the last call.
     144                 :  */
     145                 : void
     146 CBC     8357211 : pgstat_end_function_usage(PgStat_FunctionCallUsage *fcu, bool finalize)
     147                 : {
     148         8357211 :     PgStat_FunctionCounts *fs = fcu->fs;
     149                 :     instr_time  total;
     150                 :     instr_time  others;
     151                 :     instr_time  self;
     152                 : 
     153                 :     /* stats not wanted? */
     154         8357211 :     if (fs == NULL)
     155         8357107 :         return;
     156                 : 
     157                 :     /* total elapsed time in this function call */
     158 GNC         104 :     INSTR_TIME_SET_CURRENT(total);
     159             104 :     INSTR_TIME_SUBTRACT(total, fcu->start);
     160                 : 
     161                 :     /* self usage: elapsed minus anything already charged to other calls */
     162             104 :     others = total_func_time;
     163             104 :     INSTR_TIME_SUBTRACT(others, fcu->save_total);
     164             104 :     self = total;
     165             104 :     INSTR_TIME_SUBTRACT(self, others);
     166                 : 
     167                 :     /* update backend-wide total time */
     168             104 :     INSTR_TIME_ADD(total_func_time, self);
     169                 : 
     170                 :     /*
     171                 :      * Compute the new total_time as the total elapsed time added to the
     172                 :      * pre-call value of total_time.  This is necessary to avoid
     173                 :      * double-counting any time taken by recursive calls of myself.  (We do
     174                 :      * not need any similar kluge for self time, since that already excludes
     175                 :      * any recursive calls.)
     176                 :      */
     177             104 :     INSTR_TIME_ADD(total, fcu->save_f_total_time);
     178                 : 
     179                 :     /* update counters in function stats table */
     180 CBC         104 :     if (finalize)
     181 GNC         104 :         fs->numcalls++;
     182             104 :     fs->total_time = total;
     183             104 :     INSTR_TIME_ADD(fs->self_time, self);
     184                 : }
     185                 : 
     186                 : /*
     187                 :  * Flush out pending stats for the entry
     188                 :  *
     189                 :  * If nowait is true, this function returns false if lock could not
     190                 :  * immediately acquired, otherwise true is returned.
     191                 :  */
     192                 : bool
     193 CBC          66 : pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
     194                 : {
     195                 :     PgStat_FunctionCounts *localent;
     196                 :     PgStatShared_Function *shfuncent;
     197                 : 
     198 GNC          66 :     localent = (PgStat_FunctionCounts *) entry_ref->pending;
     199 CBC          66 :     shfuncent = (PgStatShared_Function *) entry_ref->shared_stats;
     200                 : 
     201                 :     /* localent always has non-zero content */
     202                 : 
     203              66 :     if (!pgstat_lock_entry(entry_ref, nowait))
     204 UBC           0 :         return false;
     205                 : 
     206 GNC          66 :     shfuncent->stats.numcalls += localent->numcalls;
     207              66 :     shfuncent->stats.total_time +=
     208              66 :         INSTR_TIME_GET_MICROSEC(localent->total_time);
     209              66 :     shfuncent->stats.self_time +=
     210              66 :         INSTR_TIME_GET_MICROSEC(localent->self_time);
     211                 : 
     212 CBC          66 :     pgstat_unlock_entry(entry_ref);
     213                 : 
     214              66 :     return true;
     215                 : }
     216                 : 
     217                 : /*
     218                 :  * find any existing PgStat_FunctionCounts entry for specified function
     219                 :  *
     220                 :  * If no entry, return NULL, don't create a new one
     221                 :  */
     222                 : PgStat_FunctionCounts *
     223              12 : find_funcstat_entry(Oid func_id)
     224                 : {
     225                 :     PgStat_EntryRef *entry_ref;
     226                 : 
     227              12 :     entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id);
     228                 : 
     229              12 :     if (entry_ref)
     230               9 :         return entry_ref->pending;
     231               3 :     return NULL;
     232                 : }
     233                 : 
     234                 : /*
     235                 :  * Support function for the SQL-callable pgstat* functions. Returns
     236                 :  * the collected statistics for one function or NULL.
     237                 :  */
     238                 : PgStat_StatFuncEntry *
     239             284 : pgstat_fetch_stat_funcentry(Oid func_id)
     240                 : {
     241             284 :     return (PgStat_StatFuncEntry *)
     242             284 :         pgstat_fetch_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id);
     243                 : }
        

Generated by: LCOV version v1.16-55-g56c0a2a