instrumentation: Separate per-node logic from other uses

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
This commit is contained in:
Andres Freund 2026-04-05 17:18:00 -04:00
parent 7d9b74df53
commit 5a79e78501
11 changed files with 178 additions and 127 deletions

View 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)

View 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,

View 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);

View 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);
}
}

View 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);

View 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);

View 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;
}

View 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;
Assert(!add->running);
INSTR_TIME_ADD(dst->counter, add->counter);
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;
}

View 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);

View 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;

View 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