PostgreSQL Source Code git master
Loading...
Searching...
No Matches
instrument.c
Go to the documentation of this file.
1/*-------------------------------------------------------------------------
2 *
3 * instrument.c
4 * functions for instrumentation of plan execution
5 *
6 *
7 * Copyright (c) 2001-2026, PostgreSQL Global Development Group
8 *
9 * IDENTIFICATION
10 * src/backend/executor/instrument.c
11 *
12 *-------------------------------------------------------------------------
13 */
14#include "postgres.h"
15
16#include <unistd.h>
17
18#include "executor/executor.h"
19#include "executor/instrument.h"
20#include "executor/tuptable.h"
21#include "nodes/execnodes.h"
23#include "utils/guc_hooks.h"
24
29
30static void BufferUsageAdd(BufferUsage *dst, const BufferUsage *add);
31static void WalUsageAdd(WalUsage *dst, WalUsage *add);
32
33
34/* General purpose instrumentation handling */
36InstrAlloc(int instrument_options)
37{
39
40 InstrInitOptions(instr, instrument_options);
41 return instr;
42}
43
44void
45InstrInitOptions(Instrumentation *instr, int instrument_options)
46{
47 instr->need_bufusage = (instrument_options & INSTRUMENT_BUFFERS) != 0;
48 instr->need_walusage = (instrument_options & INSTRUMENT_WAL) != 0;
49 instr->need_timer = (instrument_options & INSTRUMENT_TIMER) != 0;
50}
51
52inline void
54{
55 if (instr->need_timer)
56 {
57 if (!INSTR_TIME_IS_ZERO(instr->starttime))
58 elog(ERROR, "InstrStart called twice in a row");
59 else
61 }
62
63 /* save buffer usage totals at start, if needed */
64 if (instr->need_bufusage)
66
67 if (instr->need_walusage)
69}
70
71/*
72 * Helper for InstrStop() and InstrStopNode(), to avoid code duplication
73 * despite slightly different needs about how time is accumulated.
74 */
75static inline void
77{
79
80 /* update the time only if the timer was requested */
81 if (instr->need_timer)
82 {
83 if (INSTR_TIME_IS_ZERO(instr->starttime))
84 elog(ERROR, "InstrStop called without start");
85
88
90 }
91
92 /* Add delta of buffer usage since InstrStart to the totals */
93 if (instr->need_bufusage)
96
97 if (instr->need_walusage)
99 &pgWalUsage, &instr->walusage_start);
100}
101
102void
104{
105 InstrStopCommon(instr, &instr->total);
106}
107
108/* Node instrumentation handling */
109
110/* Allocate new node instrumentation structure */
112InstrAllocNode(int instrument_options, bool async_mode)
113{
115
116 InstrInitNode(instr, instrument_options, async_mode);
117
118 return instr;
119}
120
121/* Initialize a pre-allocated instrumentation structure. */
122void
123InstrInitNode(NodeInstrumentation *instr, int instrument_options, bool async_mode)
124{
125 memset(instr, 0, sizeof(NodeInstrumentation));
126 InstrInitOptions(&instr->instr, instrument_options);
127 instr->async_mode = async_mode;
128}
129
130/* Entry to a plan node */
131inline void
133{
134 InstrStart(&instr->instr);
135}
136
137/* Exit from a plan node */
138inline void
139InstrStopNode(NodeInstrumentation *instr, double nTuples)
140{
141 double save_tuplecount = instr->tuplecount;
142
143 /* count the returned tuples */
144 instr->tuplecount += nTuples;
145
146 /*
147 * Note that in contrast to InstrStop() the time is accumulated into
148 * NodeInstrumentation->counter, with total only getting updated in
149 * InstrEndLoop. We need the separate counter variable because we need to
150 * calculate start-up time for the first tuple in each cycle, and then
151 * accumulate it together.
152 */
153 InstrStopCommon(&instr->instr, &instr->counter);
154
155 /* Is this the first tuple of this cycle? */
156 if (!instr->running)
157 {
158 instr->running = true;
159 instr->firsttuple = instr->counter;
160 }
161 else
162 {
163 /*
164 * In async mode, if the plan node hadn't emitted any tuples before,
165 * this might be the first tuple
166 */
167 if (instr->async_mode && save_tuplecount < 1.0)
168 instr->firsttuple = instr->counter;
169 }
170}
171
172/*
173 * ExecProcNode wrapper that performs instrumentation calls. By keeping
174 * this a separate function, we avoid overhead in the normal case where
175 * no instrumentation is wanted.
176 *
177 * This is implemented in instrument.c as all the functions it calls directly
178 * are here, allowing them to be inlined even when not using LTO.
179 */
182{
184
186
187 result = node->ExecProcNodeReal(node);
188
189 InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0);
190
191 return result;
192}
193
194/* Update tuple count */
195void
197{
198 /* count the returned tuples */
199 instr->tuplecount += nTuples;
200}
201
202/* Finish a run cycle for a plan node */
203void
205{
206 /* Skip if nothing has happened, or already shut down */
207 if (!instr->running)
208 return;
209
211 elog(ERROR, "InstrEndLoop called on running node");
212
213 /* Accumulate per-cycle statistics into totals */
214 INSTR_TIME_ADD(instr->startup, instr->firsttuple);
215 INSTR_TIME_ADD(instr->instr.total, instr->counter);
216 instr->ntuples += instr->tuplecount;
217 instr->nloops += 1;
218
219 /* Reset for next cycle (if any) */
220 instr->running = false;
224 instr->tuplecount = 0;
225}
226
227/*
228 * Aggregate instrumentation from parallel workers. Must be called after
229 * InstrEndLoop.
230 */
231void
233{
234 Assert(!add->running);
235
236 INSTR_TIME_ADD(dst->startup, add->startup);
237 INSTR_TIME_ADD(dst->instr.total, add->instr.total);
238 dst->ntuples += add->ntuples;
239 dst->ntuples2 += add->ntuples2;
240 dst->nloops += add->nloops;
241 dst->nfiltered1 += add->nfiltered1;
242 dst->nfiltered2 += add->nfiltered2;
243
244 if (dst->instr.need_bufusage)
245 BufferUsageAdd(&dst->instr.bufusage, &add->instr.bufusage);
246
247 if (dst->instr.need_walusage)
248 WalUsageAdd(&dst->instr.walusage, &add->instr.walusage);
249}
250
251/* Trigger instrumentation handling */
253InstrAllocTrigger(int n, int instrument_options)
254{
256 int i;
257
258 for (i = 0; i < n; i++)
259 InstrInitOptions(&tginstr[i].instr, instrument_options);
260
261 return tginstr;
262}
263
264void
269
270void
272{
273 InstrStop(&tginstr->instr);
274 tginstr->firings += firings;
275}
276
277/* note current values during parallel executor startup */
278void
284
285/* report usage after parallel executor shutdown */
286void
288{
289 memset(bufusage, 0, sizeof(BufferUsage));
291 memset(walusage, 0, sizeof(WalUsage));
293}
294
295/* accumulate work done by workers in leader's stats */
296void
298{
299 BufferUsageAdd(&pgBufferUsage, bufusage);
300 WalUsageAdd(&pgWalUsage, walusage);
301}
302
303/* dst += add */
304static void
306{
307 dst->shared_blks_hit += add->shared_blks_hit;
308 dst->shared_blks_read += add->shared_blks_read;
309 dst->shared_blks_dirtied += add->shared_blks_dirtied;
310 dst->shared_blks_written += add->shared_blks_written;
311 dst->local_blks_hit += add->local_blks_hit;
312 dst->local_blks_read += add->local_blks_read;
313 dst->local_blks_dirtied += add->local_blks_dirtied;
314 dst->local_blks_written += add->local_blks_written;
315 dst->temp_blks_read += add->temp_blks_read;
316 dst->temp_blks_written += add->temp_blks_written;
317 INSTR_TIME_ADD(dst->shared_blk_read_time, add->shared_blk_read_time);
318 INSTR_TIME_ADD(dst->shared_blk_write_time, add->shared_blk_write_time);
319 INSTR_TIME_ADD(dst->local_blk_read_time, add->local_blk_read_time);
320 INSTR_TIME_ADD(dst->local_blk_write_time, add->local_blk_write_time);
321 INSTR_TIME_ADD(dst->temp_blk_read_time, add->temp_blk_read_time);
322 INSTR_TIME_ADD(dst->temp_blk_write_time, add->temp_blk_write_time);
323}
324
325/* dst += add - sub */
326inline void
328 const BufferUsage *add,
329 const BufferUsage *sub)
330{
331 dst->shared_blks_hit += add->shared_blks_hit - sub->shared_blks_hit;
332 dst->shared_blks_read += add->shared_blks_read - sub->shared_blks_read;
333 dst->shared_blks_dirtied += add->shared_blks_dirtied - sub->shared_blks_dirtied;
334 dst->shared_blks_written += add->shared_blks_written - sub->shared_blks_written;
335 dst->local_blks_hit += add->local_blks_hit - sub->local_blks_hit;
336 dst->local_blks_read += add->local_blks_read - sub->local_blks_read;
337 dst->local_blks_dirtied += add->local_blks_dirtied - sub->local_blks_dirtied;
338 dst->local_blks_written += add->local_blks_written - sub->local_blks_written;
339 dst->temp_blks_read += add->temp_blks_read - sub->temp_blks_read;
340 dst->temp_blks_written += add->temp_blks_written - sub->temp_blks_written;
341 INSTR_TIME_ACCUM_DIFF(dst->shared_blk_read_time,
342 add->shared_blk_read_time, sub->shared_blk_read_time);
343 INSTR_TIME_ACCUM_DIFF(dst->shared_blk_write_time,
344 add->shared_blk_write_time, sub->shared_blk_write_time);
345 INSTR_TIME_ACCUM_DIFF(dst->local_blk_read_time,
346 add->local_blk_read_time, sub->local_blk_read_time);
347 INSTR_TIME_ACCUM_DIFF(dst->local_blk_write_time,
348 add->local_blk_write_time, sub->local_blk_write_time);
349 INSTR_TIME_ACCUM_DIFF(dst->temp_blk_read_time,
350 add->temp_blk_read_time, sub->temp_blk_read_time);
351 INSTR_TIME_ACCUM_DIFF(dst->temp_blk_write_time,
352 add->temp_blk_write_time, sub->temp_blk_write_time);
353}
354
355/* helper functions for WAL usage accumulation */
356static inline void
358{
359 dst->wal_bytes += add->wal_bytes;
360 dst->wal_records += add->wal_records;
361 dst->wal_fpi += add->wal_fpi;
362 dst->wal_fpi_bytes += add->wal_fpi_bytes;
363 dst->wal_buffers_full += add->wal_buffers_full;
364}
365
366inline void
368{
369 dst->wal_bytes += add->wal_bytes - sub->wal_bytes;
370 dst->wal_records += add->wal_records - sub->wal_records;
371 dst->wal_fpi += add->wal_fpi - sub->wal_fpi;
372 dst->wal_fpi_bytes += add->wal_fpi_bytes - sub->wal_fpi_bytes;
373 dst->wal_buffers_full += add->wal_buffers_full - sub->wal_buffers_full;
374}
375
376/* GUC hooks for timing_clock_source */
377
378bool
380{
381 /*
382 * Do nothing if timing is not initialized. This is only expected on child
383 * processes in EXEC_BACKEND builds, as GUC hooks can be called during
384 * InitializeGUCOptions() before InitProcessGlobals() has had a chance to
385 * run pg_initialize_timing(). Instead, TSC will be initialized via
386 * restore_backend_variables.
387 */
388#ifdef EXEC_BACKEND
390 return true;
391#else
393#endif
394
395#if PG_INSTR_TSC_CLOCK
397
399 {
400 GUC_check_errdetail("TSC is not supported as timing clock source");
401 return false;
402 }
403#endif
404
405 return true;
406}
407
408void
410{
411#ifdef EXEC_BACKEND
413 return;
414#else
416#endif
417
418 /*
419 * Ignore the return code since the check hook already verified TSC is
420 * usable if it's explicitly requested.
421 */
423}
424
425const char *
427{
428 switch (timing_clock_source)
429 {
431#if PG_INSTR_TSC_CLOCK
433 return "auto (tsc)";
434#endif
435 return "auto (system)";
437 return "system";
438#if PG_INSTR_TSC_CLOCK
440 return "tsc";
441#endif
442 }
443
444 /* unreachable */
445 return "?";
446}
#define Assert(condition)
Definition c.h:943
int64_t int64
Definition c.h:621
uint32 result
#define ERROR
Definition elog.h:40
#define elog(elevel,...)
Definition elog.h:228
#define palloc_object(type)
Definition fe_memutils.h:74
#define palloc0_array(type, count)
Definition fe_memutils.h:77
#define palloc0_object(type)
Definition fe_memutils.h:75
#define newval
#define GUC_check_errdetail
Definition guc.h:507
GucSource
Definition guc.h:112
int timing_clock_source
Definition instr_time.c:64
int32 timing_tsc_frequency_khz
Definition instr_time.c:67
bool pg_set_timing_clock_source(TimingClockSourceType source)
Definition instr_time.c:92
bool timing_initialized
Definition instr_time.c:63
#define INSTR_TIME_ADD(x, y)
Definition instr_time.h:430
#define INSTR_TIME_IS_ZERO(t)
Definition instr_time.h:419
#define INSTR_TIME_SET_CURRENT_FAST(t)
Definition instr_time.h:423
static TimingClockSourceType pg_current_timing_clock_source(void)
Definition instr_time.h:178
@ TIMING_CLOCK_SOURCE_SYSTEM
Definition instr_time.h:126
@ TIMING_CLOCK_SOURCE_AUTO
Definition instr_time.h:125
#define INSTR_TIME_SET_ZERO(t)
Definition instr_time.h:421
#define INSTR_TIME_ACCUM_DIFF(x, y, z)
Definition instr_time.h:439
bool check_timing_clock_source(int *newval, void **extra, GucSource source)
Definition instrument.c:379
void InstrAccumParallelQuery(BufferUsage *bufusage, WalUsage *walusage)
Definition instrument.c:297
void InstrStart(Instrumentation *instr)
Definition instrument.c:53
static void BufferUsageAdd(BufferUsage *dst, const BufferUsage *add)
Definition instrument.c:305
void InstrUpdateTupleCount(NodeInstrumentation *instr, double nTuples)
Definition instrument.c:196
static BufferUsage save_pgBufferUsage
Definition instrument.c:26
void InstrInitNode(NodeInstrumentation *instr, int instrument_options, bool async_mode)
Definition instrument.c:123
void InstrStop(Instrumentation *instr)
Definition instrument.c:103
void InstrEndParallelQuery(BufferUsage *bufusage, WalUsage *walusage)
Definition instrument.c:287
const char * show_timing_clock_source(void)
Definition instrument.c:426
static WalUsage save_pgWalUsage
Definition instrument.c:28
static void WalUsageAdd(WalUsage *dst, WalUsage *add)
Definition instrument.c:357
TupleTableSlot * ExecProcNodeInstr(PlanState *node)
Definition instrument.c:181
void InstrStartNode(NodeInstrumentation *instr)
Definition instrument.c:132
WalUsage pgWalUsage
Definition instrument.c:27
void WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
Definition instrument.c:367
void InstrStartParallelQuery(void)
Definition instrument.c:279
void InstrInitOptions(Instrumentation *instr, int instrument_options)
Definition instrument.c:45
static void InstrStopCommon(Instrumentation *instr, instr_time *accum_time)
Definition instrument.c:76
Instrumentation * InstrAlloc(int instrument_options)
Definition instrument.c:36
TriggerInstrumentation * InstrAllocTrigger(int n, int instrument_options)
Definition instrument.c:253
BufferUsage pgBufferUsage
Definition instrument.c:25
void InstrStopNode(NodeInstrumentation *instr, double nTuples)
Definition instrument.c:139
void BufferUsageAccumDiff(BufferUsage *dst, const BufferUsage *add, const BufferUsage *sub)
Definition instrument.c:327
void InstrEndLoop(NodeInstrumentation *instr)
Definition instrument.c:204
NodeInstrumentation * InstrAllocNode(int instrument_options, bool async_mode)
Definition instrument.c:112
void InstrStartTrigger(TriggerInstrumentation *tginstr)
Definition instrument.c:265
void InstrAggNode(NodeInstrumentation *dst, NodeInstrumentation *add)
Definition instrument.c:232
void InstrStopTrigger(TriggerInstrumentation *tginstr, int64 firings)
Definition instrument.c:271
void assign_timing_clock_source(int newval, void *extra)
Definition instrument.c:409
@ INSTRUMENT_TIMER
Definition instrument.h:63
@ INSTRUMENT_BUFFERS
Definition instrument.h:64
@ INSTRUMENT_WAL
Definition instrument.h:66
int i
Definition isn.c:77
static rewind_source * source
Definition pg_rewind.c:89
static int fb(int x)
instr_time local_blk_read_time
Definition instrument.h:38
int64 shared_blks_dirtied
Definition instrument.h:28
int64 local_blks_hit
Definition instrument.h:30
instr_time temp_blk_write_time
Definition instrument.h:41
instr_time shared_blk_read_time
Definition instrument.h:36
instr_time shared_blk_write_time
Definition instrument.h:37
int64 local_blks_written
Definition instrument.h:33
instr_time temp_blk_read_time
Definition instrument.h:40
instr_time local_blk_write_time
Definition instrument.h:39
int64 temp_blks_read
Definition instrument.h:34
int64 shared_blks_read
Definition instrument.h:27
int64 shared_blks_written
Definition instrument.h:29
int64 temp_blks_written
Definition instrument.h:35
int64 local_blks_read
Definition instrument.h:31
int64 local_blks_dirtied
Definition instrument.h:32
int64 shared_blks_hit
Definition instrument.h:26
BufferUsage bufusage_start
Definition instrument.h:85
WalUsage walusage
Definition instrument.h:90
instr_time total
Definition instrument.h:88
BufferUsage bufusage
Definition instrument.h:89
instr_time starttime
Definition instrument.h:84
WalUsage walusage_start
Definition instrument.h:86
Instrumentation instr
Definition instrument.h:98
instr_time firsttuple
Definition instrument.h:104
ExecProcNodeMtd ExecProcNodeReal
Definition execnodes.h:1208
NodeInstrumentation * instrument
Definition execnodes.h:1211
int64 wal_buffers_full
Definition instrument.h:57
uint64 wal_bytes
Definition instrument.h:55
int64 wal_fpi
Definition instrument.h:54
uint64 wal_fpi_bytes
Definition instrument.h:56
int64 wal_records
Definition instrument.h:53
#define TupIsNull(slot)
Definition tuptable.h:325