LCOV - code coverage report
Current view: top level - src/backend/utils/activity - pgstat_function.c (source / functions) Hit Total Coverage
Test: PostgreSQL 17devel Lines: 55 59 93.2 %
Date: 2024-04-25 21:11:15 Functions: 7 7 100.0 %
Legend: Lines: hit not hit

          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-2024, 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       14422 : pgstat_create_function(Oid proid)
      46             : {
      47       14422 :     pgstat_create_transactional(PGSTAT_KIND_FUNCTION,
      48             :                                 MyDatabaseId,
      49             :                                 proid);
      50       14422 : }
      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        6434 : pgstat_drop_function(Oid proid)
      61             : {
      62        6434 :     pgstat_drop_transactional(PGSTAT_KIND_FUNCTION,
      63             :                               MyDatabaseId,
      64             :                               proid);
      65        6434 : }
      66             : 
      67             : /*
      68             :  * Initialize function call usage data.
      69             :  * Called by the executor before invoking a function.
      70             :  */
      71             : void
      72    20406136 : 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    20406136 :     if (pgstat_track_functions <= fcinfo->flinfo->fn_stats)
      80             :     {
      81             :         /* stats not wanted */
      82    20405922 :         fcu->fs = NULL;
      83    20405922 :         return;
      84             :     }
      85             : 
      86         214 :     entry_ref = pgstat_prep_pending_entry(PGSTAT_KIND_FUNCTION,
      87             :                                           MyDatabaseId,
      88         214 :                                           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         214 :     if (created_entry)
     111             :     {
     112          96 :         AcceptInvalidationMessages();
     113          96 :         if (!SearchSysCacheExists1(PROCOID, ObjectIdGetDatum(fcinfo->flinfo->fn_oid)))
     114             :         {
     115           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         214 :     pending = entry_ref->pending;
     123             : 
     124         214 :     fcu->fs = pending;
     125             : 
     126             :     /* save stats for this function, later used to compensate for recursion */
     127         214 :     fcu->save_f_total_time = pending->total_time;
     128             : 
     129             :     /* save current backend-wide total time */
     130         214 :     fcu->save_total = total_func_time;
     131             : 
     132             :     /* get clock time as of function start */
     133         214 :     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    20398150 : pgstat_end_function_usage(PgStat_FunctionCallUsage *fcu, bool finalize)
     147             : {
     148    20398150 :     PgStat_FunctionCounts *fs = fcu->fs;
     149             :     instr_time  total;
     150             :     instr_time  others;
     151             :     instr_time  self;
     152             : 
     153             :     /* stats not wanted? */
     154    20398150 :     if (fs == NULL)
     155    20397936 :         return;
     156             : 
     157             :     /* total elapsed time in this function call */
     158         214 :     INSTR_TIME_SET_CURRENT(total);
     159         214 :     INSTR_TIME_SUBTRACT(total, fcu->start);
     160             : 
     161             :     /* self usage: elapsed minus anything already charged to other calls */
     162         214 :     others = total_func_time;
     163         214 :     INSTR_TIME_SUBTRACT(others, fcu->save_total);
     164         214 :     self = total;
     165         214 :     INSTR_TIME_SUBTRACT(self, others);
     166             : 
     167             :     /* update backend-wide total time */
     168         214 :     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         214 :     INSTR_TIME_ADD(total, fcu->save_f_total_time);
     178             : 
     179             :     /* update counters in function stats table */
     180         214 :     if (finalize)
     181         214 :         fs->numcalls++;
     182         214 :     fs->total_time = total;
     183         214 :     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         138 : pgstat_function_flush_cb(PgStat_EntryRef *entry_ref, bool nowait)
     194             : {
     195             :     PgStat_FunctionCounts *localent;
     196             :     PgStatShared_Function *shfuncent;
     197             : 
     198         138 :     localent = (PgStat_FunctionCounts *) entry_ref->pending;
     199         138 :     shfuncent = (PgStatShared_Function *) entry_ref->shared_stats;
     200             : 
     201             :     /* localent always has non-zero content */
     202             : 
     203         138 :     if (!pgstat_lock_entry(entry_ref, nowait))
     204           0 :         return false;
     205             : 
     206         138 :     shfuncent->stats.numcalls += localent->numcalls;
     207         138 :     shfuncent->stats.total_time +=
     208         138 :         INSTR_TIME_GET_MICROSEC(localent->total_time);
     209         138 :     shfuncent->stats.self_time +=
     210         138 :         INSTR_TIME_GET_MICROSEC(localent->self_time);
     211             : 
     212         138 :     pgstat_unlock_entry(entry_ref);
     213             : 
     214         138 :     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          24 : find_funcstat_entry(Oid func_id)
     224             : {
     225             :     PgStat_EntryRef *entry_ref;
     226             : 
     227          24 :     entry_ref = pgstat_fetch_pending_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id);
     228             : 
     229          24 :     if (entry_ref)
     230          18 :         return entry_ref->pending;
     231           6 :     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         586 : pgstat_fetch_stat_funcentry(Oid func_id)
     240             : {
     241         586 :     return (PgStat_StatFuncEntry *)
     242         586 :         pgstat_fetch_entry(PGSTAT_KIND_FUNCTION, MyDatabaseId, func_id);
     243             : }

Generated by: LCOV version 1.14