Branch data Line data Source code
1 : : /* ----------
2 : : * pgstat.c
3 : : * Infrastructure for the cumulative statistics system.
4 : : *
5 : : * The cumulative statistics system accumulates statistics for different kinds
6 : : * of objects. Some kinds of statistics are collected for a fixed number of
7 : : * objects (most commonly 1), e.g., checkpointer statistics. Other kinds of
8 : : * statistics are collected for a varying number of objects
9 : : * (e.g. relations). See PgStat_KindInfo for a list of currently handled
10 : : * statistics.
11 : : *
12 : : * Statistics are loaded from the filesystem during startup (by the startup
13 : : * process), unless preceded by a crash, in which case all stats are
14 : : * discarded. They are written out by the checkpointer process just before
15 : : * shutting down (if the stats kind allows it), except when shutting down in
16 : : * immediate mode.
17 : : *
18 : : * Fixed-numbered stats are stored in plain (non-dynamic) shared memory.
19 : : *
20 : : * Statistics for variable-numbered objects are stored in dynamic shared
21 : : * memory and can be found via shared stats dshash hashtables. The statistics
22 : : * counters are not part of the dshash entry (PgStatShared_HashEntry)
23 : : * directly, but are separately allocated (PgStatShared_HashEntry->body). The
24 : : * separate allocation allows different kinds of statistics to share a
25 : : * hashtable without wasting space in PgStatShared_HashEntry.
26 : : *
27 : : * Variable-numbered stats are addressed by PgStat_HashKey while running. It
28 : : * is not possible to have statistics for an object that cannot be addressed
29 : : * that way at runtime. A wider identifier can be used when serializing to
30 : : * disk (used for replication slot stats).
31 : : *
32 : : * To avoid contention on the shared stats hashtables, each backend has a
33 : : * backend-local hashtable (pgStatEntryRefHash) containing references
34 : : * (PgStat_EntryRef) to shared stats hashtable entries. The shared stats
35 : : * hashtables only need to be accessed when no prior reference is found in the
36 : : * local hashtable. Besides pointing to the shared stats hashtable entry
37 : : * (PgStatShared_HashEntry), PgStat_EntryRef also contains a process-local
38 : : * pointer to the shared statistics data, to reduce access costs.
39 : : *
40 : : * The names for structs stored in shared memory are prefixed with
41 : : * PgStatShared instead of PgStat. Each stats entry in shared memory is
42 : : * protected by a dedicated lwlock.
43 : : *
44 : : * Most stats updates are first accumulated locally in each process as pending
45 : : * entries, then later flushed to shared memory (just after commit, or by
46 : : * idle-timeout). This practically eliminates contention on individual stats
47 : : * entries. For most kinds of variable-numbered pending stats data is stored
48 : : * in PgStat_EntryRef->pending. All entries with pending data are in the
49 : : * pgStatPending list. Pending statistics updates are flushed out by
50 : : * pgstat_report_stat().
51 : : *
52 : : * It is possible for external modules to define custom statistics kinds,
53 : : * that can use the same properties as any built-in stats kinds. Each custom
54 : : * stats kind needs to assign a unique ID to ensure that it does not overlap
55 : : * with other extensions. In order to reserve a unique stats kind ID, refer
56 : : * to https://wiki.postgresql.org/wiki/CustomCumulativeStats.
57 : : *
58 : : * The behavior of different kinds of statistics is determined by the kind's
59 : : * entry in pgstat_kind_builtin_infos for all the built-in statistics kinds
60 : : * defined, and pgstat_kind_custom_infos for custom kinds registered at
61 : : * startup by pgstat_register_kind(). See PgStat_KindInfo for details.
62 : : *
63 : : * The consistency of read accesses to statistics can be configured using the
64 : : * stats_fetch_consistency GUC (see config.sgml and monitoring.sgml for the
65 : : * settings). When using PGSTAT_FETCH_CONSISTENCY_CACHE or
66 : : * PGSTAT_FETCH_CONSISTENCY_SNAPSHOT statistics are stored in
67 : : * pgStatLocal.snapshot.
68 : : *
69 : : * To keep things manageable, stats handling is split across several
70 : : * files. Infrastructure pieces are in:
71 : : * - pgstat.c - this file, to tie it all together
72 : : * - pgstat_shmem.c - nearly everything dealing with shared memory, including
73 : : * the maintenance of hashtable entries
74 : : * - pgstat_xact.c - transactional integration, including the transactional
75 : : * creation and dropping of stats entries
76 : : *
77 : : * Each statistics kind is handled in a dedicated file:
78 : : * - pgstat_archiver.c
79 : : * - pgstat_backend.c
80 : : * - pgstat_bgwriter.c
81 : : * - pgstat_checkpointer.c
82 : : * - pgstat_database.c
83 : : * - pgstat_function.c
84 : : * - pgstat_io.c
85 : : * - pgstat_lock.c
86 : : * - pgstat_relation.c
87 : : * - pgstat_replslot.c
88 : : * - pgstat_slru.c
89 : : * - pgstat_subscription.c
90 : : * - pgstat_wal.c
91 : : *
92 : : * Whenever possible infrastructure files should not contain code related to
93 : : * specific kinds of stats.
94 : : *
95 : : *
96 : : * Copyright (c) 2001-2026, PostgreSQL Global Development Group
97 : : *
98 : : * IDENTIFICATION
99 : : * src/backend/utils/activity/pgstat.c
100 : : * ----------
101 : : */
102 : : #include "postgres.h"
103 : :
104 : : #include <unistd.h>
105 : :
106 : : #include "access/xact.h"
107 : : #include "lib/dshash.h"
108 : : #include "pgstat.h"
109 : : #include "storage/fd.h"
110 : : #include "storage/ipc.h"
111 : : #include "storage/lwlock.h"
112 : : #include "utils/guc_hooks.h"
113 : : #include "utils/memutils.h"
114 : : #include "utils/pgstat_internal.h"
115 : : #include "utils/timestamp.h"
116 : :
117 : :
118 : : /* ----------
119 : : * Timer definitions.
120 : : *
121 : : * In milliseconds.
122 : : * ----------
123 : : */
124 : :
125 : : /* minimum interval non-forced stats flushes.*/
126 : : #define PGSTAT_MIN_INTERVAL 1000
127 : : /* how long until to block flushing pending stats updates */
128 : : #define PGSTAT_MAX_INTERVAL 60000
129 : : /* when to call pgstat_report_stat() again, even when idle */
130 : : #define PGSTAT_IDLE_INTERVAL 10000
131 : :
132 : : /* ----------
133 : : * Initial size hints for the hash tables used in statistics.
134 : : * ----------
135 : : */
136 : :
137 : : #define PGSTAT_SNAPSHOT_HASH_SIZE 512
138 : :
139 : : /* ---------
140 : : * Identifiers in stats file.
141 : : * ---------
142 : : */
143 : : #define PGSTAT_FILE_ENTRY_END 'E' /* end of file */
144 : : #define PGSTAT_FILE_ENTRY_FIXED 'F' /* fixed-numbered stats entry */
145 : : #define PGSTAT_FILE_ENTRY_NAME 'N' /* stats entry identified by name */
146 : : #define PGSTAT_FILE_ENTRY_HASH 'S' /* stats entry identified by
147 : : * PgStat_HashKey */
148 : :
149 : : /* hash table for statistics snapshots entry */
150 : : typedef struct PgStat_SnapshotEntry
151 : : {
152 : : PgStat_HashKey key;
153 : : char status; /* for simplehash use */
154 : : void *data; /* the stats data itself */
155 : : } PgStat_SnapshotEntry;
156 : :
157 : :
158 : : /* ----------
159 : : * Backend-local Hash Table Definitions
160 : : * ----------
161 : : */
162 : :
163 : : /* for stats snapshot entries */
164 : : #define SH_PREFIX pgstat_snapshot
165 : : #define SH_ELEMENT_TYPE PgStat_SnapshotEntry
166 : : #define SH_KEY_TYPE PgStat_HashKey
167 : : #define SH_KEY key
168 : : #define SH_HASH_KEY(tb, key) \
169 : : pgstat_hash_hash_key(&key, sizeof(PgStat_HashKey), NULL)
170 : : #define SH_EQUAL(tb, a, b) \
171 : : pgstat_cmp_hash_key(&a, &b, sizeof(PgStat_HashKey), NULL) == 0
172 : : #define SH_SCOPE static inline
173 : : #define SH_DEFINE
174 : : #define SH_DECLARE
175 : : #include "lib/simplehash.h"
176 : :
177 : :
178 : : /* ----------
179 : : * Local function forward declarations
180 : : * ----------
181 : : */
182 : :
183 : : static void pgstat_write_statsfile(void);
184 : : static void pgstat_read_statsfile(void);
185 : :
186 : : static void pgstat_init_snapshot_fixed(void);
187 : :
188 : : static void pgstat_reset_after_failure(void);
189 : :
190 : : static bool pgstat_flush_pending_entries(bool nowait);
191 : :
192 : : static void pgstat_prep_snapshot(void);
193 : : static void pgstat_build_snapshot(void);
194 : : static void pgstat_build_snapshot_fixed(PgStat_Kind kind);
195 : :
196 : : static inline bool pgstat_is_kind_valid(PgStat_Kind kind);
197 : :
198 : :
199 : : /* ----------
200 : : * GUC parameters
201 : : * ----------
202 : : */
203 : :
204 : : bool pgstat_track_counts = false;
205 : : int pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_CACHE;
206 : :
207 : :
208 : : /* ----------
209 : : * state shared with pgstat_*.c
210 : : * ----------
211 : : */
212 : :
213 : : PgStat_LocalState pgStatLocal;
214 : :
215 : : /*
216 : : * Track pending reports for fixed-numbered stats, used by
217 : : * pgstat_report_stat().
218 : : */
219 : : bool pgstat_report_fixed = false;
220 : :
221 : : /* ----------
222 : : * Local data
223 : : *
224 : : * NB: There should be only variables related to stats infrastructure here,
225 : : * not for specific kinds of stats.
226 : : * ----------
227 : : */
228 : :
229 : : /*
230 : : * Memory contexts containing the pgStatEntryRefHash table, the
231 : : * pgStatSharedRef entries, and pending data respectively. Mostly to make it
232 : : * easier to track / attribute memory usage.
233 : : */
234 : :
235 : : static MemoryContext pgStatPendingContext = NULL;
236 : :
237 : : /*
238 : : * Backend local list of PgStat_EntryRef with unflushed pending stats.
239 : : *
240 : : * Newly pending entries should only ever be added to the end of the list,
241 : : * otherwise pgstat_flush_pending_entries() might not see them immediately.
242 : : */
243 : : static dlist_head pgStatPending = DLIST_STATIC_INIT(pgStatPending);
244 : :
245 : :
246 : : /*
247 : : * Force the next stats flush to happen regardless of
248 : : * PGSTAT_MIN_INTERVAL. Useful in test scripts.
249 : : */
250 : : static bool pgStatForceNextFlush = false;
251 : :
252 : : /*
253 : : * Force-clear existing snapshot before next use when stats_fetch_consistency
254 : : * is changed.
255 : : */
256 : : static bool force_stats_snapshot_clear = false;
257 : :
258 : :
259 : : /*
260 : : * For assertions that check pgstat is not used before initialization / after
261 : : * shutdown.
262 : : */
263 : : #ifdef USE_ASSERT_CHECKING
264 : : static bool pgstat_is_initialized = false;
265 : : static bool pgstat_is_shutdown = false;
266 : : #endif
267 : :
268 : :
269 : : /*
270 : : * The different kinds of built-in statistics.
271 : : *
272 : : * If reasonably possible, handling specific to one kind of stats should go
273 : : * through this abstraction, rather than making more of pgstat.c aware.
274 : : *
275 : : * See comments for struct PgStat_KindInfo for details about the individual
276 : : * fields.
277 : : *
278 : : * XXX: It'd be nicer to define this outside of this file. But there doesn't
279 : : * seem to be a great way of doing that, given the split across multiple
280 : : * files.
281 : : */
282 : : static const PgStat_KindInfo pgstat_kind_builtin_infos[PGSTAT_KIND_BUILTIN_SIZE] = {
283 : :
284 : : /* stats kinds for variable-numbered objects */
285 : :
286 : : [PGSTAT_KIND_DATABASE] = {
287 : : .name = "database",
288 : :
289 : : .fixed_amount = false,
290 : : .write_to_file = true,
291 : : /* so pg_stat_database entries can be seen in all databases */
292 : : .accessed_across_databases = true,
293 : :
294 : : .shared_size = sizeof(PgStatShared_Database),
295 : : .shared_data_off = offsetof(PgStatShared_Database, stats),
296 : : .shared_data_len = sizeof(((PgStatShared_Database *) 0)->stats),
297 : : .pending_size = sizeof(PgStat_StatDBEntry),
298 : :
299 : : .flush_pending_cb = pgstat_database_flush_cb,
300 : : .reset_timestamp_cb = pgstat_database_reset_timestamp_cb,
301 : : },
302 : :
303 : : [PGSTAT_KIND_RELATION] = {
304 : : .name = "relation",
305 : :
306 : : .fixed_amount = false,
307 : : .write_to_file = true,
308 : :
309 : : .shared_size = sizeof(PgStatShared_Relation),
310 : : .shared_data_off = offsetof(PgStatShared_Relation, stats),
311 : : .shared_data_len = sizeof(((PgStatShared_Relation *) 0)->stats),
312 : : .pending_size = sizeof(PgStat_RelationStatus),
313 : :
314 : : .flush_pending_cb = pgstat_relation_flush_cb,
315 : : .delete_pending_cb = pgstat_relation_delete_pending_cb,
316 : : .reset_timestamp_cb = pgstat_relation_reset_timestamp_cb,
317 : : },
318 : :
319 : : [PGSTAT_KIND_INDEX] = {
320 : : .name = "index",
321 : :
322 : : .fixed_amount = false,
323 : : .write_to_file = true,
324 : :
325 : : .shared_size = sizeof(PgStatShared_Index),
326 : : .shared_data_off = offsetof(PgStatShared_Index, stats),
327 : : .shared_data_len = sizeof(((PgStatShared_Index *) 0)->stats),
328 : : .pending_size = sizeof(PgStat_RelationStatus),
329 : :
330 : : .flush_pending_cb = pgstat_index_flush_cb,
331 : : .delete_pending_cb = pgstat_index_delete_pending_cb,
332 : : .reset_timestamp_cb = pgstat_index_reset_timestamp_cb,
333 : : },
334 : :
335 : : [PGSTAT_KIND_FUNCTION] = {
336 : : .name = "function",
337 : :
338 : : .fixed_amount = false,
339 : : .write_to_file = true,
340 : :
341 : : .shared_size = sizeof(PgStatShared_Function),
342 : : .shared_data_off = offsetof(PgStatShared_Function, stats),
343 : : .shared_data_len = sizeof(((PgStatShared_Function *) 0)->stats),
344 : : .pending_size = sizeof(PgStat_FunctionCounts),
345 : :
346 : : .flush_pending_cb = pgstat_function_flush_cb,
347 : : .reset_timestamp_cb = pgstat_function_reset_timestamp_cb,
348 : : },
349 : :
350 : : [PGSTAT_KIND_REPLSLOT] = {
351 : : .name = "replslot",
352 : :
353 : : .fixed_amount = false,
354 : : .write_to_file = true,
355 : :
356 : : .accessed_across_databases = true,
357 : :
358 : : .shared_size = sizeof(PgStatShared_ReplSlot),
359 : : .shared_data_off = offsetof(PgStatShared_ReplSlot, stats),
360 : : .shared_data_len = sizeof(((PgStatShared_ReplSlot *) 0)->stats),
361 : :
362 : : .reset_timestamp_cb = pgstat_replslot_reset_timestamp_cb,
363 : : .to_serialized_name = pgstat_replslot_to_serialized_name_cb,
364 : : .from_serialized_name = pgstat_replslot_from_serialized_name_cb,
365 : : },
366 : :
367 : : [PGSTAT_KIND_SUBSCRIPTION] = {
368 : : .name = "subscription",
369 : :
370 : : .fixed_amount = false,
371 : : .write_to_file = true,
372 : : /* so pg_stat_subscription_stats entries can be seen in all databases */
373 : : .accessed_across_databases = true,
374 : :
375 : : .shared_size = sizeof(PgStatShared_Subscription),
376 : : .shared_data_off = offsetof(PgStatShared_Subscription, stats),
377 : : .shared_data_len = sizeof(((PgStatShared_Subscription *) 0)->stats),
378 : : .pending_size = sizeof(PgStat_BackendSubEntry),
379 : :
380 : : .flush_pending_cb = pgstat_subscription_flush_cb,
381 : : .reset_timestamp_cb = pgstat_subscription_reset_timestamp_cb,
382 : : },
383 : :
384 : : [PGSTAT_KIND_BACKEND] = {
385 : : .name = "backend",
386 : :
387 : : .fixed_amount = false,
388 : : .write_to_file = false,
389 : :
390 : : .accessed_across_databases = true,
391 : :
392 : : .shared_size = sizeof(PgStatShared_Backend),
393 : : .shared_data_off = offsetof(PgStatShared_Backend, stats),
394 : : .shared_data_len = sizeof(((PgStatShared_Backend *) 0)->stats),
395 : :
396 : : .flush_static_cb = pgstat_backend_flush_cb,
397 : : .reset_timestamp_cb = pgstat_backend_reset_timestamp_cb,
398 : : },
399 : :
400 : : /* stats for fixed-numbered (mostly 1) objects */
401 : :
402 : : [PGSTAT_KIND_ARCHIVER] = {
403 : : .name = "archiver",
404 : :
405 : : .fixed_amount = true,
406 : : .write_to_file = true,
407 : :
408 : : .snapshot_ctl_off = offsetof(PgStat_Snapshot, archiver),
409 : : .shared_ctl_off = offsetof(PgStat_ShmemControl, archiver),
410 : : .shared_data_off = offsetof(PgStatShared_Archiver, stats),
411 : : .shared_data_len = sizeof(((PgStatShared_Archiver *) 0)->stats),
412 : :
413 : : .init_shmem_cb = pgstat_archiver_init_shmem_cb,
414 : : .reset_all_cb = pgstat_archiver_reset_all_cb,
415 : : .snapshot_cb = pgstat_archiver_snapshot_cb,
416 : : },
417 : :
418 : : [PGSTAT_KIND_BGWRITER] = {
419 : : .name = "bgwriter",
420 : :
421 : : .fixed_amount = true,
422 : : .write_to_file = true,
423 : :
424 : : .snapshot_ctl_off = offsetof(PgStat_Snapshot, bgwriter),
425 : : .shared_ctl_off = offsetof(PgStat_ShmemControl, bgwriter),
426 : : .shared_data_off = offsetof(PgStatShared_BgWriter, stats),
427 : : .shared_data_len = sizeof(((PgStatShared_BgWriter *) 0)->stats),
428 : :
429 : : .init_shmem_cb = pgstat_bgwriter_init_shmem_cb,
430 : : .reset_all_cb = pgstat_bgwriter_reset_all_cb,
431 : : .snapshot_cb = pgstat_bgwriter_snapshot_cb,
432 : : },
433 : :
434 : : [PGSTAT_KIND_CHECKPOINTER] = {
435 : : .name = "checkpointer",
436 : :
437 : : .fixed_amount = true,
438 : : .write_to_file = true,
439 : :
440 : : .snapshot_ctl_off = offsetof(PgStat_Snapshot, checkpointer),
441 : : .shared_ctl_off = offsetof(PgStat_ShmemControl, checkpointer),
442 : : .shared_data_off = offsetof(PgStatShared_Checkpointer, stats),
443 : : .shared_data_len = sizeof(((PgStatShared_Checkpointer *) 0)->stats),
444 : :
445 : : .init_shmem_cb = pgstat_checkpointer_init_shmem_cb,
446 : : .reset_all_cb = pgstat_checkpointer_reset_all_cb,
447 : : .snapshot_cb = pgstat_checkpointer_snapshot_cb,
448 : : },
449 : :
450 : : [PGSTAT_KIND_IO] = {
451 : : .name = "io",
452 : :
453 : : .fixed_amount = true,
454 : : .write_to_file = true,
455 : :
456 : : .snapshot_ctl_off = offsetof(PgStat_Snapshot, io),
457 : : .shared_ctl_off = offsetof(PgStat_ShmemControl, io),
458 : : .shared_data_off = offsetof(PgStatShared_IO, stats),
459 : : .shared_data_len = sizeof(((PgStatShared_IO *) 0)->stats),
460 : :
461 : : .flush_static_cb = pgstat_io_flush_cb,
462 : : .init_shmem_cb = pgstat_io_init_shmem_cb,
463 : : .reset_all_cb = pgstat_io_reset_all_cb,
464 : : .snapshot_cb = pgstat_io_snapshot_cb,
465 : : },
466 : :
467 : : [PGSTAT_KIND_LOCK] = {
468 : : .name = "lock",
469 : :
470 : : .fixed_amount = true,
471 : : .write_to_file = true,
472 : :
473 : : .snapshot_ctl_off = offsetof(PgStat_Snapshot, lock),
474 : : .shared_ctl_off = offsetof(PgStat_ShmemControl, lock),
475 : : .shared_data_off = offsetof(PgStatShared_Lock, stats),
476 : : .shared_data_len = sizeof(((PgStatShared_Lock *) 0)->stats),
477 : :
478 : : .flush_static_cb = pgstat_lock_flush_cb,
479 : : .init_shmem_cb = pgstat_lock_init_shmem_cb,
480 : : .reset_all_cb = pgstat_lock_reset_all_cb,
481 : : .snapshot_cb = pgstat_lock_snapshot_cb,
482 : : },
483 : :
484 : : [PGSTAT_KIND_SLRU] = {
485 : : .name = "slru",
486 : :
487 : : .fixed_amount = true,
488 : : .write_to_file = true,
489 : :
490 : : .snapshot_ctl_off = offsetof(PgStat_Snapshot, slru),
491 : : .shared_ctl_off = offsetof(PgStat_ShmemControl, slru),
492 : : .shared_data_off = offsetof(PgStatShared_SLRU, stats),
493 : : .shared_data_len = sizeof(((PgStatShared_SLRU *) 0)->stats),
494 : :
495 : : .flush_static_cb = pgstat_slru_flush_cb,
496 : : .init_shmem_cb = pgstat_slru_init_shmem_cb,
497 : : .reset_all_cb = pgstat_slru_reset_all_cb,
498 : : .snapshot_cb = pgstat_slru_snapshot_cb,
499 : : },
500 : :
501 : : [PGSTAT_KIND_WAL] = {
502 : : .name = "wal",
503 : :
504 : : .fixed_amount = true,
505 : : .write_to_file = true,
506 : :
507 : : .snapshot_ctl_off = offsetof(PgStat_Snapshot, wal),
508 : : .shared_ctl_off = offsetof(PgStat_ShmemControl, wal),
509 : : .shared_data_off = offsetof(PgStatShared_Wal, stats),
510 : : .shared_data_len = sizeof(((PgStatShared_Wal *) 0)->stats),
511 : :
512 : : .init_backend_cb = pgstat_wal_init_backend_cb,
513 : : .flush_static_cb = pgstat_wal_flush_cb,
514 : : .init_shmem_cb = pgstat_wal_init_shmem_cb,
515 : : .reset_all_cb = pgstat_wal_reset_all_cb,
516 : : .snapshot_cb = pgstat_wal_snapshot_cb,
517 : : },
518 : : };
519 : :
520 : : /*
521 : : * Information about custom statistics kinds.
522 : : *
523 : : * These are saved in a different array than the built-in kinds to save
524 : : * in clarity with the initializations.
525 : : *
526 : : * Indexed by PGSTAT_KIND_CUSTOM_MIN, of size PGSTAT_KIND_CUSTOM_SIZE.
527 : : */
528 : : static const PgStat_KindInfo **pgstat_kind_custom_infos = NULL;
529 : :
530 : : /* ------------------------------------------------------------
531 : : * Functions managing the state of the stats system for all backends.
532 : : * ------------------------------------------------------------
533 : : */
534 : :
535 : : /*
536 : : * Read on-disk stats into memory at server start.
537 : : *
538 : : * Should only be called by the startup process or in single user mode.
539 : : */
540 : : void
541 : 941 : pgstat_restore_stats(void)
542 : : {
543 : 941 : pgstat_read_statsfile();
544 : 941 : }
545 : :
546 : : /*
547 : : * Remove the stats file. This is currently used only if WAL recovery is
548 : : * needed after a crash.
549 : : *
550 : : * Should only be called by the startup process or in single user mode.
551 : : */
552 : : void
553 : 209 : pgstat_discard_stats(void)
554 : : {
555 : : int ret;
556 : :
557 : : /* NB: this needs to be done even in single user mode */
558 : :
559 : : /* First, cleanup the main pgstats file */
560 : 209 : ret = unlink(PGSTAT_STAT_PERMANENT_FILENAME);
561 [ + + ]: 209 : if (ret != 0)
562 : : {
563 [ + - ]: 208 : if (errno == ENOENT)
564 [ + + ]: 208 : elog(DEBUG2,
565 : : "didn't need to unlink permanent stats file \"%s\" - didn't exist",
566 : : PGSTAT_STAT_PERMANENT_FILENAME);
567 : : else
568 [ # # ]: 0 : ereport(LOG,
569 : : (errcode_for_file_access(),
570 : : errmsg("could not unlink permanent statistics file \"%s\": %m",
571 : : PGSTAT_STAT_PERMANENT_FILENAME)));
572 : : }
573 : : else
574 : : {
575 [ - + ]: 1 : ereport(DEBUG2,
576 : : (errcode_for_file_access(),
577 : : errmsg_internal("unlinked permanent statistics file \"%s\"",
578 : : PGSTAT_STAT_PERMANENT_FILENAME)));
579 : : }
580 : :
581 : : /* Finish callbacks, if required */
582 [ + + ]: 6897 : for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
583 : : {
584 : 6688 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
585 : :
586 [ + + + + ]: 6688 : if (kind_info && kind_info->finish)
587 : 3 : kind_info->finish(STATS_DISCARD);
588 : : }
589 : :
590 : : /*
591 : : * Reset stats contents. This will set reset timestamps of fixed-numbered
592 : : * stats to the current time (no variable stats exist).
593 : : */
594 : 209 : pgstat_reset_after_failure();
595 : 209 : }
596 : :
597 : : /*
598 : : * pgstat_before_server_shutdown() needs to be called by exactly one process
599 : : * during regular server shutdowns. Otherwise all stats will be lost.
600 : : *
601 : : * We currently only write out stats for proc_exit(0). We might want to change
602 : : * that at some point... But right now pgstat_discard_stats() would be called
603 : : * during the start after a disorderly shutdown, anyway.
604 : : */
605 : : void
606 : 823 : pgstat_before_server_shutdown(int code, Datum arg)
607 : : {
608 : : Assert(pgStatLocal.shmem != NULL);
609 : : Assert(!pgStatLocal.shmem->is_shutdown);
610 : :
611 : : /*
612 : : * Stats should only be reported after pgstat_initialize() and before
613 : : * pgstat_shutdown(). This is a convenient point to catch most violations
614 : : * of this rule.
615 : : */
616 : : Assert(pgstat_is_initialized && !pgstat_is_shutdown);
617 : :
618 : : /* flush out our own pending changes before writing out */
619 : 823 : pgstat_report_stat(true);
620 : :
621 : : /*
622 : : * Only write out file during normal shutdown. Don't even signal that
623 : : * we've shutdown during irregular shutdowns, because the shutdown
624 : : * sequence isn't coordinated to ensure this backend shuts down last.
625 : : */
626 [ + + ]: 823 : if (code == 0)
627 : : {
628 : 818 : pgStatLocal.shmem->is_shutdown = true;
629 : 818 : pgstat_write_statsfile();
630 : : }
631 : 823 : }
632 : :
633 : :
634 : : /* ------------------------------------------------------------
635 : : * Backend initialization / shutdown functions
636 : : * ------------------------------------------------------------
637 : : */
638 : :
639 : : /*
640 : : * Shut down a single backend's statistics reporting at process exit.
641 : : *
642 : : * Flush out any remaining statistics counts. Without this, operations
643 : : * triggered during backend exit (such as temp table deletions) won't be
644 : : * counted.
645 : : */
646 : : static void
647 : 26118 : pgstat_shutdown_hook(int code, Datum arg)
648 : : {
649 : : Assert(!pgstat_is_shutdown);
650 : : Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
651 : :
652 : : /*
653 : : * If we got as far as discovering our own database ID, we can flush out
654 : : * what we did so far. Otherwise, we'd be reporting an invalid database
655 : : * ID, so forget it. (This means that accesses to pg_database during
656 : : * failed backend starts might never get counted.)
657 : : */
658 [ + + ]: 26118 : if (OidIsValid(MyDatabaseId))
659 : 19666 : pgstat_report_disconnect(MyDatabaseId);
660 : :
661 : 26118 : pgstat_report_stat(true);
662 : :
663 : : /* there shouldn't be any pending changes left */
664 : : Assert(dlist_is_empty(&pgStatPending));
665 : 26118 : dlist_init(&pgStatPending);
666 : :
667 : : /* drop the backend stats entry */
668 [ - + ]: 26118 : if (!pgstat_drop_entry(PGSTAT_KIND_BACKEND, InvalidOid, MyProcNumber, false))
669 : 0 : pgstat_request_entry_refs_gc();
670 : :
671 : 26118 : pgstat_detach_shmem();
672 : :
673 : : #ifdef USE_ASSERT_CHECKING
674 : : pgstat_is_shutdown = true;
675 : : #endif
676 : 26118 : }
677 : :
678 : : /*
679 : : * Initialize pgstats state, and set up our on-proc-exit hook. Called from
680 : : * BaseInit().
681 : : *
682 : : * NOTE: MyDatabaseId isn't set yet; so the shutdown hook has to be careful.
683 : : */
684 : : void
685 : 26118 : pgstat_initialize(void)
686 : : {
687 : : Assert(!pgstat_is_initialized);
688 : :
689 : 26118 : pgstat_attach_shmem();
690 : :
691 : 26118 : pgstat_init_snapshot_fixed();
692 : :
693 : : /* Backend initialization callbacks */
694 [ + + ]: 861894 : for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
695 : : {
696 : 835776 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
697 : :
698 [ + + + + ]: 835776 : if (kind_info == NULL || kind_info->init_backend_cb == NULL)
699 : 809658 : continue;
700 : :
701 : 26118 : kind_info->init_backend_cb();
702 : : }
703 : :
704 : : /* Set up a process-exit hook to clean up */
705 : 26118 : before_shmem_exit(pgstat_shutdown_hook, 0);
706 : :
707 : : #ifdef USE_ASSERT_CHECKING
708 : : pgstat_is_initialized = true;
709 : : #endif
710 : 26118 : }
711 : :
712 : :
713 : : /* ------------------------------------------------------------
714 : : * Public functions used by backends follow
715 : : * ------------------------------------------------------------
716 : : */
717 : :
718 : : /*
719 : : * Must be called by processes that performs DML: tcop/postgres.c, logical
720 : : * receiver processes, SPI worker, etc. to flush pending statistics updates to
721 : : * shared memory.
722 : : *
723 : : * Unless called with 'force', pending stats updates are flushed happen once
724 : : * per PGSTAT_MIN_INTERVAL (1000ms). When not forced, stats flushes do not
725 : : * block on lock acquisition, except if stats updates have been pending for
726 : : * longer than PGSTAT_MAX_INTERVAL (60000ms).
727 : : *
728 : : * Whenever pending stats updates remain at the end of pgstat_report_stat() a
729 : : * suggested idle timeout is returned. Currently this is always
730 : : * PGSTAT_IDLE_INTERVAL (10000ms). Callers can use the returned time to set up
731 : : * a timeout after which to call pgstat_report_stat(true), but are not
732 : : * required to do so.
733 : : *
734 : : * Note that this is called only when not within a transaction, so it is fair
735 : : * to use transaction stop time as an approximation of current time.
736 : : */
737 : : long
738 : 395449 : pgstat_report_stat(bool force)
739 : : {
740 : : static TimestampTz pending_since = 0;
741 : : static TimestampTz last_flush = 0;
742 : : bool partial_flush;
743 : : TimestampTz now;
744 : : bool nowait;
745 : :
746 : : pgstat_assert_is_up();
747 : : Assert(!IsTransactionOrTransactionBlock());
748 : :
749 : : /* "absorb" the forced flush even if there's nothing to flush */
750 [ + + ]: 395449 : if (pgStatForceNextFlush)
751 : : {
752 : 352 : force = true;
753 : 352 : pgStatForceNextFlush = false;
754 : : }
755 : :
756 : : /* Don't expend a clock check if nothing to do */
757 [ + + ]: 395449 : if (dlist_is_empty(&pgStatPending) &&
758 [ + + ]: 11440 : !pgstat_report_fixed)
759 : : {
760 : 8178 : return 0;
761 : : }
762 : :
763 : : /*
764 : : * There should never be stats to report once stats are shut down. Can't
765 : : * assert that before the checks above, as there is an unconditional
766 : : * pgstat_report_stat() call in pgstat_shutdown_hook() - which at least
767 : : * the process that ran pgstat_before_server_shutdown() will still call.
768 : : */
769 : : Assert(!pgStatLocal.shmem->is_shutdown);
770 : :
771 [ + + ]: 387271 : if (force)
772 : : {
773 : : /*
774 : : * Stats reports are forced either when it's been too long since stats
775 : : * have been reported or in processes that force stats reporting to
776 : : * happen at specific points (including shutdown). In the former case
777 : : * the transaction stop time might be quite old, in the latter it
778 : : * would never get cleared.
779 : : */
780 : 25932 : now = GetCurrentTimestamp();
781 : : }
782 : : else
783 : : {
784 : 361339 : now = GetCurrentTransactionStopTimestamp();
785 : :
786 [ + + - + ]: 689003 : if (pending_since > 0 &&
787 : 327664 : TimestampDifferenceExceeds(pending_since, now, PGSTAT_MAX_INTERVAL))
788 : : {
789 : : /* don't keep pending updates longer than PGSTAT_MAX_INTERVAL */
790 : 0 : force = true;
791 : : }
792 [ + + ]: 361339 : else if (last_flush > 0 &&
793 [ + + ]: 345502 : !TimestampDifferenceExceeds(last_flush, now, PGSTAT_MIN_INTERVAL))
794 : : {
795 : : /* don't flush too frequently */
796 [ + + ]: 343441 : if (pending_since == 0)
797 : 17618 : pending_since = now;
798 : :
799 : 343441 : return PGSTAT_IDLE_INTERVAL;
800 : : }
801 : : }
802 : :
803 : 43830 : pgstat_update_dbstats(now);
804 : :
805 : : /* don't wait for lock acquisition when !force */
806 : 43830 : nowait = !force;
807 : :
808 : 43830 : partial_flush = false;
809 : :
810 : : /* flush of variable-numbered stats tracked in pending entries list */
811 : 43830 : partial_flush |= pgstat_flush_pending_entries(nowait);
812 : :
813 : : /* flush of other stats kinds */
814 [ + + ]: 43830 : if (pgstat_report_fixed)
815 : : {
816 [ + + ]: 1403820 : for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
817 : : {
818 : 1361280 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
819 : :
820 [ + + ]: 1361280 : if (!kind_info)
821 : 765454 : continue;
822 [ + + ]: 595826 : if (!kind_info->flush_static_cb)
823 : 383126 : continue;
824 : :
825 : 212700 : partial_flush |= kind_info->flush_static_cb(nowait);
826 : : }
827 : : }
828 : :
829 : 43830 : last_flush = now;
830 : :
831 : : /*
832 : : * If some of the pending stats could not be flushed due to lock
833 : : * contention, let the caller know when to retry.
834 : : */
835 [ + + ]: 43830 : if (partial_flush)
836 : : {
837 : : /* force should have prevented us from getting here */
838 : : Assert(!force);
839 : :
840 : : /* remember since when stats have been pending */
841 [ + + ]: 10 : if (pending_since == 0)
842 : 9 : pending_since = now;
843 : :
844 : 10 : return PGSTAT_IDLE_INTERVAL;
845 : : }
846 : :
847 : 43820 : pending_since = 0;
848 : 43820 : pgstat_report_fixed = false;
849 : :
850 : 43820 : return 0;
851 : : }
852 : :
853 : : /*
854 : : * Force locally pending stats to be flushed during the next
855 : : * pgstat_report_stat() call. This is useful for writing tests.
856 : : */
857 : : void
858 : 352 : pgstat_force_next_flush(void)
859 : : {
860 : 352 : pgStatForceNextFlush = true;
861 : 352 : }
862 : :
863 : : /*
864 : : * Only for use by pgstat_reset_counters()
865 : : */
866 : : static bool
867 : 15251 : match_db_entries(PgStatShared_HashEntry *entry, Datum match_data)
868 : : {
869 : 15251 : return entry->key.dboid == MyDatabaseId;
870 : : }
871 : :
872 : : /*
873 : : * Reset counters for our database.
874 : : *
875 : : * Permission checking for this function is managed through the normal
876 : : * GRANT system.
877 : : */
878 : : void
879 : 15 : pgstat_reset_counters(void)
880 : : {
881 : 15 : TimestampTz ts = GetCurrentTimestamp();
882 : :
883 : 15 : pgstat_reset_matching_entries(match_db_entries,
884 : : ObjectIdGetDatum(MyDatabaseId),
885 : : ts);
886 : 15 : }
887 : :
888 : : /*
889 : : * Reset a single variable-numbered entry.
890 : : *
891 : : * If the stats kind is within a database, also reset the database's
892 : : * stat_reset_timestamp.
893 : : *
894 : : * Permission checking for this function is managed through the normal
895 : : * GRANT system.
896 : : */
897 : : void
898 : 45 : pgstat_reset(PgStat_Kind kind, Oid dboid, uint64 objid)
899 : : {
900 : 45 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
901 : 45 : TimestampTz ts = GetCurrentTimestamp();
902 : :
903 : : /* not needed atm, and doesn't make sense with the current signature */
904 : : Assert(!pgstat_get_kind_info(kind)->fixed_amount);
905 : :
906 : : /* reset the "single counter" */
907 : 45 : pgstat_reset_entry(kind, dboid, objid, ts);
908 : :
909 [ + + ]: 45 : if (!kind_info->accessed_across_databases)
910 : 28 : pgstat_reset_database_timestamp(dboid, ts);
911 : 45 : }
912 : :
913 : : /*
914 : : * Reset stats for all entries of a kind.
915 : : *
916 : : * Permission checking for this function is managed through the normal
917 : : * GRANT system.
918 : : */
919 : : void
920 : 48 : pgstat_reset_of_kind(PgStat_Kind kind)
921 : : {
922 : 48 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
923 : 48 : TimestampTz ts = GetCurrentTimestamp();
924 : :
925 [ + + ]: 48 : if (kind_info->fixed_amount)
926 : 42 : kind_info->reset_all_cb(ts);
927 : : else
928 : 6 : pgstat_reset_entries_of_kind(kind, ts);
929 : 48 : }
930 : :
931 : :
932 : : /* ------------------------------------------------------------
933 : : * Fetching of stats
934 : : * ------------------------------------------------------------
935 : : */
936 : :
937 : : /*
938 : : * Discard any data collected in the current transaction. Any subsequent
939 : : * request will cause new snapshots to be read.
940 : : *
941 : : * This is also invoked during transaction commit or abort to discard
942 : : * the no-longer-wanted snapshot. Updates of stats_fetch_consistency can
943 : : * cause this routine to be called.
944 : : */
945 : : void
946 : 646721 : pgstat_clear_snapshot(void)
947 : : {
948 : : pgstat_assert_is_up();
949 : :
950 : 646721 : memset(&pgStatLocal.snapshot.fixed_valid, 0,
951 : : sizeof(pgStatLocal.snapshot.fixed_valid));
952 : 646721 : memset(&pgStatLocal.snapshot.custom_valid, 0,
953 : : sizeof(pgStatLocal.snapshot.custom_valid));
954 : 646721 : pgStatLocal.snapshot.stats = NULL;
955 : 646721 : pgStatLocal.snapshot.mode = PGSTAT_FETCH_CONSISTENCY_NONE;
956 : :
957 : : /* Release memory, if any was allocated */
958 [ + + ]: 646721 : if (pgStatLocal.snapshot.context)
959 : : {
960 : 851 : MemoryContextDelete(pgStatLocal.snapshot.context);
961 : :
962 : : /* Reset variables */
963 : 851 : pgStatLocal.snapshot.context = NULL;
964 : : }
965 : :
966 : : /*
967 : : * Historically the backend_status.c facilities lived in this file, and
968 : : * were reset with the same function. For now keep it that way, and
969 : : * forward the reset request.
970 : : */
971 : 646721 : pgstat_clear_backend_activity_snapshot();
972 : :
973 : : /* Reset this flag, as it may be possible that a cleanup was forced. */
974 : 646721 : force_stats_snapshot_clear = false;
975 : 646721 : }
976 : :
977 : : void *
978 : 290367 : pgstat_fetch_entry(PgStat_Kind kind, Oid dboid, uint64 objid, bool *may_free)
979 : : {
980 : 290367 : PgStat_HashKey key = {0};
981 : : PgStat_EntryRef *entry_ref;
982 : : void *stats_data;
983 : 290367 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
984 : :
985 : : /* should be called from backends */
986 : : Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
987 : : Assert(!kind_info->fixed_amount);
988 : :
989 : : /*
990 : : * Initialize *may_free to false. We'll change it to true later if we end
991 : : * up allocating the result in the caller's context and not caching it.
992 : : */
993 [ + + ]: 290367 : if (may_free)
994 : 277789 : *may_free = false;
995 : :
996 : 290367 : pgstat_prep_snapshot();
997 : :
998 : 290367 : key.kind = kind;
999 : 290367 : key.dboid = dboid;
1000 : 290367 : key.objid = objid;
1001 : :
1002 : : /* if we need to build a full snapshot, do so */
1003 [ + + ]: 290367 : if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
1004 : 323 : pgstat_build_snapshot();
1005 : :
1006 : : /* if caching is desired, look up in cache */
1007 [ + + ]: 290367 : if (pgstat_fetch_consistency > PGSTAT_FETCH_CONSISTENCY_NONE)
1008 : : {
1009 : 6877 : PgStat_SnapshotEntry *entry = NULL;
1010 : :
1011 : 6877 : entry = pgstat_snapshot_lookup(pgStatLocal.snapshot.stats, key);
1012 : :
1013 [ + + ]: 6877 : if (entry)
1014 : 648 : return entry->data;
1015 : :
1016 : : /*
1017 : : * If we built a full snapshot and the key is not in
1018 : : * pgStatLocal.snapshot.stats, there are no matching stats.
1019 : : */
1020 [ + + ]: 6229 : if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
1021 : 16 : return NULL;
1022 : : }
1023 : :
1024 : 289703 : pgStatLocal.snapshot.mode = pgstat_fetch_consistency;
1025 : :
1026 : 289703 : entry_ref = pgstat_get_entry_ref(kind, dboid, objid, false, NULL);
1027 : :
1028 [ + + + + ]: 289703 : if (entry_ref == NULL || entry_ref->shared_entry->dropped)
1029 : : {
1030 : : /* create empty entry when using PGSTAT_FETCH_CONSISTENCY_CACHE */
1031 [ + + ]: 7354 : if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_CACHE)
1032 : : {
1033 : 1238 : PgStat_SnapshotEntry *entry = NULL;
1034 : : bool found;
1035 : :
1036 : 1238 : entry = pgstat_snapshot_insert(pgStatLocal.snapshot.stats, key, &found);
1037 : : Assert(!found);
1038 : 1238 : entry->data = NULL;
1039 : : }
1040 : 7354 : return NULL;
1041 : : }
1042 : :
1043 : : /*
1044 : : * Allocate in caller's context for PGSTAT_FETCH_CONSISTENCY_NONE,
1045 : : * otherwise we could quickly end up with a fair bit of memory used due to
1046 : : * repeated accesses.
1047 : : */
1048 [ + + ]: 282349 : if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE)
1049 : : {
1050 : 277374 : stats_data = palloc(kind_info->shared_data_len);
1051 : :
1052 : : /*
1053 : : * Since we allocated the result in the caller's context and aren't
1054 : : * caching it, the caller can safely pfree() it.
1055 : : */
1056 [ + + ]: 277374 : if (may_free)
1057 : 272962 : *may_free = true;
1058 : : }
1059 : : else
1060 : 4975 : stats_data = MemoryContextAlloc(pgStatLocal.snapshot.context,
1061 : 4975 : kind_info->shared_data_len);
1062 : :
1063 : 282349 : (void) pgstat_lock_entry_shared(entry_ref, false);
1064 : 564698 : memcpy(stats_data,
1065 : 282349 : pgstat_get_entry_data(kind, entry_ref->shared_stats),
1066 : 282349 : kind_info->shared_data_len);
1067 : 282349 : pgstat_unlock_entry(entry_ref);
1068 : :
1069 [ + + ]: 282349 : if (pgstat_fetch_consistency > PGSTAT_FETCH_CONSISTENCY_NONE)
1070 : : {
1071 : 4975 : PgStat_SnapshotEntry *entry = NULL;
1072 : : bool found;
1073 : :
1074 : 4975 : entry = pgstat_snapshot_insert(pgStatLocal.snapshot.stats, key, &found);
1075 : 4975 : entry->data = stats_data;
1076 : : }
1077 : :
1078 : 282349 : return stats_data;
1079 : : }
1080 : :
1081 : : /*
1082 : : * If a stats snapshot has been taken, return the timestamp at which that was
1083 : : * done, and set *have_snapshot to true. Otherwise *have_snapshot is set to
1084 : : * false.
1085 : : */
1086 : : TimestampTz
1087 : 40 : pgstat_get_stat_snapshot_timestamp(bool *have_snapshot)
1088 : : {
1089 [ + + ]: 40 : if (force_stats_snapshot_clear)
1090 : 12 : pgstat_clear_snapshot();
1091 : :
1092 [ + + ]: 40 : if (pgStatLocal.snapshot.mode == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
1093 : : {
1094 : 16 : *have_snapshot = true;
1095 : 16 : return pgStatLocal.snapshot.snapshot_timestamp;
1096 : : }
1097 : :
1098 : 24 : *have_snapshot = false;
1099 : :
1100 : 24 : return 0;
1101 : : }
1102 : :
1103 : : bool
1104 : 95 : pgstat_have_entry(PgStat_Kind kind, Oid dboid, uint64 objid)
1105 : : {
1106 : : /* fixed-numbered stats always exist */
1107 [ + + ]: 95 : if (pgstat_get_kind_info(kind)->fixed_amount)
1108 : 8 : return true;
1109 : :
1110 : 87 : return pgstat_get_entry_ref(kind, dboid, objid, false, NULL) != NULL;
1111 : : }
1112 : :
1113 : : /*
1114 : : * Ensure snapshot for fixed-numbered 'kind' exists.
1115 : : *
1116 : : * Typically used by the pgstat_fetch_* functions for a kind of stats, before
1117 : : * massaging the data into the desired format.
1118 : : */
1119 : : void
1120 : 284 : pgstat_snapshot_fixed(PgStat_Kind kind)
1121 : : {
1122 : : Assert(pgstat_is_kind_valid(kind));
1123 : : Assert(pgstat_get_kind_info(kind)->fixed_amount);
1124 : :
1125 [ - + ]: 284 : if (force_stats_snapshot_clear)
1126 : 0 : pgstat_clear_snapshot();
1127 : :
1128 [ + + ]: 284 : if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
1129 : 12 : pgstat_build_snapshot();
1130 : : else
1131 : 272 : pgstat_build_snapshot_fixed(kind);
1132 : :
1133 [ + + ]: 284 : if (pgstat_is_kind_builtin(kind))
1134 : : Assert(pgStatLocal.snapshot.fixed_valid[kind]);
1135 : 5 : else if (pgstat_is_kind_custom(kind))
1136 : : Assert(pgStatLocal.snapshot.custom_valid[kind - PGSTAT_KIND_CUSTOM_MIN]);
1137 : 284 : }
1138 : :
1139 : : static void
1140 : 26118 : pgstat_init_snapshot_fixed(void)
1141 : : {
1142 : : /*
1143 : : * Initialize fixed-numbered statistics data in snapshots, only for custom
1144 : : * stats kinds.
1145 : : */
1146 [ + + ]: 261180 : for (PgStat_Kind kind = PGSTAT_KIND_CUSTOM_MIN; kind <= PGSTAT_KIND_CUSTOM_MAX; kind++)
1147 : : {
1148 : 235062 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
1149 : :
1150 [ + + + + ]: 235062 : if (!kind_info || !kind_info->fixed_amount)
1151 : 234962 : continue;
1152 : :
1153 : 100 : pgStatLocal.snapshot.custom_data[kind - PGSTAT_KIND_CUSTOM_MIN] =
1154 : 100 : MemoryContextAlloc(TopMemoryContext, kind_info->shared_data_len);
1155 : : }
1156 : 26118 : }
1157 : :
1158 : : static void
1159 : 290397 : pgstat_prep_snapshot(void)
1160 : : {
1161 [ + + ]: 290397 : if (force_stats_snapshot_clear)
1162 : 12 : pgstat_clear_snapshot();
1163 : :
1164 [ + + ]: 290397 : if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE ||
1165 [ + + ]: 6907 : pgStatLocal.snapshot.stats != NULL)
1166 : 289546 : return;
1167 : :
1168 [ + - ]: 851 : if (!pgStatLocal.snapshot.context)
1169 : 851 : pgStatLocal.snapshot.context = AllocSetContextCreate(TopMemoryContext,
1170 : : "PgStat Snapshot",
1171 : : ALLOCSET_SMALL_SIZES);
1172 : :
1173 : 851 : pgStatLocal.snapshot.stats =
1174 : 851 : pgstat_snapshot_create(pgStatLocal.snapshot.context,
1175 : : PGSTAT_SNAPSHOT_HASH_SIZE,
1176 : : NULL);
1177 : : }
1178 : :
1179 : : static void
1180 : 335 : pgstat_build_snapshot(void)
1181 : : {
1182 : : dshash_seq_status hstat;
1183 : : PgStatShared_HashEntry *p;
1184 : :
1185 : : /* should only be called when we need a snapshot */
1186 : : Assert(pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT);
1187 : :
1188 : : /* snapshot already built */
1189 [ + + ]: 335 : if (pgStatLocal.snapshot.mode == PGSTAT_FETCH_CONSISTENCY_SNAPSHOT)
1190 : 305 : return;
1191 : :
1192 : 30 : pgstat_prep_snapshot();
1193 : :
1194 : : Assert(pgStatLocal.snapshot.stats->members == 0);
1195 : :
1196 : 30 : pgStatLocal.snapshot.snapshot_timestamp = GetCurrentTimestamp();
1197 : :
1198 : : /*
1199 : : * Snapshot all variable stats.
1200 : : */
1201 [ + + ]: 60 : for (int h = 0; h < pgStatLocal.num_var_hashes; h++)
1202 : : {
1203 : 30 : dshash_table *hash = pgStatLocal.var_hashes[h];
1204 : :
1205 : 30 : dshash_seq_init(&hstat, hash, false);
1206 [ + + ]: 34123 : while ((p = dshash_seq_next(&hstat)) != NULL)
1207 : : {
1208 : 34093 : PgStat_Kind kind = p->key.kind;
1209 : 34093 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
1210 : : bool found;
1211 : : PgStat_SnapshotEntry *entry;
1212 : : PgStatShared_Common *stats_data;
1213 : :
1214 : : /*
1215 : : * Check if the stats object should be included in the snapshot.
1216 : : * Unless the stats kind can be accessed from all databases (e.g.,
1217 : : * database stats themselves), we only include stats for the
1218 : : * current database or objects not associated with a database
1219 : : * (e.g. shared relations).
1220 : : */
1221 [ + + ]: 34093 : if (p->key.dboid != MyDatabaseId &&
1222 [ + + ]: 9543 : p->key.dboid != InvalidOid &&
1223 [ + + ]: 7886 : !kind_info->accessed_across_databases)
1224 : 7898 : continue;
1225 : :
1226 [ + + ]: 26297 : if (p->dropped)
1227 : 102 : continue;
1228 : :
1229 : : Assert(pg_atomic_read_u32(&p->refcount) > 0);
1230 : :
1231 : 26195 : stats_data = dsa_get_address(pgStatLocal.kind_dsa[kind], p->body);
1232 : : Assert(stats_data);
1233 : :
1234 : 26195 : entry = pgstat_snapshot_insert(pgStatLocal.snapshot.stats, p->key, &found);
1235 : : Assert(!found);
1236 : :
1237 : 26195 : entry->data = MemoryContextAlloc(pgStatLocal.snapshot.context,
1238 : : pgstat_get_entry_len(kind));
1239 : :
1240 : : /*
1241 : : * Acquire the LWLock directly instead of using
1242 : : * pg_stat_lock_entry_shared() which requires a reference.
1243 : : */
1244 : 26195 : LWLockAcquire(&stats_data->lock, LW_SHARED);
1245 : 26195 : memcpy(entry->data,
1246 : 26195 : pgstat_get_entry_data(kind, stats_data),
1247 : : pgstat_get_entry_len(kind));
1248 : 26195 : LWLockRelease(&stats_data->lock);
1249 : : }
1250 : 30 : dshash_seq_term(&hstat);
1251 : : }
1252 : :
1253 : : /*
1254 : : * Build snapshot of all fixed-numbered stats.
1255 : : */
1256 [ + + ]: 990 : for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
1257 : : {
1258 : 960 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
1259 : :
1260 [ + + ]: 960 : if (!kind_info)
1261 : 540 : continue;
1262 [ + + ]: 420 : if (!kind_info->fixed_amount)
1263 : : {
1264 : : Assert(kind_info->snapshot_cb == NULL);
1265 : 210 : continue;
1266 : : }
1267 : :
1268 : 210 : pgstat_build_snapshot_fixed(kind);
1269 : : }
1270 : :
1271 : 30 : pgStatLocal.snapshot.mode = PGSTAT_FETCH_CONSISTENCY_SNAPSHOT;
1272 : : }
1273 : :
1274 : : static void
1275 : 6213 : pgstat_build_snapshot_fixed(PgStat_Kind kind)
1276 : : {
1277 : 6213 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
1278 : : int idx;
1279 : : bool *valid;
1280 : :
1281 : : /* Position in fixed_valid or custom_valid */
1282 [ + + ]: 6213 : if (pgstat_is_kind_builtin(kind))
1283 : : {
1284 : 6203 : idx = kind;
1285 : 6203 : valid = pgStatLocal.snapshot.fixed_valid;
1286 : : }
1287 : : else
1288 : : {
1289 : 10 : idx = kind - PGSTAT_KIND_CUSTOM_MIN;
1290 : 10 : valid = pgStatLocal.snapshot.custom_valid;
1291 : : }
1292 : :
1293 : : Assert(kind_info->fixed_amount);
1294 : : Assert(kind_info->snapshot_cb != NULL);
1295 : :
1296 [ + + ]: 6213 : if (pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_NONE)
1297 : : {
1298 : : /* rebuild every time */
1299 : 5746 : valid[idx] = false;
1300 : : }
1301 [ + + ]: 467 : else if (valid[idx])
1302 : : {
1303 : : /* in snapshot mode we shouldn't get called again */
1304 : : Assert(pgstat_fetch_consistency == PGSTAT_FETCH_CONSISTENCY_CACHE);
1305 : 6 : return;
1306 : : }
1307 : :
1308 : : Assert(!valid[idx]);
1309 : :
1310 : 6207 : kind_info->snapshot_cb();
1311 : :
1312 : : Assert(!valid[idx]);
1313 : 6207 : valid[idx] = true;
1314 : : }
1315 : :
1316 : :
1317 : : /* ------------------------------------------------------------
1318 : : * Backend-local pending stats infrastructure
1319 : : * ------------------------------------------------------------
1320 : : */
1321 : :
1322 : : /*
1323 : : * Returns the appropriate PgStat_EntryRef, preparing it to receive pending
1324 : : * stats if not already done.
1325 : : *
1326 : : * If created_entry is non-NULL, it'll be set to true if the entry is newly
1327 : : * created, false otherwise.
1328 : : */
1329 : : PgStat_EntryRef *
1330 : 2459208 : pgstat_prep_pending_entry(PgStat_Kind kind, Oid dboid, uint64 objid, bool *created_entry)
1331 : : {
1332 : : PgStat_EntryRef *entry_ref;
1333 : :
1334 : 2459208 : entry_ref = pgstat_get_entry_ref(kind, dboid, objid,
1335 : : true, created_entry);
1336 : :
1337 : 2459208 : pgstat_prep_pending_from_entry_ref(entry_ref);
1338 : :
1339 : 2459208 : return entry_ref;
1340 : : }
1341 : :
1342 : : /*
1343 : : * Return an existing stats entry, or NULL.
1344 : : *
1345 : : * This should only be used for helper function for pgstatfuncs.c - outside of
1346 : : * that it shouldn't be needed.
1347 : : */
1348 : : PgStat_EntryRef *
1349 : 57 : pgstat_fetch_pending_entry(PgStat_Kind kind, Oid dboid, uint64 objid)
1350 : : {
1351 : : PgStat_EntryRef *entry_ref;
1352 : :
1353 : 57 : entry_ref = pgstat_get_entry_ref(kind, dboid, objid, false, NULL);
1354 : :
1355 [ + + + + ]: 57 : if (entry_ref == NULL || entry_ref->pending == NULL)
1356 : 20 : return NULL;
1357 : :
1358 : 37 : return entry_ref;
1359 : : }
1360 : :
1361 : : void
1362 : 1256220 : pgstat_delete_pending_entry(PgStat_EntryRef *entry_ref)
1363 : : {
1364 : 1256220 : PgStat_Kind kind = entry_ref->shared_entry->key.kind;
1365 : 1256220 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
1366 : 1256220 : void *pending_data = entry_ref->pending;
1367 : :
1368 : : Assert(pending_data != NULL);
1369 : : /* !fixed_amount stats should be handled explicitly */
1370 : : Assert(!pgstat_get_kind_info(kind)->fixed_amount);
1371 : :
1372 [ + + ]: 1256220 : if (kind_info->delete_pending_cb)
1373 : 1185544 : kind_info->delete_pending_cb(entry_ref);
1374 : :
1375 : 1256220 : pfree(pending_data);
1376 : 1256220 : entry_ref->pending = NULL;
1377 : :
1378 : 1256220 : dlist_delete(&entry_ref->pending_node);
1379 : 1256220 : }
1380 : :
1381 : : /*
1382 : : * Prepare the given entry to receive pending stats, if not already done.
1383 : : */
1384 : : void
1385 : 2459208 : pgstat_prep_pending_from_entry_ref(PgStat_EntryRef *entry_ref)
1386 : : {
1387 : : PgStat_Kind kind;
1388 : :
1389 : : Assert(entry_ref != NULL);
1390 : :
1391 : 2459208 : kind = entry_ref->shared_entry->key.kind;
1392 : :
1393 : : /* need to be able to flush out */
1394 : : Assert(pgstat_get_kind_info(kind)->flush_pending_cb != NULL);
1395 : :
1396 [ + + ]: 2459208 : if (entry_ref->pending == NULL)
1397 : : {
1398 : 1256220 : size_t entrysize = pgstat_get_kind_info(kind)->pending_size;
1399 : :
1400 : : Assert(entrysize != (size_t) -1);
1401 : :
1402 [ + + ]: 1256220 : if (unlikely(!pgStatPendingContext))
1403 : : {
1404 : 21386 : pgStatPendingContext =
1405 : 21386 : AllocSetContextCreate(TopMemoryContext,
1406 : : "PgStat Pending",
1407 : : ALLOCSET_SMALL_SIZES);
1408 : : }
1409 : :
1410 : 1256220 : entry_ref->pending = MemoryContextAllocZero(pgStatPendingContext, entrysize);
1411 : 1256220 : dlist_push_tail(&pgStatPending, &entry_ref->pending_node);
1412 : : }
1413 : 2459208 : }
1414 : :
1415 : : /*
1416 : : * Flush out pending variable-numbered stats.
1417 : : */
1418 : : static bool
1419 : 43830 : pgstat_flush_pending_entries(bool nowait)
1420 : : {
1421 : 43830 : bool have_pending = false;
1422 : 43830 : dlist_node *cur = NULL;
1423 : :
1424 : : /*
1425 : : * Need to be a bit careful iterating over the list of pending entries.
1426 : : * Processing a pending entry may queue further pending entries to the end
1427 : : * of the list that we want to process, so a simple iteration won't do.
1428 : : * Further complicating matters is that we want to delete the current
1429 : : * entry in each iteration from the list if we flushed successfully.
1430 : : *
1431 : : * So we just keep track of the next pointer in each loop iteration.
1432 : : */
1433 [ + + ]: 43830 : if (!dlist_is_empty(&pgStatPending))
1434 : 40804 : cur = dlist_head_node(&pgStatPending);
1435 : :
1436 [ + + ]: 1255768 : while (cur)
1437 : : {
1438 : 1211938 : PgStat_EntryRef *entry_ref =
1439 : : dlist_container(PgStat_EntryRef, pending_node, cur);
1440 : 1211938 : PgStat_HashKey key = entry_ref->shared_entry->key;
1441 : 1211938 : PgStat_Kind kind = key.kind;
1442 : 1211938 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
1443 : : bool did_flush;
1444 : : dlist_node *next;
1445 : :
1446 : : Assert(!kind_info->fixed_amount);
1447 : : Assert(kind_info->flush_pending_cb != NULL);
1448 : :
1449 : : /* flush the stats, if possible */
1450 : 1211938 : did_flush = kind_info->flush_pending_cb(entry_ref, nowait);
1451 : :
1452 : : Assert(did_flush || nowait);
1453 : :
1454 : : /* determine next entry, before deleting the pending entry */
1455 [ + + ]: 1211938 : if (dlist_has_next(&pgStatPending, cur))
1456 : 1171134 : next = dlist_next_node(&pgStatPending, cur);
1457 : : else
1458 : 40804 : next = NULL;
1459 : :
1460 : : /* if successfully flushed, remove entry */
1461 [ + + ]: 1211938 : if (did_flush)
1462 : 1211923 : pgstat_delete_pending_entry(entry_ref);
1463 : : else
1464 : 15 : have_pending = true;
1465 : :
1466 : 1211938 : cur = next;
1467 : : }
1468 : :
1469 : : Assert(dlist_is_empty(&pgStatPending) == !have_pending);
1470 : :
1471 : 43830 : return have_pending;
1472 : : }
1473 : :
1474 : :
1475 : : /* ------------------------------------------------------------
1476 : : * Helper / infrastructure functions
1477 : : * ------------------------------------------------------------
1478 : : */
1479 : :
1480 : : PgStat_Kind
1481 : 99 : pgstat_get_kind_from_str(char *kind_str)
1482 : : {
1483 [ + + ]: 374 : for (PgStat_Kind kind = PGSTAT_KIND_BUILTIN_MIN; kind <= PGSTAT_KIND_BUILTIN_MAX; kind++)
1484 : : {
1485 [ + + ]: 370 : if (pg_strcasecmp(kind_str, pgstat_kind_builtin_infos[kind].name) == 0)
1486 : 95 : return kind;
1487 : : }
1488 : :
1489 : : /* Check the custom set of cumulative stats */
1490 [ - + ]: 4 : if (pgstat_kind_custom_infos)
1491 : : {
1492 [ # # ]: 0 : for (PgStat_Kind kind = PGSTAT_KIND_CUSTOM_MIN; kind <= PGSTAT_KIND_CUSTOM_MAX; kind++)
1493 : : {
1494 : 0 : uint32 idx = kind - PGSTAT_KIND_CUSTOM_MIN;
1495 : :
1496 [ # # # # ]: 0 : if (pgstat_kind_custom_infos[idx] &&
1497 : 0 : pg_strcasecmp(kind_str, pgstat_kind_custom_infos[idx]->name) == 0)
1498 : 0 : return kind;
1499 : : }
1500 : : }
1501 : :
1502 [ + - ]: 4 : ereport(ERROR,
1503 : : (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
1504 : : errmsg("invalid statistics kind: \"%s\"", kind_str)));
1505 : : return PGSTAT_KIND_INVALID; /* avoid compiler warnings */
1506 : : }
1507 : :
1508 : : static inline bool
1509 : 491796 : pgstat_is_kind_valid(PgStat_Kind kind)
1510 : : {
1511 [ + + + - ]: 491796 : return pgstat_is_kind_builtin(kind) || pgstat_is_kind_custom(kind);
1512 : : }
1513 : :
1514 : : const PgStat_KindInfo *
1515 : 10281522 : pgstat_get_kind_info(PgStat_Kind kind)
1516 : : {
1517 [ + + ]: 10281522 : if (pgstat_is_kind_builtin(kind))
1518 : 8200947 : return &pgstat_kind_builtin_infos[kind];
1519 : :
1520 [ + + ]: 2080575 : if (pgstat_is_kind_custom(kind))
1521 : : {
1522 : 1163880 : uint32 idx = kind - PGSTAT_KIND_CUSTOM_MIN;
1523 : :
1524 [ + + ]: 1163880 : if (pgstat_kind_custom_infos == NULL ||
1525 [ + + ]: 4590 : pgstat_kind_custom_infos[idx] == NULL)
1526 : 1162748 : return NULL;
1527 : 1132 : return pgstat_kind_custom_infos[idx];
1528 : : }
1529 : :
1530 : 916695 : return NULL;
1531 : : }
1532 : :
1533 : : /*
1534 : : * Register a new stats kind.
1535 : : *
1536 : : * PgStat_Kinds must be globally unique across all extensions. Refer
1537 : : * to https://wiki.postgresql.org/wiki/CustomCumulativeStats to reserve a
1538 : : * unique ID for your extension, to avoid conflicts with other extension
1539 : : * developers. During development, use PGSTAT_KIND_EXPERIMENTAL to avoid
1540 : : * needlessly reserving a new ID.
1541 : : */
1542 : : void
1543 : 18 : pgstat_register_kind(PgStat_Kind kind, const PgStat_KindInfo *kind_info)
1544 : : {
1545 : 18 : uint32 idx = kind - PGSTAT_KIND_CUSTOM_MIN;
1546 : :
1547 [ + - - + ]: 18 : if (kind_info->name == NULL || strlen(kind_info->name) == 0)
1548 [ # # ]: 0 : ereport(ERROR,
1549 : : (errmsg("failed to register custom cumulative statistics with ID %u", kind),
1550 : : errhint("Provide a non-empty name for the custom cumulative statistics.")));
1551 : :
1552 [ - + ]: 18 : if (!pgstat_is_kind_custom(kind))
1553 [ # # ]: 0 : ereport(ERROR, (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1554 : : errhint("Provide a custom cumulative statistics ID between %u and %u.",
1555 : : PGSTAT_KIND_CUSTOM_MIN, PGSTAT_KIND_CUSTOM_MAX)));
1556 : :
1557 [ - + ]: 18 : if (!process_shared_preload_libraries_in_progress)
1558 [ # # ]: 0 : ereport(ERROR,
1559 : : (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1560 : : errdetail("Custom cumulative statistics must be registered while initializing modules in \"%s\".",
1561 : : "shared_preload_libraries")));
1562 : :
1563 : : /*
1564 : : * Check some data for fixed-numbered stats.
1565 : : */
1566 [ + + ]: 18 : if (kind_info->fixed_amount)
1567 : : {
1568 [ - + ]: 9 : if (kind_info->shared_size == 0)
1569 [ # # ]: 0 : ereport(ERROR,
1570 : : (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1571 : : errhint("Custom cumulative statistics require a shared memory size for fixed-numbered objects.")));
1572 [ - + ]: 9 : if (kind_info->init_shmem_cb == NULL)
1573 [ # # ]: 0 : ereport(ERROR,
1574 : : (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1575 : : errhint("Custom cumulative statistics require a \"%s\" callback for fixed-numbered objects.",
1576 : : "init_shmem_cb")));
1577 [ - + ]: 9 : if (kind_info->reset_all_cb == NULL)
1578 [ # # ]: 0 : ereport(ERROR,
1579 : : (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1580 : : errhint("Custom cumulative statistics require a \"%s\" callback for fixed-numbered objects.",
1581 : : "reset_all_cb")));
1582 [ - + ]: 9 : if (kind_info->snapshot_cb == NULL)
1583 [ # # ]: 0 : ereport(ERROR,
1584 : : (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1585 : : errhint("Custom cumulative statistics require a \"%s\" callback for fixed-numbered objects.",
1586 : : "snapshot_cb")));
1587 [ - + ]: 9 : if (kind_info->track_entry_count)
1588 [ # # ]: 0 : ereport(ERROR,
1589 : : (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1590 : : errhint("Custom cumulative statistics cannot use entry count tracking for fixed-numbered objects.")));
1591 [ - + ]: 9 : if (kind_info->own_hash)
1592 [ # # ]: 0 : ereport(ERROR,
1593 : : (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1594 : : errhint("Custom cumulative statistics cannot use a dedicated hash table for fixed-numbered objects.")));
1595 [ + - - + ]: 9 : if (kind_info->to_serialized_name || kind_info->from_serialized_name)
1596 [ # # ]: 0 : ereport(ERROR,
1597 : : (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1598 : : errhint("Custom cumulative statistics cannot use serialized names for fixed-numbered objects.")));
1599 : : }
1600 : : else
1601 : : {
1602 [ + - - + ]: 9 : if (kind_info->pending_size > 0 && kind_info->flush_pending_cb == NULL)
1603 [ # # ]: 0 : ereport(ERROR,
1604 : : (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1605 : : errhint("Custom cumulative statistics require a \"%s\" callback when pending size is set.",
1606 : : "flush_pending_cb")));
1607 : : }
1608 : :
1609 : : /*
1610 : : * If pgstat_kind_custom_infos is not available yet, allocate it.
1611 : : */
1612 [ + + ]: 18 : if (pgstat_kind_custom_infos == NULL)
1613 : : {
1614 : 9 : pgstat_kind_custom_infos = (const PgStat_KindInfo **)
1615 : 9 : MemoryContextAllocZero(TopMemoryContext,
1616 : : sizeof(PgStat_KindInfo *) * PGSTAT_KIND_CUSTOM_SIZE);
1617 : : }
1618 : :
1619 [ - + ]: 18 : if (pgstat_kind_custom_infos[idx] != NULL &&
1620 [ # # ]: 0 : pgstat_kind_custom_infos[idx]->name != NULL)
1621 [ # # ]: 0 : ereport(ERROR,
1622 : : (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1623 : : errdetail("Custom cumulative statistics \"%s\" already registered with the same ID.",
1624 : : pgstat_kind_custom_infos[idx]->name)));
1625 : :
1626 : : /* check for existing custom stats with the same name */
1627 [ + + ]: 180 : for (PgStat_Kind existing_kind = PGSTAT_KIND_CUSTOM_MIN; existing_kind <= PGSTAT_KIND_CUSTOM_MAX; existing_kind++)
1628 : : {
1629 : 162 : uint32 existing_idx = existing_kind - PGSTAT_KIND_CUSTOM_MIN;
1630 : :
1631 [ + + ]: 162 : if (pgstat_kind_custom_infos[existing_idx] == NULL)
1632 : 153 : continue;
1633 [ - + ]: 9 : if (!pg_strcasecmp(pgstat_kind_custom_infos[existing_idx]->name, kind_info->name))
1634 [ # # ]: 0 : ereport(ERROR,
1635 : : (errmsg("failed to register custom cumulative statistics \"%s\" with ID %u", kind_info->name, kind),
1636 : : errdetail("Existing cumulative statistics with ID %u has the same name.", existing_kind)));
1637 : : }
1638 : :
1639 : : /* Register it */
1640 : 18 : pgstat_kind_custom_infos[idx] = kind_info;
1641 [ + - ]: 18 : ereport(LOG,
1642 : : (errmsg("registered custom cumulative statistics \"%s\" with ID %u",
1643 : : kind_info->name, kind)));
1644 : 18 : }
1645 : :
1646 : : /*
1647 : : * Stats should only be reported after pgstat_initialize() and before
1648 : : * pgstat_shutdown(). This check is put in a few central places to catch
1649 : : * violations of this rule more easily.
1650 : : */
1651 : : #ifdef USE_ASSERT_CHECKING
1652 : : void
1653 : : pgstat_assert_is_up(void)
1654 : : {
1655 : : Assert(pgstat_is_initialized && !pgstat_is_shutdown);
1656 : : }
1657 : : #endif
1658 : :
1659 : :
1660 : : /* ------------------------------------------------------------
1661 : : * reading and writing of on-disk stats file
1662 : : * ------------------------------------------------------------
1663 : : */
1664 : :
1665 : : #define write_chunk_s(fpout, ptr) write_chunk(fpout, ptr, sizeof(*ptr))
1666 : : #define read_chunk_s(fpin, ptr) read_chunk(fpin, ptr, sizeof(*ptr))
1667 : :
1668 : : /* helpers for pgstat_write_statsfile() */
1669 : : static void
1670 : 491909 : write_chunk(FILE *fpout, void *ptr, size_t len)
1671 : : {
1672 : : int rc;
1673 : :
1674 : 491909 : rc = fwrite(ptr, len, 1, fpout);
1675 : :
1676 : : /* we'll check for errors with ferror once at the end */
1677 : : (void) rc;
1678 : 491909 : }
1679 : :
1680 : : /*
1681 : : * This function is called in the last process that is accessing the shared
1682 : : * stats so locking is not required.
1683 : : */
1684 : : static void
1685 : 818 : pgstat_write_statsfile(void)
1686 : : {
1687 : : FILE *fpout;
1688 : : int32 format_id;
1689 : 818 : const char *tmpfile = PGSTAT_STAT_PERMANENT_TMPFILE;
1690 : 818 : const char *statfile = PGSTAT_STAT_PERMANENT_FILENAME;
1691 : : dshash_seq_status hstat;
1692 : : PgStatShared_HashEntry *ps;
1693 : 818 : PgStat_StatsFileOp status = STATS_WRITE;
1694 : :
1695 : : pgstat_assert_is_up();
1696 : :
1697 : : /* should be called only by the checkpointer or single user mode */
1698 : : Assert(!IsUnderPostmaster || MyBackendType == B_CHECKPOINTER);
1699 : :
1700 : : /* we're shutting down, so it's ok to just override this */
1701 : 818 : pgstat_fetch_consistency = PGSTAT_FETCH_CONSISTENCY_NONE;
1702 : :
1703 [ + + ]: 818 : elog(DEBUG2, "writing stats file \"%s\"", statfile);
1704 : :
1705 : : /*
1706 : : * Open the statistics temp file to write out the current values.
1707 : : */
1708 : 818 : fpout = AllocateFile(tmpfile, PG_BINARY_W);
1709 [ - + ]: 818 : if (fpout == NULL)
1710 : : {
1711 [ # # ]: 0 : ereport(LOG,
1712 : : (errcode_for_file_access(),
1713 : : errmsg("could not open temporary statistics file \"%s\": %m",
1714 : : tmpfile)));
1715 : 0 : return;
1716 : : }
1717 : :
1718 : : /*
1719 : : * Write the file header --- currently just a format ID.
1720 : : */
1721 : 818 : format_id = PGSTAT_FILE_FORMAT_ID;
1722 : 818 : write_chunk_s(fpout, &format_id);
1723 : :
1724 : : /* Write various stats structs for fixed number of objects */
1725 [ + + ]: 26994 : for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
1726 : : {
1727 : : char *ptr;
1728 : 26176 : const PgStat_KindInfo *info = pgstat_get_kind_info(kind);
1729 : :
1730 [ + + + + ]: 26176 : if (!info || !info->fixed_amount)
1731 : 20445 : continue;
1732 : :
1733 : 5731 : if (pgstat_is_kind_builtin(kind))
1734 : : Assert(info->snapshot_ctl_off != 0);
1735 : :
1736 : : /* skip if no need to write to file */
1737 [ - + ]: 5731 : if (!info->write_to_file)
1738 : 0 : continue;
1739 : :
1740 : 5731 : pgstat_build_snapshot_fixed(kind);
1741 [ + + ]: 5731 : if (pgstat_is_kind_builtin(kind))
1742 : 5726 : ptr = ((char *) &pgStatLocal.snapshot) + info->snapshot_ctl_off;
1743 : : else
1744 : 5 : ptr = pgStatLocal.snapshot.custom_data[kind - PGSTAT_KIND_CUSTOM_MIN];
1745 : :
1746 : 5731 : fputc(PGSTAT_FILE_ENTRY_FIXED, fpout);
1747 : 5731 : write_chunk_s(fpout, &kind);
1748 : 5731 : write_chunk(fpout, ptr, info->shared_data_len);
1749 : : }
1750 : :
1751 : : /*
1752 : : * Walk through the stats entries, as long as writes can happen (note that
1753 : : * switching to a STATS_DISCARD status is possible while walking through).
1754 : : */
1755 [ + + + - ]: 1638 : for (int h = 0; h < pgStatLocal.num_var_hashes && status == STATS_WRITE; h++)
1756 : : {
1757 : 820 : dshash_table *hash = pgStatLocal.var_hashes[h];
1758 : :
1759 : 820 : dshash_seq_init(&hstat, hash, false);
1760 [ + + ]: 240710 : while ((ps = dshash_seq_next(&hstat)) != NULL)
1761 : : {
1762 : : PgStatShared_Common *shstats;
1763 : 239890 : const PgStat_KindInfo *kind_info = NULL;
1764 : :
1765 : : /*
1766 : : * We should not see any "dropped" entries when writing the stats
1767 : : * file, as all backends and auxiliary processes should have
1768 : : * cleaned up their references before they terminated.
1769 : : *
1770 : : * However, since we are already shutting down, it is not worth
1771 : : * crashing the server over any potential cleanup issues, so we
1772 : : * simply skip such entries if encountered.
1773 : : */
1774 : : Assert(!ps->dropped);
1775 [ - + ]: 239890 : if (ps->dropped)
1776 : 0 : continue;
1777 : :
1778 : : /*
1779 : : * This discards data related to custom stats kinds that are
1780 : : * unknown to this process.
1781 : : */
1782 [ - + ]: 239890 : if (!pgstat_is_kind_valid(ps->key.kind))
1783 : : {
1784 [ # # ]: 0 : elog(WARNING, "found unknown stats entry %u/%u/%" PRIu64,
1785 : : ps->key.kind, ps->key.dboid,
1786 : : ps->key.objid);
1787 : 0 : continue;
1788 : : }
1789 : :
1790 : 239890 : kind_info = pgstat_get_kind_info(ps->key.kind);
1791 : : shstats = (PgStatShared_Common *)
1792 : 239890 : dsa_get_address(pgStatLocal.kind_dsa[ps->key.kind], ps->body);
1793 : :
1794 : : /* if not dropped the valid-entry refcount should exist */
1795 : : Assert(pg_atomic_read_u32(&ps->refcount) > 0);
1796 : :
1797 : : /* skip if no need to write to file */
1798 [ + + ]: 239890 : if (!kind_info->write_to_file)
1799 : 137 : continue;
1800 : :
1801 [ + + ]: 239753 : if (!kind_info->to_serialized_name)
1802 : : {
1803 : : /* normal stats entry, identified by PgStat_HashKey */
1804 : 239630 : fputc(PGSTAT_FILE_ENTRY_HASH, fpout);
1805 : 239630 : write_chunk_s(fpout, &ps->key);
1806 : : }
1807 : : else
1808 : : {
1809 : : /* stats entry identified by name on disk (e.g. slots) */
1810 : : NameData name;
1811 : :
1812 : 123 : kind_info->to_serialized_name(&ps->key, shstats, &name);
1813 : :
1814 : 123 : fputc(PGSTAT_FILE_ENTRY_NAME, fpout);
1815 : 123 : write_chunk_s(fpout, &ps->key.kind);
1816 : 123 : write_chunk_s(fpout, &name);
1817 : : }
1818 : :
1819 : : /* Write except the header part of the entry */
1820 : 239753 : write_chunk(fpout,
1821 : : pgstat_get_entry_data(ps->key.kind, shstats),
1822 : : pgstat_get_entry_len(ps->key.kind));
1823 : :
1824 : : /* Write more data for the entry, if required */
1825 [ + + ]: 239753 : if (kind_info->to_serialized_data &&
1826 [ - + ]: 4 : !kind_info->to_serialized_data(&ps->key, shstats, fpout))
1827 : : {
1828 : 0 : status = STATS_DISCARD;
1829 : 0 : break;
1830 : : }
1831 : : }
1832 : 820 : dshash_seq_term(&hstat);
1833 : : }
1834 : :
1835 : : /*
1836 : : * No more output to be done. Close the temp file and replace the old
1837 : : * pgstat.stat with it. The ferror() check replaces testing for error
1838 : : * after each individual fputc or fwrite (in write_chunk()) above.
1839 : : */
1840 : 818 : fputc(PGSTAT_FILE_ENTRY_END, fpout);
1841 : :
1842 [ - + ]: 818 : if (status == STATS_DISCARD)
1843 : : {
1844 : : /*
1845 : : * A to_serialized_data callback failed. DEBUG2 because the callback
1846 : : * already logged the reason.
1847 : : */
1848 [ # # ]: 0 : elog(DEBUG2, "discarding temporary statistics file \"%s\"", tmpfile);
1849 : 0 : FreeFile(fpout);
1850 : 0 : unlink(tmpfile);
1851 : : }
1852 [ - + ]: 818 : else if (ferror(fpout))
1853 : : {
1854 [ # # ]: 0 : ereport(LOG,
1855 : : (errcode_for_file_access(),
1856 : : errmsg("could not write temporary statistics file \"%s\": %m",
1857 : : tmpfile)));
1858 : 0 : FreeFile(fpout);
1859 : 0 : unlink(tmpfile);
1860 : 0 : status = STATS_DISCARD;
1861 : : }
1862 [ - + ]: 818 : else if (FreeFile(fpout) < 0)
1863 : : {
1864 [ # # ]: 0 : ereport(LOG,
1865 : : (errcode_for_file_access(),
1866 : : errmsg("could not close temporary statistics file \"%s\": %m",
1867 : : tmpfile)));
1868 : 0 : unlink(tmpfile);
1869 : 0 : status = STATS_DISCARD;
1870 : : }
1871 [ - + ]: 818 : else if (durable_rename(tmpfile, statfile, LOG) < 0)
1872 : : {
1873 : : /* durable_rename already emitted log message */
1874 : 0 : unlink(tmpfile);
1875 : 0 : status = STATS_DISCARD;
1876 : : }
1877 : :
1878 : : /* Finish callbacks, if required */
1879 [ + + ]: 26994 : for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
1880 : : {
1881 : 26176 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
1882 : :
1883 [ + + + + ]: 26176 : if (kind_info && kind_info->finish)
1884 : 5 : kind_info->finish(status);
1885 : : }
1886 : : }
1887 : :
1888 : : /* helpers for pgstat_read_statsfile() */
1889 : : static bool
1890 : 504781 : read_chunk(FILE *fpin, void *ptr, size_t len)
1891 : : {
1892 : 504781 : return fread(ptr, 1, len, fpin) == len;
1893 : : }
1894 : :
1895 : : /*
1896 : : * Reads in existing statistics file into memory.
1897 : : *
1898 : : * This function is called in the only process that is accessing the shared
1899 : : * stats so locking is not required.
1900 : : */
1901 : : static void
1902 : 941 : pgstat_read_statsfile(void)
1903 : : {
1904 : : FILE *fpin;
1905 : : int32 format_id;
1906 : : bool found;
1907 : 941 : PgStat_StatsFileOp status = STATS_READ;
1908 : 941 : const char *statfile = PGSTAT_STAT_PERMANENT_FILENAME;
1909 : 941 : PgStat_ShmemControl *shmem = pgStatLocal.shmem;
1910 : :
1911 : : /* shouldn't be called from postmaster */
1912 : : Assert(IsUnderPostmaster || !IsPostmasterEnvironment);
1913 : :
1914 [ + + ]: 941 : elog(DEBUG2, "reading stats file \"%s\"", statfile);
1915 : :
1916 : : /*
1917 : : * Try to open the stats file. If it doesn't exist, the backends simply
1918 : : * returns zero for anything and statistics simply starts from scratch
1919 : : * with empty counters.
1920 : : *
1921 : : * ENOENT is a possibility if stats collection was previously disabled or
1922 : : * has not yet written the stats file for the first time. Any other
1923 : : * failure condition is suspicious.
1924 : : */
1925 [ + + ]: 941 : if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
1926 : : {
1927 [ - + ]: 60 : if (errno != ENOENT)
1928 [ # # ]: 0 : ereport(LOG,
1929 : : (errcode_for_file_access(),
1930 : : errmsg("could not open statistics file \"%s\": %m",
1931 : : statfile)));
1932 : 60 : pgstat_reset_after_failure();
1933 : 60 : status = STATS_DISCARD;
1934 : 60 : goto finish;
1935 : : }
1936 : :
1937 : : /*
1938 : : * Verify it's of the expected format.
1939 : : */
1940 [ - + ]: 881 : if (!read_chunk_s(fpin, &format_id))
1941 : : {
1942 [ # # ]: 0 : elog(WARNING, "could not read format ID");
1943 : 0 : goto error;
1944 : : }
1945 : :
1946 [ + + ]: 881 : if (format_id != PGSTAT_FILE_FORMAT_ID)
1947 : : {
1948 [ + - ]: 1 : elog(WARNING, "found incorrect format ID %d (expected %d)",
1949 : : format_id, PGSTAT_FILE_FORMAT_ID);
1950 : 1 : goto error;
1951 : : }
1952 : :
1953 : : /*
1954 : : * We found an existing statistics file. Read it and put all the stats
1955 : : * data into place.
1956 : : */
1957 : : for (;;)
1958 : 251906 : {
1959 : 252786 : int t = fgetc(fpin);
1960 : :
1961 [ + + + - ]: 252786 : switch (t)
1962 : : {
1963 : 6165 : case PGSTAT_FILE_ENTRY_FIXED:
1964 : : {
1965 : : PgStat_Kind kind;
1966 : : const PgStat_KindInfo *info;
1967 : : char *ptr;
1968 : :
1969 : : /* entry for fixed-numbered stats */
1970 [ - + ]: 6165 : if (!read_chunk_s(fpin, &kind))
1971 : : {
1972 [ # # ]: 0 : elog(WARNING, "could not read stats kind for entry of type %c", t);
1973 : 0 : goto error;
1974 : : }
1975 : :
1976 [ - + ]: 6165 : if (!pgstat_is_kind_valid(kind))
1977 : : {
1978 [ # # ]: 0 : elog(WARNING, "invalid stats kind %u for entry of type %c",
1979 : : kind, t);
1980 : 0 : goto error;
1981 : : }
1982 : :
1983 : 6165 : info = pgstat_get_kind_info(kind);
1984 [ - + ]: 6165 : if (!info)
1985 : : {
1986 [ # # ]: 0 : elog(WARNING, "could not find information of kind %u for entry of type %c",
1987 : : kind, t);
1988 : 0 : goto error;
1989 : : }
1990 : :
1991 [ - + ]: 6165 : if (!info->fixed_amount)
1992 : : {
1993 [ # # ]: 0 : elog(WARNING, "invalid fixed_amount in stats kind %u for entry of type %c",
1994 : : kind, t);
1995 : 0 : goto error;
1996 : : }
1997 : :
1998 : : /* Load back stats into shared memory */
1999 [ + + ]: 6165 : if (pgstat_is_kind_builtin(kind))
2000 : 6160 : ptr = ((char *) shmem) + info->shared_ctl_off +
2001 : 6160 : info->shared_data_off;
2002 : : else
2003 : : {
2004 : 5 : int idx = kind - PGSTAT_KIND_CUSTOM_MIN;
2005 : :
2006 : 5 : ptr = ((char *) shmem->custom_data[idx]) +
2007 : 5 : info->shared_data_off;
2008 : : }
2009 : :
2010 [ - + ]: 6165 : if (!read_chunk(fpin, ptr, info->shared_data_len))
2011 : : {
2012 [ # # ]: 0 : elog(WARNING, "could not read data of stats kind %u for entry of type %c with size %u",
2013 : : kind, t, info->shared_data_len);
2014 : 0 : goto error;
2015 : : }
2016 : :
2017 : 6165 : break;
2018 : : }
2019 : 245741 : case PGSTAT_FILE_ENTRY_HASH:
2020 : : case PGSTAT_FILE_ENTRY_NAME:
2021 : : {
2022 : : PgStat_HashKey key;
2023 : : PgStatShared_HashEntry *p;
2024 : : PgStatShared_Common *header;
2025 : 245741 : const PgStat_KindInfo *kind_info = NULL;
2026 : : dsa_pointer chunk;
2027 : :
2028 [ - + ]: 245741 : CHECK_FOR_INTERRUPTS();
2029 : :
2030 [ + + ]: 245741 : if (t == PGSTAT_FILE_ENTRY_HASH)
2031 : : {
2032 : : /* normal stats entry, identified by PgStat_HashKey */
2033 [ - + ]: 245652 : if (!read_chunk_s(fpin, &key))
2034 : : {
2035 [ # # ]: 0 : elog(WARNING, "could not read key for entry of type %c", t);
2036 : 0 : goto error;
2037 : : }
2038 : :
2039 [ - + ]: 245652 : if (!pgstat_is_kind_valid(key.kind))
2040 : : {
2041 [ # # ]: 0 : elog(WARNING, "invalid stats kind for entry %u/%u/%" PRIu64 " of type %c",
2042 : : key.kind, key.dboid,
2043 : : key.objid, t);
2044 : 0 : goto error;
2045 : : }
2046 : :
2047 : 245652 : kind_info = pgstat_get_kind_info(key.kind);
2048 [ - + ]: 245652 : if (!kind_info)
2049 : : {
2050 [ # # ]: 0 : elog(WARNING, "could not find information of kind for entry %u/%u/%" PRIu64 " of type %c",
2051 : : key.kind, key.dboid,
2052 : : key.objid, t);
2053 : 0 : goto error;
2054 : : }
2055 : :
2056 [ - + ]: 245652 : if (kind_info->fixed_amount)
2057 : : {
2058 [ # # ]: 0 : elog(WARNING, "invalid fixed_amount in stats kind for entry %u/%u/%" PRIu64 " of type %c",
2059 : : key.kind, key.dboid,
2060 : : key.objid, t);
2061 : 0 : goto error;
2062 : : }
2063 : : }
2064 : : else
2065 : : {
2066 : : /* stats entry identified by name on disk (e.g. slots) */
2067 : : PgStat_Kind kind;
2068 : : NameData name;
2069 : :
2070 [ - + ]: 89 : if (!read_chunk_s(fpin, &kind))
2071 : : {
2072 [ # # ]: 0 : elog(WARNING, "could not read stats kind for entry of type %c", t);
2073 : 0 : goto error;
2074 : : }
2075 [ - + ]: 89 : if (!read_chunk_s(fpin, &name))
2076 : : {
2077 [ # # ]: 0 : elog(WARNING, "could not read name of stats kind %u for entry of type %c",
2078 : : kind, t);
2079 : 0 : goto error;
2080 : : }
2081 [ - + ]: 89 : if (!pgstat_is_kind_valid(kind))
2082 : : {
2083 [ # # ]: 0 : elog(WARNING, "invalid stats kind %u for entry of type %c",
2084 : : kind, t);
2085 : 0 : goto error;
2086 : : }
2087 : :
2088 : 89 : kind_info = pgstat_get_kind_info(kind);
2089 [ - + ]: 89 : if (!kind_info)
2090 : : {
2091 [ # # ]: 0 : elog(WARNING, "could not find information of kind %u for entry of type %c",
2092 : : kind, t);
2093 : 0 : goto error;
2094 : : }
2095 : :
2096 [ - + ]: 89 : if (!kind_info->from_serialized_name)
2097 : : {
2098 [ # # ]: 0 : elog(WARNING, "invalid from_serialized_name in stats kind %u for entry of type %c",
2099 : : kind, t);
2100 : 0 : goto error;
2101 : : }
2102 : :
2103 [ + + ]: 89 : if (!kind_info->from_serialized_name(&name, &key))
2104 : : {
2105 : : /* skip over data for entry we don't care about */
2106 [ - + ]: 1 : if (fseek(fpin, pgstat_get_entry_len(kind), SEEK_CUR) != 0)
2107 : : {
2108 [ # # ]: 0 : elog(WARNING, "could not seek \"%s\" of stats kind %u for entry of type %c",
2109 : : NameStr(name), kind, t);
2110 : 0 : goto error;
2111 : : }
2112 : :
2113 : 1 : continue;
2114 : : }
2115 : :
2116 : : Assert(key.kind == kind);
2117 : : }
2118 : :
2119 : : /*
2120 : : * This intentionally doesn't use pgstat_get_entry_ref() -
2121 : : * putting all stats into checkpointer's
2122 : : * pgStatEntryRefHash would be wasted effort and memory.
2123 : : *
2124 : : * Allocate the DSA body before inserting the hash entry.
2125 : : */
2126 : 245740 : chunk = pgstat_alloc_entry_body(key.kind);
2127 [ - + ]: 245740 : if (chunk == InvalidDsaPointer)
2128 : : {
2129 : : /*
2130 : : * It would be tempting to switch this ERROR to a
2131 : : * WARNING, but it would mean that all the statistics
2132 : : * are discarded when the environment fails on OOM.
2133 : : */
2134 [ # # ]: 0 : elog(ERROR, "could not allocate entry %u/%u/%" PRIu64 " of type %c",
2135 : : key.kind, key.dboid,
2136 : : key.objid, t);
2137 : : }
2138 : :
2139 : 245740 : p = dshash_find_or_insert_extended(pgStatLocal.kind_hash[key.kind],
2140 : : &key, &found,
2141 : : DSHASH_INSERT_NO_OOM);
2142 [ - + ]: 245740 : if (!p)
2143 : : {
2144 : 0 : dsa_free(pgStatLocal.kind_dsa[key.kind], chunk);
2145 : :
2146 : : /*
2147 : : * for the same reason as previously, ERROR not
2148 : : * WARNING
2149 : : */
2150 [ # # ]: 0 : elog(ERROR, "could not insert entry %u/%u/%" PRIu64 " of type %c",
2151 : : key.kind, key.dboid,
2152 : : key.objid, t);
2153 : : }
2154 : :
2155 : : /* don't allow duplicate entries */
2156 [ - + ]: 245740 : if (found)
2157 : : {
2158 : 0 : dshash_release_lock(pgStatLocal.kind_hash[key.kind], p);
2159 : 0 : dsa_free(pgStatLocal.kind_dsa[key.kind], chunk);
2160 [ # # ]: 0 : elog(WARNING, "found duplicate stats entry %u/%u/%" PRIu64 " of type %c",
2161 : : key.kind, key.dboid,
2162 : : key.objid, t);
2163 : 0 : goto error;
2164 : : }
2165 : :
2166 : 245740 : header = pgstat_init_entry(key.kind, p, chunk);
2167 : 245740 : dshash_release_lock(pgStatLocal.kind_hash[key.kind], p);
2168 : :
2169 [ - + ]: 245740 : if (!read_chunk(fpin,
2170 : : pgstat_get_entry_data(key.kind, header),
2171 : : pgstat_get_entry_len(key.kind)))
2172 : : {
2173 [ # # ]: 0 : elog(WARNING, "could not read data for entry %u/%u/%" PRIu64 " of type %c",
2174 : : key.kind, key.dboid,
2175 : : key.objid, t);
2176 : 0 : goto error;
2177 : : }
2178 : :
2179 : : /* read more data for the entry, if required */
2180 [ + + ]: 245740 : if (kind_info->from_serialized_data)
2181 : : {
2182 [ - + ]: 4 : if (!kind_info->from_serialized_data(&key, header, fpin))
2183 : : {
2184 [ # # ]: 0 : elog(WARNING, "could not read auxiliary data for entry %u/%u/%" PRIu64 " of type %c",
2185 : : key.kind, key.dboid,
2186 : : key.objid, t);
2187 : 0 : goto error;
2188 : : }
2189 : : }
2190 : :
2191 : 245740 : break;
2192 : : }
2193 : 880 : case PGSTAT_FILE_ENTRY_END:
2194 : :
2195 : : /*
2196 : : * check that PGSTAT_FILE_ENTRY_END actually signals end of
2197 : : * file
2198 : : */
2199 [ + + ]: 880 : if (fgetc(fpin) != EOF)
2200 : : {
2201 [ + - ]: 1 : elog(WARNING, "could not read end-of-file");
2202 : 1 : goto error;
2203 : : }
2204 : :
2205 : 879 : goto done;
2206 : :
2207 : 0 : default:
2208 [ # # ]: 0 : elog(WARNING, "could not read entry of type %c", t);
2209 : 0 : goto error;
2210 : : }
2211 : : }
2212 : :
2213 : 881 : done:
2214 : : /* First, cleanup the main stats file */
2215 : 881 : FreeFile(fpin);
2216 : :
2217 [ + + ]: 881 : elog(DEBUG2, "removing permanent stats file \"%s\"", statfile);
2218 : 881 : unlink(statfile);
2219 : :
2220 : 941 : finish:
2221 : : /* Finish callbacks, if required */
2222 [ + + ]: 31053 : for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
2223 : : {
2224 : 30112 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
2225 : :
2226 [ + + + + ]: 30112 : if (kind_info && kind_info->finish)
2227 : 6 : kind_info->finish(status);
2228 : : }
2229 : :
2230 : 941 : return;
2231 : :
2232 : 2 : error:
2233 [ + - ]: 2 : ereport(LOG,
2234 : : (errmsg("corrupted statistics file \"%s\"", statfile)));
2235 : :
2236 : 2 : pgstat_reset_after_failure();
2237 : 2 : status = STATS_DISCARD;
2238 : :
2239 : 2 : goto done;
2240 : : }
2241 : :
2242 : : /*
2243 : : * Helper to reset / drop stats after a crash or after restoring stats from
2244 : : * disk failed, potentially after already loading parts.
2245 : : */
2246 : : static void
2247 : 271 : pgstat_reset_after_failure(void)
2248 : : {
2249 : 271 : TimestampTz ts = GetCurrentTimestamp();
2250 : :
2251 : : /* reset fixed-numbered stats */
2252 [ + + ]: 8943 : for (PgStat_Kind kind = PGSTAT_KIND_MIN; kind <= PGSTAT_KIND_MAX; kind++)
2253 : : {
2254 : 8672 : const PgStat_KindInfo *kind_info = pgstat_get_kind_info(kind);
2255 : :
2256 [ + + + + ]: 8672 : if (!kind_info || !kind_info->fixed_amount)
2257 : 6772 : continue;
2258 : :
2259 : 1900 : kind_info->reset_all_cb(ts);
2260 : : }
2261 : :
2262 : : /* and drop variable-numbered ones */
2263 : 271 : pgstat_drop_all_entries();
2264 : 271 : }
2265 : :
2266 : : /*
2267 : : * GUC assign_hook for stats_fetch_consistency.
2268 : : */
2269 : : void
2270 : 3614 : assign_stats_fetch_consistency(int newval, void *extra)
2271 : : {
2272 : : /*
2273 : : * Changing this value in a transaction may cause snapshot state
2274 : : * inconsistencies, so force a clear of the current snapshot on the next
2275 : : * snapshot build attempt.
2276 : : */
2277 [ + + ]: 3614 : if (pgstat_fetch_consistency != newval)
2278 : 2134 : force_stats_snapshot_clear = true;
2279 : 3614 : }
|