]> git.ipfire.org Git - thirdparty/postgresql.git/commitdiff
instrumentation: Separate per-node logic from other uses
authorAndres Freund <andres@anarazel.de>
Sun, 5 Apr 2026 21:18:00 +0000 (17:18 -0400)
committerAndres Freund <andres@anarazel.de>
Sun, 5 Apr 2026 23:04:24 +0000 (19:04 -0400)
Previously, different places (e.g. query "total time") were repurposing the
Instrumentation struct initially introduced for capturing per-node statistics
during execution. This overuse of the same struct is confusing, e.g. by
cluttering calls of InstrStartNode/InstrStopNode in unrelated code paths, and
prevents future refactorings.

Instead, simplify the Instrumentation struct to only track time and WAL/buffer
usage. Similarly, drop the use of InstrEndLoop outside of per-node
instrumentation - these calls were added without any apparent benefit since
the relevant fields were never read.

Introduce the NodeInstrumentation struct to carry forward the per-node
instrumentation information. WorkerInstrumentation is renamed to
WorkerNodeInstrumentation for clarity.

In passing, clarify that InstrAggNode is expected to only run after
InstrEndLoop (as it does in practice), and drop unused code.

This also fixes a consequence-less bug: Previously ->async_mode was only set
when a non-zero instrument_option was passed. That turns out to be harmless
right now, as ->async_mode only affects a timing related field.

Author: Lukas Fittl <lukas@fittl.com>
Reviewed-by: Andres Freund <andres@anarazel.de>
Discussion: https://postgr.es/m/CAP53PkzdBK8VJ1fS4AZ481LgMN8f9mJiC39ZRHqkFUSYq6KWmg@mail.gmail.com

contrib/auto_explain/auto_explain.c
contrib/pg_stat_statements/pg_stat_statements.c
contrib/postgres_fdw/postgres_fdw.c
src/backend/commands/explain.c
src/backend/executor/execMain.c
src/backend/executor/execParallel.c
src/backend/executor/execProcnode.c
src/backend/executor/instrument.c
src/include/executor/instrument.h
src/include/nodes/execnodes.h
src/tools/pgindent/typedefs.list

index 5f5c1ff0da3e980ce4f99ac672e417166bdbc2ae..39bf2543b701dc7efd094f63ffbb9d3a9f79d36a 100644 (file)
@@ -315,7 +315,7 @@ explain_ExecutorStart(QueryDesc *queryDesc, int eflags)
                        MemoryContext oldcxt;
 
                        oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
-                       queryDesc->totaltime = InstrAlloc(INSTRUMENT_ALL, false);
+                       queryDesc->totaltime = InstrAlloc(INSTRUMENT_ALL);
                        MemoryContextSwitchTo(oldcxt);
                }
        }
@@ -381,12 +381,6 @@ explain_ExecutorEnd(QueryDesc *queryDesc)
                 */
                oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
 
-               /*
-                * Make sure stats accumulation is done.  (Note: it's okay if several
-                * levels of hook all do this.)
-                */
-               InstrEndLoop(queryDesc->totaltime);
-
                /* Log plan if duration is exceeded. */
                msec = INSTR_TIME_GET_MILLISEC(queryDesc->totaltime->total);
                if (msec >= auto_explain_log_min_duration)
index ddbd5727ddf5e6ec10ea7ac77d09de9a08565c4a..fbf32f0e72c291051e6fe3f7ef95c57833f95f4a 100644 (file)
@@ -1025,7 +1025,7 @@ pgss_ExecutorStart(QueryDesc *queryDesc, int eflags)
                        MemoryContext oldcxt;
 
                        oldcxt = MemoryContextSwitchTo(queryDesc->estate->es_query_cxt);
-                       queryDesc->totaltime = InstrAlloc(INSTRUMENT_ALL, false);
+                       queryDesc->totaltime = InstrAlloc(INSTRUMENT_ALL);
                        MemoryContextSwitchTo(oldcxt);
                }
        }
