Age Owner Branch data TLA Line data Source code
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"
22 : : #include "portability/instr_time.h"
23 : : #include "utils/guc_hooks.h"
24 : :
25 : : BufferUsage pgBufferUsage;
26 : : static BufferUsage save_pgBufferUsage;
27 : : WalUsage pgWalUsage;
28 : : static WalUsage save_pgWalUsage;
29 : :
30 : : static void BufferUsageAdd(BufferUsage *dst, const BufferUsage *add);
31 : : static void WalUsageAdd(WalUsage *dst, WalUsage *add);
32 : :
33 : :
34 : : /* General purpose instrumentation handling */
35 : : Instrumentation *
30 andres@anarazel.de 36 :GNC 42766 : InstrAlloc(int instrument_options)
37 : : {
38 : 42766 : Instrumentation *instr = palloc0_object(Instrumentation);
39 : :
40 : 42766 : InstrInitOptions(instr, instrument_options);
8995 tgl@sss.pgh.pa.us 41 :CBC 42766 : return instr;
42 : : }
43 : :
44 : : void
30 andres@anarazel.de 45 :GNC 50881 : InstrInitOptions(Instrumentation *instr, int instrument_options)
46 : : {
3872 rhaas@postgresql.org 47 :CBC 50881 : instr->need_bufusage = (instrument_options & INSTRUMENT_BUFFERS) != 0;
2222 akapila@postgresql.o 48 : 50881 : instr->need_walusage = (instrument_options & INSTRUMENT_WAL) != 0;
3872 rhaas@postgresql.org 49 : 50881 : instr->need_timer = (instrument_options & INSTRUMENT_TIMER) != 0;
50 : 50881 : }
51 : :
52 : : inline void
30 andres@anarazel.de 53 :GNC 9102648 : InstrStart(Instrumentation *instr)
54 : : {
68 55 [ + + ]: 9102648 : if (instr->need_timer)
56 : : {
57 [ - + ]: 7203666 : if (!INSTR_TIME_IS_ZERO(instr->starttime))
30 andres@anarazel.de 58 [ # # ]:UNC 0 : elog(ERROR, "InstrStart called twice in a row");
59 : : else
28 andres@anarazel.de 60 :GNC 7203666 : INSTR_TIME_SET_CURRENT_FAST(instr->starttime);
61 : : }
62 : :
63 : : /* save buffer usage totals at start, if needed */
5339 tgl@sss.pgh.pa.us 64 [ + + ]:CBC 9102648 : if (instr->need_bufusage)
5985 rhaas@postgresql.org 65 : 7151555 : instr->bufusage_start = pgBufferUsage;
66 : :
2222 akapila@postgresql.o 67 [ + + ]: 9102648 : if (instr->need_walusage)
68 : 83395 : instr->walusage_start = pgWalUsage;
8995 tgl@sss.pgh.pa.us 69 : 9102648 : }
70 : :
71 : : /*
72 : : * Helper for InstrStop() and InstrStopNode(), to avoid code duplication
73 : : * despite slightly different needs about how time is accumulated.
74 : : */
75 : : static inline void
30 andres@anarazel.de 76 :GNC 9100975 : InstrStopCommon(Instrumentation *instr, instr_time *accum_time)
77 : : {
78 : : instr_time endtime;
79 : :
80 : : /* update the time only if the timer was requested */
5201 rhaas@postgresql.org 81 [ + + ]:CBC 9100975 : if (instr->need_timer)
82 : : {
83 [ - + ]: 7202001 : if (INSTR_TIME_IS_ZERO(instr->starttime))
30 andres@anarazel.de 84 [ # # ]:UNC 0 : elog(ERROR, "InstrStop called without start");
85 : :
28 andres@anarazel.de 86 :GNC 7202001 : INSTR_TIME_SET_CURRENT_FAST(endtime);
30 87 : 7202001 : INSTR_TIME_ACCUM_DIFF(*accum_time, endtime, instr->starttime);
88 : :
5201 rhaas@postgresql.org 89 :CBC 7202001 : INSTR_TIME_SET_ZERO(instr->starttime);
90 : : }
91 : :
92 : : /* Add delta of buffer usage since InstrStart to the totals */
5339 tgl@sss.pgh.pa.us 93 [ + + ]: 9100975 : if (instr->need_bufusage)
5985 rhaas@postgresql.org 94 : 7149890 : BufferUsageAccumDiff(&instr->bufusage,
5912 bruce@momjian.us 95 : 7149890 : &pgBufferUsage, &instr->bufusage_start);
96 : :
2222 akapila@postgresql.o 97 [ + + ]: 9100975 : if (instr->need_walusage)
98 : 81730 : WalUsageAccumDiff(&instr->walusage,
99 : 81730 : &pgWalUsage, &instr->walusage_start);
30 andres@anarazel.de 100 :GNC 9100975 : }
101 : :
102 : : void
103 : 81752 : InstrStop(Instrumentation *instr)
104 : : {
105 : 81752 : InstrStopCommon(instr, &instr->total);
106 : 81752 : }
107 : :
108 : : /* Node instrumentation handling */
109 : :
110 : : /* Allocate new node instrumentation structure */
111 : : NodeInstrumentation *
112 : 6995 : InstrAllocNode(int instrument_options, bool async_mode)
113 : : {
114 : 6995 : NodeInstrumentation *instr = palloc_object(NodeInstrumentation);
115 : :
116 : 6995 : InstrInitNode(instr, instrument_options, async_mode);
117 : :
118 : 6995 : return instr;
119 : : }
120 : :
121 : : /* Initialize a pre-allocated instrumentation structure. */
122 : : void
123 : 8115 : InstrInitNode(NodeInstrumentation *instr, int instrument_options, bool async_mode)
124 : : {
125 : 8115 : memset(instr, 0, sizeof(NodeInstrumentation));
126 : 8115 : InstrInitOptions(&instr->instr, instrument_options);
127 : 8115 : instr->async_mode = async_mode;
128 : 8115 : }
129 : :
130 : : /* Entry to a plan node */
131 : : inline void
132 : 9019231 : InstrStartNode(NodeInstrumentation *instr)
133 : : {
134 : 9019231 : InstrStart(&instr->instr);
135 : 9019231 : }
136 : :
137 : : /* Exit from a plan node */
138 : : inline void
139 : 9019223 : InstrStopNode(NodeInstrumentation *instr, double nTuples)
140 : : {
141 : 9019223 : double save_tuplecount = instr->tuplecount;
142 : :
143 : : /* count the returned tuples */
144 : 9019223 : 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 : 9019223 : InstrStopCommon(&instr->instr, &instr->counter);
154 : :
155 : : /* Is this the first tuple of this cycle? */
8995 tgl@sss.pgh.pa.us 156 [ + + ]:CBC 9019223 : if (!instr->running)
157 : : {
158 : 34219 : instr->running = true;
116 andres@anarazel.de 159 :GNC 34219 : 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 : : */
1819 efujita@postgresql.o 167 [ + + + + ]:CBC 8985004 : if (instr->async_mode && save_tuplecount < 1.0)
116 andres@anarazel.de 168 :GNC 22 : instr->firsttuple = instr->counter;
169 : : }
1819 efujita@postgresql.o 170 :CBC 9019223 : }
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 : : */
180 : : TupleTableSlot *
28 andres@anarazel.de 181 :GNC 9011913 : ExecProcNodeInstr(PlanState *node)
182 : : {
183 : : TupleTableSlot *result;
184 : :
185 : 9011913 : InstrStartNode(node->instrument);
186 : :
187 : 9011913 : result = node->ExecProcNodeReal(node);
188 : :
189 [ + + + + ]: 9011905 : InstrStopNode(node->instrument, TupIsNull(result) ? 0.0 : 1.0);
190 : :
191 : 9011905 : return result;
192 : : }
193 : :
194 : : /* Update tuple count */
195 : : void
30 196 : 2 : InstrUpdateTupleCount(NodeInstrumentation *instr, double nTuples)
197 : : {
198 : : /* count the returned tuples */
1819 efujita@postgresql.o 199 :CBC 2 : instr->tuplecount += nTuples;
7689 tgl@sss.pgh.pa.us 200 : 2 : }
201 : :
202 : : /* Finish a run cycle for a plan node */
203 : : void
30 andres@anarazel.de 204 :GNC 35586 : InstrEndLoop(NodeInstrumentation *instr)
205 : : {
206 : : /* Skip if nothing has happened, or already shut down */
8995 tgl@sss.pgh.pa.us 207 [ + + ]:CBC 35586 : if (!instr->running)
208 : 1376 : return;
209 : :
30 andres@anarazel.de 210 [ - + ]:GNC 34210 : if (!INSTR_TIME_IS_ZERO(instr->instr.starttime))
4835 tgl@sss.pgh.pa.us 211 [ # # ]:UBC 0 : elog(ERROR, "InstrEndLoop called on running node");
212 : :
213 : : /* Accumulate per-cycle statistics into totals */
116 andres@anarazel.de 214 :GNC 34210 : INSTR_TIME_ADD(instr->startup, instr->firsttuple);
30 215 : 34210 : INSTR_TIME_ADD(instr->instr.total, instr->counter);
8995 tgl@sss.pgh.pa.us 216 :CBC 34210 : instr->ntuples += instr->tuplecount;
217 : 34210 : instr->nloops += 1;
218 : :
219 : : /* Reset for next cycle (if any) */
220 : 34210 : instr->running = false;
30 andres@anarazel.de 221 :GNC 34210 : INSTR_TIME_SET_ZERO(instr->instr.starttime);
7716 tgl@sss.pgh.pa.us 222 :CBC 34210 : INSTR_TIME_SET_ZERO(instr->counter);
116 andres@anarazel.de 223 :GNC 34210 : INSTR_TIME_SET_ZERO(instr->firsttuple);
8995 tgl@sss.pgh.pa.us 224 :CBC 34210 : instr->tuplecount = 0;
225 : : }
226 : :
227 : : /*
228 : : * Aggregate instrumentation from parallel workers. Must be called after
229 : : * InstrEndLoop.
230 : : */
231 : : void
30 andres@anarazel.de 232 :GNC 2704 : InstrAggNode(NodeInstrumentation *dst, NodeInstrumentation *add)
233 : : {
234 [ - + ]: 2704 : Assert(!add->running);
235 : :
116 236 : 2704 : INSTR_TIME_ADD(dst->startup, add->startup);
30 237 : 2704 : INSTR_TIME_ADD(dst->instr.total, add->instr.total);
3872 rhaas@postgresql.org 238 :CBC 2704 : dst->ntuples += add->ntuples;
2947 alvherre@alvh.no-ip. 239 : 2704 : dst->ntuples2 += add->ntuples2;
3872 rhaas@postgresql.org 240 : 2704 : dst->nloops += add->nloops;
241 : 2704 : dst->nfiltered1 += add->nfiltered1;
242 : 2704 : dst->nfiltered2 += add->nfiltered2;
243 : :
30 andres@anarazel.de 244 [ + + ]:GNC 2704 : if (dst->instr.need_bufusage)
245 : 1272 : BufferUsageAdd(&dst->instr.bufusage, &add->instr.bufusage);
246 : :
247 [ - + ]: 2704 : if (dst->instr.need_walusage)
30 andres@anarazel.de 248 :UNC 0 : WalUsageAdd(&dst->instr.walusage, &add->instr.walusage);
3872 rhaas@postgresql.org 249 :GNC 2704 : }
250 : :
251 : : /* Trigger instrumentation handling */
252 : : TriggerInstrumentation *
30 andres@anarazel.de 253 :UNC 0 : InstrAllocTrigger(int n, int instrument_options)
254 : : {
255 : 0 : TriggerInstrumentation *tginstr = palloc0_array(TriggerInstrumentation, n);
256 : : int i;
257 : :
258 [ # # ]: 0 : for (i = 0; i < n; i++)
259 : 0 : InstrInitOptions(&tginstr[i].instr, instrument_options);
260 : :
261 : 0 : return tginstr;
262 : : }
263 : :
264 : : void
265 : 0 : InstrStartTrigger(TriggerInstrumentation *tginstr)
266 : : {
267 : 0 : InstrStart(&tginstr->instr);
268 : 0 : }
269 : :
270 : : void
271 : 0 : InstrStopTrigger(TriggerInstrumentation *tginstr, int64 firings)
272 : : {
273 : 0 : InstrStop(&tginstr->instr);
274 : 0 : tginstr->firings += firings;
30 andres@anarazel.de 275 :LBC (2028) : }
276 : :
277 : : /* note current values during parallel executor startup */
278 : : void
3872 rhaas@postgresql.org 279 :CBC 2008 : InstrStartParallelQuery(void)
280 : : {
281 : 2008 : save_pgBufferUsage = pgBufferUsage;
2222 akapila@postgresql.o 282 : 2008 : save_pgWalUsage = pgWalUsage;
3872 rhaas@postgresql.org 283 : 2008 : }
284 : :
285 : : /* report usage after parallel executor shutdown */
286 : : void
2222 akapila@postgresql.o 287 : 2000 : InstrEndParallelQuery(BufferUsage *bufusage, WalUsage *walusage)
288 : : {
2217 289 : 2000 : memset(bufusage, 0, sizeof(BufferUsage));
290 : 2000 : BufferUsageAccumDiff(bufusage, &pgBufferUsage, &save_pgBufferUsage);
2222 291 : 2000 : memset(walusage, 0, sizeof(WalUsage));
292 : 2000 : WalUsageAccumDiff(walusage, &pgWalUsage, &save_pgWalUsage);
3872 rhaas@postgresql.org 293 : 2000 : }
294 : :
295 : : /* accumulate work done by workers in leader's stats */
296 : : void
2222 akapila@postgresql.o 297 : 1999 : InstrAccumParallelQuery(BufferUsage *bufusage, WalUsage *walusage)
298 : : {
2217 299 : 1999 : BufferUsageAdd(&pgBufferUsage, bufusage);
2222 300 : 1999 : WalUsageAdd(&pgWalUsage, walusage);
3872 rhaas@postgresql.org 301 : 1999 : }
302 : :
303 : : /* dst += add */
304 : : static void
305 : 3271 : BufferUsageAdd(BufferUsage *dst, const BufferUsage *add)
306 : : {
307 : 3271 : dst->shared_blks_hit += add->shared_blks_hit;
308 : 3271 : dst->shared_blks_read += add->shared_blks_read;
309 : 3271 : dst->shared_blks_dirtied += add->shared_blks_dirtied;
310 : 3271 : dst->shared_blks_written += add->shared_blks_written;
311 : 3271 : dst->local_blks_hit += add->local_blks_hit;
312 : 3271 : dst->local_blks_read += add->local_blks_read;
313 : 3271 : dst->local_blks_dirtied += add->local_blks_dirtied;
314 : 3271 : dst->local_blks_written += add->local_blks_written;
315 : 3271 : dst->temp_blks_read += add->temp_blks_read;
316 : 3271 : dst->temp_blks_written += add->temp_blks_written;
929 michael@paquier.xyz 317 : 3271 : INSTR_TIME_ADD(dst->shared_blk_read_time, add->shared_blk_read_time);
318 : 3271 : INSTR_TIME_ADD(dst->shared_blk_write_time, add->shared_blk_write_time);
319 : 3271 : INSTR_TIME_ADD(dst->local_blk_read_time, add->local_blk_read_time);
320 : 3271 : INSTR_TIME_ADD(dst->local_blk_write_time, add->local_blk_write_time);
1488 321 : 3271 : INSTR_TIME_ADD(dst->temp_blk_read_time, add->temp_blk_read_time);
322 : 3271 : INSTR_TIME_ADD(dst->temp_blk_write_time, add->temp_blk_write_time);
3872 rhaas@postgresql.org 323 : 3271 : }
324 : :
325 : : /* dst += add - sub */
326 : : inline void
5985 327 : 7179660 : BufferUsageAccumDiff(BufferUsage *dst,
328 : : const BufferUsage *add,
329 : : const BufferUsage *sub)
330 : : {
331 : 7179660 : dst->shared_blks_hit += add->shared_blks_hit - sub->shared_blks_hit;
332 : 7179660 : dst->shared_blks_read += add->shared_blks_read - sub->shared_blks_read;
5186 333 : 7179660 : dst->shared_blks_dirtied += add->shared_blks_dirtied - sub->shared_blks_dirtied;
5985 334 : 7179660 : dst->shared_blks_written += add->shared_blks_written - sub->shared_blks_written;
335 : 7179660 : dst->local_blks_hit += add->local_blks_hit - sub->local_blks_hit;
336 : 7179660 : dst->local_blks_read += add->local_blks_read - sub->local_blks_read;
5186 337 : 7179660 : dst->local_blks_dirtied += add->local_blks_dirtied - sub->local_blks_dirtied;
5985 338 : 7179660 : dst->local_blks_written += add->local_blks_written - sub->local_blks_written;
339 : 7179660 : dst->temp_blks_read += add->temp_blks_read - sub->temp_blks_read;
340 : 7179660 : dst->temp_blks_written += add->temp_blks_written - sub->temp_blks_written;
929 michael@paquier.xyz 341 : 7179660 : INSTR_TIME_ACCUM_DIFF(dst->shared_blk_read_time,
342 : : add->shared_blk_read_time, sub->shared_blk_read_time);
343 : 7179660 : INSTR_TIME_ACCUM_DIFF(dst->shared_blk_write_time,
344 : : add->shared_blk_write_time, sub->shared_blk_write_time);
345 : 7179660 : INSTR_TIME_ACCUM_DIFF(dst->local_blk_read_time,
346 : : add->local_blk_read_time, sub->local_blk_read_time);
347 : 7179660 : INSTR_TIME_ACCUM_DIFF(dst->local_blk_write_time,
348 : : add->local_blk_write_time, sub->local_blk_write_time);
1488 349 : 7179660 : INSTR_TIME_ACCUM_DIFF(dst->temp_blk_read_time,
350 : : add->temp_blk_read_time, sub->temp_blk_read_time);
351 : 7179660 : INSTR_TIME_ACCUM_DIFF(dst->temp_blk_write_time,
352 : : add->temp_blk_write_time, sub->temp_blk_write_time);
5985 rhaas@postgresql.org 353 : 7179660 : }
354 : :
355 : : /* helper functions for WAL usage accumulation */
356 : : static inline void
2222 akapila@postgresql.o 357 : 1999 : WalUsageAdd(WalUsage *dst, WalUsage *add)
358 : : {
359 : 1999 : dst->wal_bytes += add->wal_bytes;
360 : 1999 : dst->wal_records += add->wal_records;
2191 361 : 1999 : dst->wal_fpi += add->wal_fpi;
189 michael@paquier.xyz 362 :GNC 1999 : dst->wal_fpi_bytes += add->wal_fpi_bytes;
442 michael@paquier.xyz 363 :CBC 1999 : dst->wal_buffers_full += add->wal_buffers_full;
2222 akapila@postgresql.o 364 : 1999 : }
365 : :
366 : : inline void
367 : 133924 : WalUsageAccumDiff(WalUsage *dst, const WalUsage *add, const WalUsage *sub)
368 : : {
369 : 133924 : dst->wal_bytes += add->wal_bytes - sub->wal_bytes;
370 : 133924 : dst->wal_records += add->wal_records - sub->wal_records;
2191 371 : 133924 : dst->wal_fpi += add->wal_fpi - sub->wal_fpi;
189 michael@paquier.xyz 372 :GNC 133924 : dst->wal_fpi_bytes += add->wal_fpi_bytes - sub->wal_fpi_bytes;
442 michael@paquier.xyz 373 :CBC 133924 : dst->wal_buffers_full += add->wal_buffers_full - sub->wal_buffers_full;
2222 akapila@postgresql.o 374 : 133924 : }
375 : :
376 : : /* GUC hooks for timing_clock_source */
377 : :
378 : : bool
28 andres@anarazel.de 379 :GNC 1286 : check_timing_clock_source(int *newval, void **extra, GucSource source)
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
389 : : if (!timing_initialized)
390 : : return true;
391 : : #else
392 [ - + ]: 1286 : Assert(timing_initialized);
393 : : #endif
394 : :
395 : : #if PG_INSTR_TSC_CLOCK
396 : 1286 : pg_initialize_timing_tsc();
397 : :
398 [ - + - - ]: 1286 : if (*newval == TIMING_CLOCK_SOURCE_TSC && timing_tsc_frequency_khz <= 0)
399 : : {
28 andres@anarazel.de 400 :UNC 0 : GUC_check_errdetail("TSC is not supported as timing clock source");
401 : 0 : return false;
402 : : }
403 : : #endif
404 : :
28 andres@anarazel.de 405 :GNC 1286 : return true;
406 : : }
407 : :
408 : : void
409 : 1286 : assign_timing_clock_source(int newval, void *extra)
410 : : {
411 : : #ifdef EXEC_BACKEND
412 : : if (!timing_initialized)
413 : : return;
414 : : #else
415 [ - + ]: 1286 : Assert(timing_initialized);
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 : : */
422 : 1286 : pg_set_timing_clock_source(newval);
423 : 1286 : }
424 : :
425 : : const char *
426 : 1869 : show_timing_clock_source(void)
427 : : {
428 [ + - - - ]: 1869 : switch (timing_clock_source)
429 : : {
430 : 1869 : case TIMING_CLOCK_SOURCE_AUTO:
431 : : #if PG_INSTR_TSC_CLOCK
432 [ + - ]: 1869 : if (pg_current_timing_clock_source() == TIMING_CLOCK_SOURCE_TSC)
433 : 1869 : return "auto (tsc)";
434 : : #endif
28 andres@anarazel.de 435 :UNC 0 : return "auto (system)";
436 : 0 : case TIMING_CLOCK_SOURCE_SYSTEM:
437 : 0 : return "system";
438 : : #if PG_INSTR_TSC_CLOCK
439 : 0 : case TIMING_CLOCK_SOURCE_TSC:
440 : 0 : return "tsc";
441 : : #endif
442 : : }
443 : :
444 : : /* unreachable */
445 : 0 : return "?";
446 : : }
|