@@ -1084,12 +1084,6 @@ pgss_ExecutorEnd(QueryDesc *queryDesc)
        if (queryId != INT64CONST(0) && queryDesc->totaltime &&
                pgss_enabled(nesting_level))
        {
-               /*
-                * Make sure stats accumulation is done.  (Note: it's okay if several
-                * levels of hook all do this.)
-                */
-               InstrEndLoop(queryDesc->totaltime);
-
                pgss_store(queryDesc->sourceText,
                                   queryId,
                                   queryDesc->plannedstmt->stmt_location,
index 41e47cc795ba89cd22aa15538ca8bfa1e904a729..cc8ec24c30eb02cb6318d07ce33f6da920725a75 100644 (file)
@@ -2779,7 +2779,7 @@ postgresIterateDirectModify(ForeignScanState *node)
        if (!resultRelInfo->ri_projectReturning)
        {
                TupleTableSlot *slot = node->ss.ss_ScanTupleSlot;
-               Instrumentation *instr = node->ss.ps.instrument;
+               NodeInstrumentation *instr = node->ss.ps.instrument;
 
                Assert(!dmstate->has_returning);
 
index 5b76cbc26fad277a8ec471d082e08e33305329dc..73eaaf176acc4c5418ee06db5e83bb08990c852c 100644 (file)
@@ -1105,9 +1105,6 @@ report_triggers(ResultRelInfo *rInfo, bool show_relname, ExplainState *es)
                char       *relname;
                char       *conname = NULL;
 
-               /* Ensure total timing is updated from the internal counter */
-               InstrEndLoop(&tginstr->instr);
-
                /*
                 * We ignore triggers that were never invoked; they likely aren't
                 * relevant to the current query type.
@@ -1839,10 +1836,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
        if (es->analyze &&
                planstate->instrument && planstate->instrument->nloops > 0)
        {
-               double          nloops = planstate->instrument->nloops;
-               double          startup_ms = INSTR_TIME_GET_MILLISEC(planstate->instrument->startup) / nloops;
-               double          total_ms = INSTR_TIME_GET_MILLISEC(planstate->instrument->total) / nloops;
-               double          rows = planstate->instrument->ntuples / nloops;
+               NodeInstrumentation *instr = planstate->instrument;
+               double          nloops = instr->nloops;
+               double          startup_ms = INSTR_TIME_GET_MILLISEC(instr->startup) / nloops;
+               double          total_ms = INSTR_TIME_GET_MILLISEC(instr->instr.total) / nloops;
+               double          rows = instr->ntuples / nloops;
 
                if (es->format == EXPLAIN_FORMAT_TEXT)
                {
@@ -1894,11 +1892,11 @@ ExplainNode(PlanState *planstate, List *ancestors,
        /* prepare per-worker general execution details */
        if (es->workers_state && es->verbose)
        {
-               WorkerInstrumentation *w = planstate->worker_instrument;
+               WorkerNodeInstrumentation *w = planstate->worker_instrument;
 
                for (int n = 0; n < w->num_workers; n++)
                {
-                       Instrumentation *instrument = &w->instrument[n];
+                       NodeInstrumentation *instrument = &w->instrument[n];
                        double          nloops = instrument->nloops;
                        double          startup_ms;
                        double          total_ms;
@@ -1907,7 +1905,7 @@ ExplainNode(PlanState *planstate, List *ancestors,
                        if (nloops <= 0)
                                continue;
                        startup_ms = INSTR_TIME_GET_MILLISEC(instrument->startup) / nloops;
-                       total_ms = INSTR_TIME_GET_MILLISEC(instrument->total) / nloops;
+                       total_ms = INSTR_TIME_GET_MILLISEC(instrument->instr.total) / nloops;
                        rows = instrument->ntuples / nloops;
 
                        ExplainOpenWorker(n, es);
@@ -2294,18 +2292,18 @@ ExplainNode(PlanState *planstate, List *ancestors,
 
        /* Show buffer/WAL usage */
        if (es->buffers && planstate->instrument)
-               show_buffer_usage(es, &planstate->instrument->bufusage);
+               show_buffer_usage(es, &planstate->instrument->instr.bufusage);
        if (es->wal && planstate->instrument)
-               show_wal_usage(es, &planstate->instrument->walusage);
+               show_wal_usage(es, &planstate->instrument->instr.walusage);
 
        /* Prepare per-worker buffer/WAL usage */
        if (es->workers_state && (es->buffers || es->wal) && es->verbose)
        {
-               WorkerInstrumentation *w = planstate->worker_instrument;
+               WorkerNodeInstrumentation *w = planstate->worker_instrument;
 
                for (int n = 0; n < w->num_workers; n++)
                {
-                       Instrumentation *instrument = &w->instrument[n];
+                       NodeInstrumentation *instrument = &w->instrument[n];
                        double          nloops = instrument->nloops;
 
                        if (nloops <= 0)
@@ -2313,9 +2311,9 @@ ExplainNode(PlanState *planstate, List *ancestors,
 
                        ExplainOpenWorker(n, es);
                        if (es->buffers)
-                               show_buffer_usage(es, &instrument->bufusage);
+                               show_buffer_usage(es, &instrument->instr.bufusage);
                        if (es->wal)
-                               show_wal_usage(es, &instrument->walusage);
+                               show_wal_usage(es, &instrument->instr.walusage);
                        ExplainCloseWorker(n, es);
                }
        }
index 0237d8c3b1d8a76185ffa08146c13a53e8895d64..b0f636bf8b6c2c8713400f2684b473a3be94ac85 100644 (file)
@@ -333,7 +333,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
 
        /* Allow instrumentation of Executor overall runtime */
        if (queryDesc->totaltime)
-               InstrStartNode(queryDesc->totaltime);
+               InstrStart(queryDesc->totaltime);
 
        /*
         * extract information from the query descriptor and the query feature.
@@ -385,7 +385,7 @@ standard_ExecutorRun(QueryDesc *queryDesc,
                dest->rShutdown(dest);
 
        if (queryDesc->totaltime)
-               InstrStopNode(queryDesc->totaltime, estate->es_processed);
+               InstrStop(queryDesc->totaltime);
 
        MemoryContextSwitchTo(oldcontext);
 }
@@ -435,7 +435,7 @@ standard_ExecutorFinish(QueryDesc *queryDesc)
 
        /* Allow instrumentation of Executor overall runtime */
        if (queryDesc->totaltime)
-               InstrStartNode(queryDesc->totaltime);
+               InstrStart(queryDesc->totaltime);
 
        /* Run ModifyTable nodes to completion */
        ExecPostprocessPlan(estate);
@@ -445,7 +445,7 @@ standard_ExecutorFinish(QueryDesc *queryDesc)
                AfterTriggerEndQuery(estate);
 
        if (queryDesc->totaltime)
-               InstrStopNode(queryDesc->totaltime, 0);
+               InstrStop(queryDesc->totaltime);
 
        MemoryContextSwitchTo(oldcontext);
 
index 755191b51ef66541c42d1dde64f48a920d1dc10a..5e4a4a9740c5740b3f02634a09a50f5af3a246f1 100644 (file)
@@ -87,7 +87,7 @@ typedef struct FixedParallelExecutorState
  * instrument_options: Same meaning here as in instrument.c.
  *
  * instrument_offset: Offset, relative to the start of this structure,
- * of the first Instrumentation object.  This will depend on the length of
+ * of the first NodeInstrumentation object.  This will depend on the length of
  * the plan_node_id array.
  *
  * num_workers: Number of workers.
@@ -104,11 +104,15 @@ struct SharedExecutorInstrumentation
        int                     num_workers;
        int                     num_plan_nodes;
        int                     plan_node_id[FLEXIBLE_ARRAY_MEMBER];
-       /* array of num_plan_nodes * num_workers Instrumentation objects follows */
+
+       /*
+        * Array of num_plan_nodes * num_workers NodeInstrumentation objects
+        * follows.
+        */
 };
 #define GetInstrumentationArray(sei) \
        (StaticAssertVariableIsOfTypeMacro(sei, SharedExecutorInstrumentation *), \
-        (Instrumentation *) (((char *) sei) + sei->instrument_offset))
+        (NodeInstrumentation *) (((char *) sei) + sei->instrument_offset))
 
 /* Context object for ExecParallelEstimate. */
 typedef struct ExecParallelEstimateContext
@@ -731,7 +735,7 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
                instrumentation_len = MAXALIGN(instrumentation_len);
                instrument_offset = instrumentation_len;
                instrumentation_len +=
-                       mul_size(sizeof(Instrumentation),
+                       mul_size(sizeof(NodeInstrumentation),
                                         mul_size(e.nnodes, nworkers));
                shm_toc_estimate_chunk(&pcxt->estimator, instrumentation_len);
                shm_toc_estimate_keys(&pcxt->estimator, 1);
@@ -817,7 +821,7 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
         */
        if (estate->es_instrument)
        {
-               Instrumentation *instrument;
+               NodeInstrumentation *instrument;
                int                     i;
 
                instrumentation = shm_toc_allocate(pcxt->toc, instrumentation_len);
@@ -827,7 +831,7 @@ ExecInitParallelPlan(PlanState *planstate, EState *estate,
                instrumentation->num_plan_nodes = e.nnodes;
                instrument = GetInstrumentationArray(instrumentation);
                for (i = 0; i < nworkers * e.nnodes; ++i)
-                       InstrInit(&instrument[i], estate->es_instrument);
+                       InstrInitNode(&instrument[i], estate->es_instrument, false);
                shm_toc_insert(pcxt->toc, PARALLEL_KEY_INSTRUMENTATION,
                                           instrumentation);
                pei->instrumentation = instrumentation;
@@ -1059,7 +1063,7 @@ static bool
 ExecParallelRetrieveInstrumentation(PlanState *planstate,
                                                                        SharedExecutorInstrumentation *instrumentation)
 {
-       Instrumentation *instrument;
+       NodeInstrumentation *instrument;
        int                     i;
        int                     n;
        int                     ibytes;
@@ -1087,9 +1091,9 @@ ExecParallelRetrieveInstrumentation(PlanState *planstate,
         * Switch into per-query memory context.
         */
        oldcontext = MemoryContextSwitchTo(planstate->state->es_query_cxt);
-       ibytes = mul_size(instrumentation->num_workers, sizeof(Instrumentation));
+       ibytes = mul_size(instrumentation->num_workers, sizeof(NodeInstrumentation));
        planstate->worker_instrument =
-               palloc(ibytes + offsetof(WorkerInstrumentation, instrument));
+               palloc(ibytes + offsetof(WorkerNodeInstrumentation, instrument));
        MemoryContextSwitchTo(oldcontext);
 
        planstate->worker_instrument->num_workers = instrumentation->num_workers;
@@ -1319,7 +1323,7 @@ ExecParallelReportInstrumentation(PlanState *planstate,
 {
        int                     i;
        int                     plan_node_id = planstate->plan->plan_node_id;
-       Instrumentation *instrument;
+       NodeInstrumentation *instrument;
 
        InstrEndLoop(planstate->instrument);
 
index a2047e4dbc6655b1a3cee9eddaed701549af5e6b..132fe37ef60f8d4ba2b7247ffe12bc1f28641ec7 100644 (file)
@@ -414,8 +414,8 @@ ExecInitNode(Plan *node, EState *estate, int eflags)
 
        /* Set up instrumentation for this node if requested */
        if (estate->es_instrument)
-               result->instrument = InstrAlloc(estate->es_instrument,
-                                                                               result->async_capable);
+               result->instrument = InstrAllocNode(estate->es_instrument,
+                                                                                       result->async_capable);
 
        return result;
 }
index df686ffe048207b1e7ee2479dc0b7654628404bb..011a9684df0d5ddf66fb297044e0c344d1ae4a17 100644 (file)
@@ -26,48 +26,36 @@ static void BufferUsageAdd(BufferUsage *dst, const BufferUsage *add);
 static void WalUsageAdd(WalUsage *dst, WalUsage *add);
 
 
-/* Allocate new instrumentation structure */
+/* General purpose instrumentation handling */
 Instrumentation *
-InstrAlloc(int instrument_options, bool async_mode)
+InstrAlloc(int instrument_options)
 {
-       Instrumentation *instr;
-
-       /* initialize all fields to zeroes, then modify as needed */
-       instr = palloc0_object(Instrumentation);
-       if (instrument_options & (INSTRUMENT_BUFFERS | INSTRUMENT_TIMER | INSTRUMENT_WAL))
-       {
-               instr->need_bufusage = (instrument_options & INSTRUMENT_BUFFERS) != 0;
-               instr->need_walusage = (instrument_options & INSTRUMENT_WAL) != 0;
-               instr->need_timer = (instrument_options & INSTRUMENT_TIMER) != 0;
-               instr->async_mode = async_mode;
-       }
+       Instrumentation *instr = palloc0_object(Instrumentation);
 
+       InstrInitOptions(instr, instrument_options);
        return instr;
 }
 
-/* Initialize a pre-allocated instrumentation structure. */
 void
-InstrInit(Instrumentation *instr, int instrument_options)
+InstrInitOptions(Instrumentation *instr, int instrument_options)
 {
-       memset(instr, 0, sizeof(Instrumentation));
        instr->need_bufusage = (instrument_options & INSTRUMENT_BUFFERS) != 0;
        instr->need_walusage = (instrument_options & INSTRUMENT_WAL) != 0;
        instr->need_timer = (instrument_options & INSTRUMENT_TIMER) != 0;
 }
 
-/* Entry to a plan node */
 void
-InstrStartNode(Instrumentation *instr)
+InstrStart(Instrumentation *instr)
 {
        if (instr->need_timer)
        {
                if (!INSTR_TIME_IS_ZERO(instr->starttime))
-                       elog(ERROR, "InstrStartNode called twice in a row");
+                       elog(ERROR, "InstrStart called twice in a row");
                else
                        INSTR_TIME_SET_CURRENT(instr->starttime);
        }
 
-       /* save buffer usage totals at node entry, if needed */
+       /* save buffer usage totals at start, if needed */
        if (instr->need_bufusage)
                instr->bufusage_start = pgBufferUsage;
 
@@ -75,29 +63,28 @@ InstrStartNode(Instrumentation *instr)
                instr->walusage_start = pgWalUsage;
 }
 
-/* Exit from a plan node */
-void
-InstrStopNode(Instrumentation *instr, double nTuples)
+/*
+ * Helper for InstrStop() and InstrStopNode(), to avoid code duplication
+ * despite slightly different needs about how time is accumulated.
+ */
+static inline void
+InstrStopCommon(Instrumentation *instr, instr_time *accum_time)
 {
-       double          save_tuplecount = instr->tuplecount;
        instr_time      endtime;
 
-       /* count the returned tuples */
-       instr->tuplecount += nTuples;
-
-       /* let's update the time only if the timer was requested */
+       /* update the time only if the timer was requested */
        if (instr->need_timer)
        {
                if (INSTR_TIME_IS_ZERO(instr->starttime))
-                       elog(ERROR, "InstrStopNode called without start");
+                       elog(ERROR, "InstrStop called without start");
 
                INSTR_TIME_SET_CURRENT(endtime);
-               INSTR_TIME_ACCUM_DIFF(instr->counter, endtime, instr->starttime);
+               INSTR_TIME_ACCUM_DIFF(*accum_time, endtime, instr->starttime);
 
                INSTR_TIME_SET_ZERO(instr->starttime);
        }
 
-       /* Add delta of buffer usage since entry to node's totals */
+       /* Add delta of buffer usage since InstrStart to the totals */
        if (instr->need_bufusage)
                BufferUsageAccumDiff(&instr->bufusage,
                                                         &pgBufferUsage, &instr->bufusage_start);
@@ -105,6 +92,60 @@ InstrStopNode(Instrumentation *instr, double nTuples)
        if (instr->need_walusage)
                WalUsageAccumDiff(&instr->walusage,
                                                  &pgWalUsage, &instr->walusage_start);
+}
+
+void
+InstrStop(Instrumentation *instr)
+{
+       InstrStopCommon(instr, &instr->total);
+}
+
+/* Node instrumentation handling */
+
+/* Allocate new node instrumentation structure */
+NodeInstrumentation *
+InstrAllocNode(int instrument_options, bool async_mode)
+{
+       NodeInstrumentation *instr = palloc_object(NodeInstrumentation);
+
+       InstrInitNode(instr, instrument_options, async_mode);
+
+       return instr;
+}
+
+/* Initialize a pre-allocated instrumentation structure. */
+void
+InstrInitNode(NodeInstrumentation *instr, int instrument_options, bool async_mode)
+{
+       memset(instr, 0, sizeof(NodeInstrumentation));
+       InstrInitOptions(&instr->instr, instrument_options);
+       instr->async_mode = async_mode;
+}
+
+/* Entry to a plan node */
+void
+InstrStartNode(NodeInstrumentation *instr)
+{
+       InstrStart(&instr->instr);
+}
+
+/* Exit from a plan node */
+void
+InstrStopNode(NodeInstrumentation *instr, double nTuples)
+{
+       double          save_tuplecount = instr->tuplecount;
+
+       /* count the returned tuples */
+       instr->tuplecount += nTuples;
+
+       /*
+        * Note that in contrast to InstrStop() the time is accumulated into
+        * NodeInstrumentation->counter, with total only getting updated in
+        * InstrEndLoop.  We need the separate counter variable because we need to
+        * calculate start-up time for the first tuple in each cycle, and then
+        * accumulate it together.
+        */
+       InstrStopCommon(&instr->instr, &instr->counter);
 
        /* Is this the first tuple of this cycle? */
        if (!instr->running)
@@ -125,7 +166,7 @@ InstrStopNode(Instrumentation *instr, double nTuples)
 
 /* Update tuple count */
 void
-InstrUpdateTupleCount(Instrumentation *instr, double nTuples)
+InstrUpdateTupleCount(NodeInstrumentation *instr, double nTuples)
 {
        /* count the returned tuples */
        instr->tuplecount += nTuples;
@@ -133,59 +174,51 @@ InstrUpdateTupleCount(Instrumentation *instr, double nTuples)
 
 /* Finish a run cycle for a plan node */
 void
-InstrEndLoop(Instrumentation *instr)
+InstrEndLoop(NodeInstrumentation *instr)
 {
        /* Skip if nothing has happened, or already shut down */
        if (!instr->running)
                return;
 
-       if (!INSTR_TIME_IS_ZERO(instr->starttime))
+       if (!INSTR_TIME_IS_ZERO(instr->instr.starttime))
                elog(ERROR, "InstrEndLoop called on running node");
 
        /* Accumulate per-cycle statistics into totals */
        INSTR_TIME_ADD(instr->startup, instr->firsttuple);
-       INSTR_TIME_ADD(instr->total, instr->counter);
+       INSTR_TIME_ADD(instr->instr.total, instr->counter);
        instr->ntuples += instr->tuplecount;
        instr->nloops += 1;
 
        /* Reset for next cycle (if any) */
        instr->running = false;
-       INSTR_TIME_SET_ZERO(instr->starttime);
+       INSTR_TIME_SET_ZERO(instr->instr.starttime);
        INSTR_TIME_SET_ZERO(instr->counter);
        INSTR_TIME_SET_ZERO(instr->firsttuple);
        instr->tuplecount = 0;
 }
 
-/* aggregate instrumentation information */
+/*
+ * Aggregate instrumentation from parallel workers. Must be called after
+ * InstrEndLoop.
+ */
 void
-InstrAggNode(Instrumentation *dst, Instrumentation *add)
+InstrAggNode(NodeInstrumentation *dst, NodeInstrumentation *add)
 {
-       if (!dst->running && add->running)
-       {
-               dst->running = true;
-               dst->firsttuple = add->firsttuple;
-       }
-       else if (dst->running && add->running &&
-                        INSTR_TIME_GT(dst->firsttuple, add->firsttuple))
-               dst->firsttuple = add->firsttuple;
-
-       INSTR_TIME_ADD(dst->counter, add->counter);
+       Assert(!add->running);
 
-       dst->tuplecount += add->tuplecount;
        INSTR_TIME_ADD(dst->startup, add->startup);
-       INSTR_TIME_ADD(dst->total, add->total);
+       INSTR_TIME_ADD(dst->instr.total, add->instr.total);
        dst->ntuples += add->ntuples;
        dst->ntuples2 += add->ntuples2;
        dst->nloops += add->nloops;
        dst->nfiltered1 += add->nfiltered1;
        dst->nfiltered2 += add->nfiltered2;
 
-       /* Add delta of buffer usage since entry to node's totals */
-       if (dst->need_bufusage)
-               BufferUsageAdd(&dst->bufusage, &add->bufusage);
+       if (dst->instr.need_bufusage)
+               BufferUsageAdd(&dst->instr.bufusage, &add->instr.bufusage);
 
-       if (dst->need_walusage)
-               WalUsageAdd(&dst->walusage, &add->walusage);
+       if (dst->instr.need_walusage)
+               WalUsageAdd(&dst->instr.walusage, &add->instr.walusage);
 }
 
 /* Trigger instrumentation handling */
@@ -196,7 +229,7 @@ InstrAllocTrigger(int n, int instrument_options)
        int                     i;
 
        for (i = 0; i < n; i++)
-               InstrInit(&tginstr[i].instr, instrument_options);
+               InstrInitOptions(&tginstr[i].instr, instrument_options);
 
        return tginstr;
 }
@@ -204,13 +237,13 @@ InstrAllocTrigger(int n, int instrument_options)
 void
 InstrStartTrigger(TriggerInstrumentation *tginstr)
 {
-       InstrStartNode(&tginstr->instr);
+       InstrStart(&tginstr->instr);
 }
 
 void
 InstrStopTrigger(TriggerInstrumentation *tginstr, int64 firings)
 {
-       InstrStopNode(&tginstr->instr, 0);
+       InstrStop(&tginstr->instr);
        tginstr->firings += firings;
 }
 
index be44f629e91f9a6ae2739c7b289a3e2a03845d52..cc9fbb0e2f01abc04cbb12ce0516b4970a418ac3 100644 (file)
@@ -67,38 +67,55 @@ typedef enum InstrumentOption
        INSTRUMENT_ALL = PG_INT32_MAX
 } InstrumentOption;
 
+/*
+ * General purpose instrumentation that can capture time and WAL/buffer usage
+ *
+ * Initialized through InstrAlloc, followed by one or more calls to a pair of
+ * InstrStart/InstrStop (activity is measured in between).
+ */
 typedef struct Instrumentation
 {
-       /* Parameters set at node creation: */
+       /* Parameters set at creation: */
        bool            need_timer;             /* true if we need timer data */
        bool            need_bufusage;  /* true if we need buffer usage data */
        bool            need_walusage;  /* true if we need WAL usage data */
+       /* Internal state keeping: */
+       instr_time      starttime;              /* start time of last InstrStart */
+       BufferUsage bufusage_start; /* buffer usage at start */
+       WalUsage        walusage_start; /* WAL usage at start */
+       /* Accumulated statistics: */
+       instr_time      total;                  /* total runtime */
+       BufferUsage bufusage;           /* total buffer usage */
+       WalUsage        walusage;               /* total WAL usage */
+} Instrumentation;
+
+/*
+ * Specialized instrumentation for per-node execution statistics
+ */
+typedef struct NodeInstrumentation
+{
+       Instrumentation instr;
+       /* Parameters set at node creation: */
        bool            async_mode;             /* true if node is in async mode */
        /* Info about current plan cycle: */
        bool            running;                /* true if we've completed first tuple */
-       instr_time      starttime;              /* start time of current iteration of node */
        instr_time      counter;                /* accumulated runtime for this node */
        instr_time      firsttuple;             /* time for first tuple of this cycle */
        double          tuplecount;             /* # of tuples emitted so far this cycle */
-       BufferUsage bufusage_start; /* buffer usage at start */
-       WalUsage        walusage_start; /* WAL usage at start */
        /* Accumulated statistics across all completed cycles: */
        instr_time      startup;                /* total startup time */
-       instr_time      total;                  /* total time */
        double          ntuples;                /* total tuples produced */
        double          ntuples2;               /* secondary node-specific tuple counter */
        double          nloops;                 /* # of run cycles for this node */
        double          nfiltered1;             /* # of tuples removed by scanqual or joinqual */
        double          nfiltered2;             /* # of tuples removed by "other" quals */
-       BufferUsage bufusage;           /* total buffer usage */
-       WalUsage        walusage;               /* total WAL usage */
-} Instrumentation;
+} NodeInstrumentation;
 
-typedef struct WorkerInstrumentation
+typedef struct WorkerNodeInstrumentation
 {
        int                     num_workers;    /* # of structures that follow */
-       Instrumentation instrument[FLEXIBLE_ARRAY_MEMBER];
-} WorkerInstrumentation;
+       NodeInstrumentation instrument[FLEXIBLE_ARRAY_MEMBER];
+} WorkerNodeInstrumentation;
 
 typedef struct TriggerInstrumentation
 {
@@ -110,13 +127,20 @@ typedef struct TriggerInstrumentation
 extern PGDLLIMPORT BufferUsage pgBufferUsage;
 extern PGDLLIMPORT WalUsage pgWalUsage;
 
-extern Instrumentation *InstrAlloc(int instrument_options, bool async_mode);
-extern void InstrInit(Instrumentation *instr, int instrument_options);
-extern void InstrStartNode(Instrumentation *instr);
-extern void InstrStopNode(Instrumentation *instr, double nTuples);
-extern void InstrUpdateTupleCount(Instrumentation *instr, double nTuples);
-extern void InstrEndLoop(Instrumentation *instr);
-extern void InstrAggNode(Instrumentation *dst, Instrumentation *add);
+extern Instrumentation *InstrAlloc(int instrument_options);
+extern void InstrInitOptions(Instrumentation *instr, int instrument_options);
+extern void InstrStart(Instrumentation *instr);
+extern void InstrStop(Instrumentation *instr);
+
+extern NodeInstrumentation *InstrAllocNode(int instrument_options,
+                                                                                  bool async_mode);
+extern void InstrInitNode(NodeInstrumentation *instr, int instrument_options,
+                                                 bool async_mode);
+extern void InstrStartNode(NodeInstrumentation *instr);
+extern void InstrStopNode(NodeInstrumentation *instr, double nTuples);
+extern void InstrUpdateTupleCount(NodeInstrumentation *instr, double nTuples);
+extern void InstrEndLoop(NodeInstrumentation *instr);
+extern void InstrAggNode(NodeInstrumentation *dst, NodeInstrumentation *add);
 
 extern TriggerInstrumentation *InstrAllocTrigger(int n, int instrument_options);
 extern void InstrStartTrigger(TriggerInstrumentation *tginstr);
index 908898aa7c9e5ee086d67a528886bec2cb2a6410..3ecae7552fc712e0a1d72b386490cd6be03c9c58 100644 (file)
@@ -60,6 +60,7 @@ typedef struct ScanKeyData ScanKeyData;
 typedef struct SnapshotData *Snapshot;
 typedef struct SortSupportData *SortSupport;
 typedef struct TIDBitmap TIDBitmap;
+typedef struct NodeInstrumentation NodeInstrumentation;
 typedef struct TriggerInstrumentation TriggerInstrumentation;
 typedef struct TupleConversionMap TupleConversionMap;
 typedef struct TupleDescData *TupleDesc;
@@ -68,7 +69,7 @@ typedef struct Tuplestorestate Tuplestorestate;
 typedef struct TupleTableSlot TupleTableSlot;
 typedef struct TupleTableSlotOps TupleTableSlotOps;
 typedef struct WalUsage WalUsage;
-typedef struct WorkerInstrumentation WorkerInstrumentation;
+typedef struct WorkerNodeInstrumentation WorkerNodeInstrumentation;
 
 
 /* ----------------
@@ -1207,8 +1208,10 @@ typedef struct PlanState
        ExecProcNodeMtd ExecProcNodeReal;       /* actual function, if above is a
                                                                                 * wrapper */
 
-       Instrumentation *instrument;    /* Optional runtime stats for this node */
-       WorkerInstrumentation *worker_instrument;       /* per-worker instrumentation */
+       NodeInstrumentation *instrument;        /* Optional runtime stats for this
+                                                                                * node */
+       WorkerNodeInstrumentation *worker_instrument;   /* per-worker
+                                                                                                        * instrumentation */
 
        /* Per-worker JIT instrumentation */
        struct SharedJitInstrumentation *worker_jit_instrument;
index 6a328fceaeeba84817ccf4e09af055aa6670c51e..ca0c86d9e594bac5786b56e5c40883d0174035c3 100644 (file)
@@ -1824,6 +1824,7 @@ NextSampleBlock_function
 NextSampleTuple_function
 NextValueExpr
 Node
+NodeInstrumentation
 NodeTag
 NonEmptyRange
 NoneCompressorState
@@ -3438,9 +3439,9 @@ WorkTableScan
 WorkTableScanState
 WorkerInfo
 WorkerInfoData
-WorkerInstrumentation
 WorkerJobDumpPtrType
 WorkerJobRestorePtrType
+WorkerNodeInstrumentation
 Working_State
 WriteBufPtrType
 WriteBytePtrType