00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019 #include "postgres.h"
00020
00021 #include <unistd.h>
00022 #include <fcntl.h>
00023 #include <sys/param.h>
00024 #include <sys/time.h>
00025 #include <sys/socket.h>
00026 #include <netdb.h>
00027 #include <netinet/in.h>
00028 #include <arpa/inet.h>
00029 #include <signal.h>
00030 #include <time.h>
00031
00032 #include "pgstat.h"
00033
00034 #include "access/heapam.h"
00035 #include "access/htup_details.h"
00036 #include "access/transam.h"
00037 #include "access/twophase_rmgr.h"
00038 #include "access/xact.h"
00039 #include "catalog/pg_database.h"
00040 #include "catalog/pg_proc.h"
00041 #include "lib/ilist.h"
00042 #include "libpq/ip.h"
00043 #include "libpq/libpq.h"
00044 #include "libpq/pqsignal.h"
00045 #include "mb/pg_wchar.h"
00046 #include "miscadmin.h"
00047 #include "pg_trace.h"
00048 #include "postmaster/autovacuum.h"
00049 #include "postmaster/fork_process.h"
00050 #include "postmaster/postmaster.h"
00051 #include "storage/backendid.h"
00052 #include "storage/fd.h"
00053 #include "storage/ipc.h"
00054 #include "storage/latch.h"
00055 #include "storage/pg_shmem.h"
00056 #include "storage/procsignal.h"
00057 #include "utils/ascii.h"
00058 #include "utils/guc.h"
00059 #include "utils/memutils.h"
00060 #include "utils/ps_status.h"
00061 #include "utils/rel.h"
00062 #include "utils/timestamp.h"
00063 #include "utils/tqual.h"
00064
00065
00066
00067
00068
00069
00070 #define PGSTAT_STAT_PERMANENT_DIRECTORY "pg_stat"
00071 #define PGSTAT_STAT_PERMANENT_FILENAME "pg_stat/global.stat"
00072 #define PGSTAT_STAT_PERMANENT_TMPFILE "pg_stat/global.tmp"
00073
00074
00075
00076
00077
00078 #define PGSTAT_STAT_INTERVAL 500
00079
00080
00081 #define PGSTAT_RETRY_DELAY 10
00082
00083
00084 #define PGSTAT_MAX_WAIT_TIME 10000
00085
00086
00087 #define PGSTAT_INQ_INTERVAL 640
00088
00089
00090 #define PGSTAT_RESTART_INTERVAL 60
00091
00092
00093
00094 #define PGSTAT_POLL_LOOP_COUNT (PGSTAT_MAX_WAIT_TIME / PGSTAT_RETRY_DELAY)
00095 #define PGSTAT_INQ_LOOP_COUNT (PGSTAT_INQ_INTERVAL / PGSTAT_RETRY_DELAY)
00096
00097
00098
00099
00100
00101
00102 #define PGSTAT_DB_HASH_SIZE 16
00103 #define PGSTAT_TAB_HASH_SIZE 512
00104 #define PGSTAT_FUNCTION_HASH_SIZE 512
00105
00106
00107
00108
00109
00110
00111 bool pgstat_track_activities = false;
00112 bool pgstat_track_counts = false;
00113 int pgstat_track_functions = TRACK_FUNC_OFF;
00114 int pgstat_track_activity_query_size = 1024;
00115
00116
00117
00118
00119
00120 char *pgstat_stat_directory = NULL;
00121 char *pgstat_stat_filename = NULL;
00122 char *pgstat_stat_tmpname = NULL;
00123
00124
00125
00126
00127
00128
00129 PgStat_MsgBgWriter BgWriterStats;
00130
00131
00132
00133
00134
00135 NON_EXEC_STATIC pgsocket pgStatSock = PGINVALID_SOCKET;
00136
00137 static Latch pgStatLatch;
00138
00139 static struct sockaddr_storage pgStatAddr;
00140
00141 static time_t last_pgstat_start_time;
00142
00143 static bool pgStatRunningInCollector = false;
00144
00145
00146
00147
00148
00149
00150
00151
00152
00153
00154
00155
00156 #define TABSTAT_QUANTUM 100
00157
00158 typedef struct TabStatusArray
00159 {
00160 struct TabStatusArray *tsa_next;
00161 int tsa_used;
00162 PgStat_TableStatus tsa_entries[TABSTAT_QUANTUM];
00163 } TabStatusArray;
00164
00165 static TabStatusArray *pgStatTabList = NULL;
00166
00167
00168
00169
00170
00171 static HTAB *pgStatFunctions = NULL;
00172
00173
00174
00175
00176
00177 static bool have_function_stats = false;
00178
00179
00180
00181
00182
00183
00184
00185
00186 typedef struct PgStat_SubXactStatus
00187 {
00188 int nest_level;
00189 struct PgStat_SubXactStatus *prev;
00190 PgStat_TableXactStatus *first;
00191 } PgStat_SubXactStatus;
00192
00193 static PgStat_SubXactStatus *pgStatXactStack = NULL;
00194
00195 static int pgStatXactCommit = 0;
00196 static int pgStatXactRollback = 0;
00197 PgStat_Counter pgStatBlockReadTime = 0;
00198 PgStat_Counter pgStatBlockWriteTime = 0;
00199
00200
00201 typedef struct TwoPhasePgStatRecord
00202 {
00203 PgStat_Counter tuples_inserted;
00204 PgStat_Counter tuples_updated;
00205 PgStat_Counter tuples_deleted;
00206 Oid t_id;
00207 bool t_shared;
00208 } TwoPhasePgStatRecord;
00209
00210
00211
00212
00213 static MemoryContext pgStatLocalContext = NULL;
00214 static HTAB *pgStatDBHash = NULL;
00215 static PgBackendStatus *localBackendStatusTable = NULL;
00216 static int localNumBackends = 0;
00217
00218
00219
00220
00221
00222
00223 static PgStat_GlobalStats globalStats;
00224
00225
00226 typedef struct DBWriteRequest
00227 {
00228 Oid databaseid;
00229 TimestampTz request_time;
00230 slist_node next;
00231 } DBWriteRequest;
00232
00233
00234 static slist_head last_statrequests = SLIST_STATIC_INIT(last_statrequests);
00235
00236 static volatile bool need_exit = false;
00237 static volatile bool got_SIGHUP = false;
00238
00239
00240
00241
00242
00243
00244 static instr_time total_func_time;
00245
00246
00247
00248
00249
00250
00251 #ifdef EXEC_BACKEND
00252 static pid_t pgstat_forkexec(void);
00253 #endif
00254
00255 NON_EXEC_STATIC void PgstatCollectorMain(int argc, char *argv[]) __attribute__((noreturn));
00256 static void pgstat_exit(SIGNAL_ARGS);
00257 static void pgstat_beshutdown_hook(int code, Datum arg);
00258 static void pgstat_sighup_handler(SIGNAL_ARGS);
00259
00260 static PgStat_StatDBEntry *pgstat_get_db_entry(Oid databaseid, bool create);
00261 static PgStat_StatTabEntry *pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry,
00262 Oid tableoid, bool create);
00263 static void pgstat_write_statsfiles(bool permanent, bool allDbs);
00264 static void pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanent);
00265 static HTAB *pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep);
00266 static void pgstat_read_db_statsfile(Oid databaseid, HTAB *tabhash, HTAB *funchash, bool permanent);
00267 static void backend_read_statsfile(void);
00268 static void pgstat_read_current_status(void);
00269
00270 static bool pgstat_write_statsfile_needed(void);
00271 static bool pgstat_db_requested(Oid databaseid);
00272
00273 static void pgstat_send_tabstat(PgStat_MsgTabstat *tsmsg);
00274 static void pgstat_send_funcstats(void);
00275 static HTAB *pgstat_collect_oids(Oid catalogid);
00276
00277 static PgStat_TableStatus *get_tabstat_entry(Oid rel_id, bool isshared);
00278
00279 static void pgstat_setup_memcxt(void);
00280
00281 static void pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype);
00282 static void pgstat_send(void *msg, int len);
00283
00284 static void pgstat_recv_inquiry(PgStat_MsgInquiry *msg, int len);
00285 static void pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len);
00286 static void pgstat_recv_tabpurge(PgStat_MsgTabpurge *msg, int len);
00287 static void pgstat_recv_dropdb(PgStat_MsgDropdb *msg, int len);
00288 static void pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len);
00289 static void pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len);
00290 static void pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, int len);
00291 static void pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len);
00292 static void pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len);
00293 static void pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len);
00294 static void pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len);
00295 static void pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len);
00296 static void pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len);
00297 static void pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len);
00298 static void pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len);
00299 static void pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len);
00300
00301
00302
00303
00304
00305
00306
00307
00308
00309
00310
00311
00312
00313
00314
00315 void
00316 pgstat_init(void)
00317 {
00318 ACCEPT_TYPE_ARG3 alen;
00319 struct addrinfo *addrs = NULL,
00320 *addr,
00321 hints;
00322 int ret;
00323 fd_set rset;
00324 struct timeval tv;
00325 char test_byte;
00326 int sel_res;
00327 int tries = 0;
00328
00329 #define TESTBYTEVAL ((char) 199)
00330
00331
00332
00333
00334 hints.ai_flags = AI_PASSIVE;
00335 hints.ai_family = PF_UNSPEC;
00336 hints.ai_socktype = SOCK_DGRAM;
00337 hints.ai_protocol = 0;
00338 hints.ai_addrlen = 0;
00339 hints.ai_addr = NULL;
00340 hints.ai_canonname = NULL;
00341 hints.ai_next = NULL;
00342 ret = pg_getaddrinfo_all("localhost", NULL, &hints, &addrs);
00343 if (ret || !addrs)
00344 {
00345 ereport(LOG,
00346 (errmsg("could not resolve \"localhost\": %s",
00347 gai_strerror(ret))));
00348 goto startup_failed;
00349 }
00350
00351
00352
00353
00354
00355
00356
00357
00358
00359 for (addr = addrs; addr; addr = addr->ai_next)
00360 {
00361 #ifdef HAVE_UNIX_SOCKETS
00362
00363 if (addr->ai_family == AF_UNIX)
00364 continue;
00365 #endif
00366
00367 if (++tries > 1)
00368 ereport(LOG,
00369 (errmsg("trying another address for the statistics collector")));
00370
00371
00372
00373
00374 if ((pgStatSock = socket(addr->ai_family, SOCK_DGRAM, 0)) == PGINVALID_SOCKET)
00375 {
00376 ereport(LOG,
00377 (errcode_for_socket_access(),
00378 errmsg("could not create socket for statistics collector: %m")));
00379 continue;
00380 }
00381
00382
00383
00384
00385
00386 if (bind(pgStatSock, addr->ai_addr, addr->ai_addrlen) < 0)
00387 {
00388 ereport(LOG,
00389 (errcode_for_socket_access(),
00390 errmsg("could not bind socket for statistics collector: %m")));
00391 closesocket(pgStatSock);
00392 pgStatSock = PGINVALID_SOCKET;
00393 continue;
00394 }
00395
00396 alen = sizeof(pgStatAddr);
00397 if (getsockname(pgStatSock, (struct sockaddr *) & pgStatAddr, &alen) < 0)
00398 {
00399 ereport(LOG,
00400 (errcode_for_socket_access(),
00401 errmsg("could not get address of socket for statistics collector: %m")));
00402 closesocket(pgStatSock);
00403 pgStatSock = PGINVALID_SOCKET;
00404 continue;
00405 }
00406
00407
00408
00409
00410
00411
00412
00413 if (connect(pgStatSock, (struct sockaddr *) & pgStatAddr, alen) < 0)
00414 {
00415 ereport(LOG,
00416 (errcode_for_socket_access(),
00417 errmsg("could not connect socket for statistics collector: %m")));
00418 closesocket(pgStatSock);
00419 pgStatSock = PGINVALID_SOCKET;
00420 continue;
00421 }
00422
00423
00424
00425
00426
00427
00428
00429 test_byte = TESTBYTEVAL;
00430
00431 retry1:
00432 if (send(pgStatSock, &test_byte, 1, 0) != 1)
00433 {
00434 if (errno == EINTR)
00435 goto retry1;
00436 ereport(LOG,
00437 (errcode_for_socket_access(),
00438 errmsg("could not send test message on socket for statistics collector: %m")));
00439 closesocket(pgStatSock);
00440 pgStatSock = PGINVALID_SOCKET;
00441 continue;
00442 }
00443
00444
00445
00446
00447
00448
00449 for (;;)
00450 {
00451 FD_ZERO(&rset);
00452 FD_SET(pgStatSock, &rset);
00453
00454 tv.tv_sec = 0;
00455 tv.tv_usec = 500000;
00456 sel_res = select(pgStatSock + 1, &rset, NULL, NULL, &tv);
00457 if (sel_res >= 0 || errno != EINTR)
00458 break;
00459 }
00460 if (sel_res < 0)
00461 {
00462 ereport(LOG,
00463 (errcode_for_socket_access(),
00464 errmsg("select() failed in statistics collector: %m")));
00465 closesocket(pgStatSock);
00466 pgStatSock = PGINVALID_SOCKET;
00467 continue;
00468 }
00469 if (sel_res == 0 || !FD_ISSET(pgStatSock, &rset))
00470 {
00471
00472
00473
00474
00475
00476
00477 ereport(LOG,
00478 (errcode(ERRCODE_CONNECTION_FAILURE),
00479 errmsg("test message did not get through on socket for statistics collector")));
00480 closesocket(pgStatSock);
00481 pgStatSock = PGINVALID_SOCKET;
00482 continue;
00483 }
00484
00485 test_byte++;
00486
00487 retry2:
00488 if (recv(pgStatSock, &test_byte, 1, 0) != 1)
00489 {
00490 if (errno == EINTR)
00491 goto retry2;
00492 ereport(LOG,
00493 (errcode_for_socket_access(),
00494 errmsg("could not receive test message on socket for statistics collector: %m")));
00495 closesocket(pgStatSock);
00496 pgStatSock = PGINVALID_SOCKET;
00497 continue;
00498 }
00499
00500 if (test_byte != TESTBYTEVAL)
00501 {
00502 ereport(LOG,
00503 (errcode(ERRCODE_INTERNAL_ERROR),
00504 errmsg("incorrect test message transmission on socket for statistics collector")));
00505 closesocket(pgStatSock);
00506 pgStatSock = PGINVALID_SOCKET;
00507 continue;
00508 }
00509
00510
00511 break;
00512 }
00513
00514
00515 if (!addr || pgStatSock == PGINVALID_SOCKET)
00516 goto startup_failed;
00517
00518
00519
00520
00521
00522
00523 if (!pg_set_noblock(pgStatSock))
00524 {
00525 ereport(LOG,
00526 (errcode_for_socket_access(),
00527 errmsg("could not set statistics collector socket to nonblocking mode: %m")));
00528 goto startup_failed;
00529 }
00530
00531 pg_freeaddrinfo_all(hints.ai_family, addrs);
00532
00533 return;
00534
00535 startup_failed:
00536 ereport(LOG,
00537 (errmsg("disabling statistics collector for lack of working socket")));
00538
00539 if (addrs)
00540 pg_freeaddrinfo_all(hints.ai_family, addrs);
00541
00542 if (pgStatSock != PGINVALID_SOCKET)
00543 closesocket(pgStatSock);
00544 pgStatSock = PGINVALID_SOCKET;
00545
00546
00547
00548
00549
00550
00551
00552 SetConfigOption("track_counts", "off", PGC_INTERNAL, PGC_S_OVERRIDE);
00553 }
00554
00555
00556
00557
00558 static void
00559 pgstat_reset_remove_files(const char *directory)
00560 {
00561 DIR *dir;
00562 struct dirent *entry;
00563 char fname[MAXPGPATH];
00564
00565 dir = AllocateDir(pgstat_stat_directory);
00566 while ((entry = ReadDir(dir, pgstat_stat_directory)) != NULL)
00567 {
00568 if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0)
00569 continue;
00570
00571
00572
00573 snprintf(fname, MAXPGPATH, "%s/%s", pgstat_stat_directory,
00574 entry->d_name);
00575 unlink(fname);
00576 }
00577 FreeDir(dir);
00578 }
00579
00580
00581
00582
00583
00584
00585
00586 void
00587 pgstat_reset_all(void)
00588 {
00589 pgstat_reset_remove_files(pgstat_stat_directory);
00590 pgstat_reset_remove_files(PGSTAT_STAT_PERMANENT_DIRECTORY);
00591 }
00592
00593 #ifdef EXEC_BACKEND
00594
00595
00596
00597
00598
00599
00600 static pid_t
00601 pgstat_forkexec(void)
00602 {
00603 char *av[10];
00604 int ac = 0;
00605
00606 av[ac++] = "postgres";
00607 av[ac++] = "--forkcol";
00608 av[ac++] = NULL;
00609
00610 av[ac] = NULL;
00611 Assert(ac < lengthof(av));
00612
00613 return postmaster_forkexec(ac, av);
00614 }
00615 #endif
00616
00617
00618
00619
00620
00621
00622
00623
00624
00625
00626
00627
00628 int
00629 pgstat_start(void)
00630 {
00631 time_t curtime;
00632 pid_t pgStatPid;
00633
00634
00635
00636
00637
00638 if (pgStatSock == PGINVALID_SOCKET)
00639 return 0;
00640
00641
00642
00643
00644
00645
00646
00647 curtime = time(NULL);
00648 if ((unsigned int) (curtime - last_pgstat_start_time) <
00649 (unsigned int) PGSTAT_RESTART_INTERVAL)
00650 return 0;
00651 last_pgstat_start_time = curtime;
00652
00653
00654
00655
00656 #ifdef EXEC_BACKEND
00657 switch ((pgStatPid = pgstat_forkexec()))
00658 #else
00659 switch ((pgStatPid = fork_process()))
00660 #endif
00661 {
00662 case -1:
00663 ereport(LOG,
00664 (errmsg("could not fork statistics collector: %m")));
00665 return 0;
00666
00667 #ifndef EXEC_BACKEND
00668 case 0:
00669
00670
00671 ClosePostmasterPorts(false);
00672
00673
00674 on_exit_reset();
00675
00676
00677 PGSharedMemoryDetach();
00678
00679 PgstatCollectorMain(0, NULL);
00680 break;
00681 #endif
00682
00683 default:
00684 return (int) pgStatPid;
00685 }
00686
00687
00688 return 0;
00689 }
00690
00691 void
00692 allow_immediate_pgstat_restart(void)
00693 {
00694 last_pgstat_start_time = 0;
00695 }
00696
00697
00698
00699
00700
00701
00702
00703
00704
00705
00706
00707
00708
00709
00710
00711
00712 void
00713 pgstat_report_stat(bool force)
00714 {
00715
00716 static const PgStat_TableCounts all_zeroes;
00717 static TimestampTz last_report = 0;
00718
00719 TimestampTz now;
00720 PgStat_MsgTabstat regular_msg;
00721 PgStat_MsgTabstat shared_msg;
00722 TabStatusArray *tsa;
00723 int i;
00724
00725
00726 if ((pgStatTabList == NULL || pgStatTabList->tsa_used == 0) &&
00727 !have_function_stats && !force)
00728 return;
00729
00730
00731
00732
00733
00734 now = GetCurrentTransactionStopTimestamp();
00735 if (!force &&
00736 !TimestampDifferenceExceeds(last_report, now, PGSTAT_STAT_INTERVAL))
00737 return;
00738 last_report = now;
00739
00740
00741
00742
00743
00744
00745
00746 regular_msg.m_databaseid = MyDatabaseId;
00747 shared_msg.m_databaseid = InvalidOid;
00748 regular_msg.m_nentries = 0;
00749 shared_msg.m_nentries = 0;
00750
00751 for (tsa = pgStatTabList; tsa != NULL; tsa = tsa->tsa_next)
00752 {
00753 for (i = 0; i < tsa->tsa_used; i++)
00754 {
00755 PgStat_TableStatus *entry = &tsa->tsa_entries[i];
00756 PgStat_MsgTabstat *this_msg;
00757 PgStat_TableEntry *this_ent;
00758
00759
00760 Assert(entry->trans == NULL);
00761
00762
00763
00764
00765
00766 if (memcmp(&entry->t_counts, &all_zeroes,
00767 sizeof(PgStat_TableCounts)) == 0)
00768 continue;
00769
00770
00771
00772
00773 this_msg = entry->t_shared ? &shared_msg : ®ular_msg;
00774 this_ent = &this_msg->m_entry[this_msg->m_nentries];
00775 this_ent->t_id = entry->t_id;
00776 memcpy(&this_ent->t_counts, &entry->t_counts,
00777 sizeof(PgStat_TableCounts));
00778 if (++this_msg->m_nentries >= PGSTAT_NUM_TABENTRIES)
00779 {
00780 pgstat_send_tabstat(this_msg);
00781 this_msg->m_nentries = 0;
00782 }
00783 }
00784
00785 MemSet(tsa->tsa_entries, 0,
00786 tsa->tsa_used * sizeof(PgStat_TableStatus));
00787 tsa->tsa_used = 0;
00788 }
00789
00790
00791
00792
00793
00794 if (regular_msg.m_nentries > 0 ||
00795 (force && (pgStatXactCommit > 0 || pgStatXactRollback > 0)))
00796 pgstat_send_tabstat(®ular_msg);
00797 if (shared_msg.m_nentries > 0)
00798 pgstat_send_tabstat(&shared_msg);
00799
00800
00801 pgstat_send_funcstats();
00802 }
00803
00804
00805
00806
00807 static void
00808 pgstat_send_tabstat(PgStat_MsgTabstat *tsmsg)
00809 {
00810 int n;
00811 int len;
00812
00813
00814 if (pgStatSock == PGINVALID_SOCKET)
00815 return;
00816
00817
00818
00819
00820
00821 if (OidIsValid(tsmsg->m_databaseid))
00822 {
00823 tsmsg->m_xact_commit = pgStatXactCommit;
00824 tsmsg->m_xact_rollback = pgStatXactRollback;
00825 tsmsg->m_block_read_time = pgStatBlockReadTime;
00826 tsmsg->m_block_write_time = pgStatBlockWriteTime;
00827 pgStatXactCommit = 0;
00828 pgStatXactRollback = 0;
00829 pgStatBlockReadTime = 0;
00830 pgStatBlockWriteTime = 0;
00831 }
00832 else
00833 {
00834 tsmsg->m_xact_commit = 0;
00835 tsmsg->m_xact_rollback = 0;
00836 tsmsg->m_block_read_time = 0;
00837 tsmsg->m_block_write_time = 0;
00838 }
00839
00840 n = tsmsg->m_nentries;
00841 len = offsetof(PgStat_MsgTabstat, m_entry[0]) +
00842 n * sizeof(PgStat_TableEntry);
00843
00844 pgstat_setheader(&tsmsg->m_hdr, PGSTAT_MTYPE_TABSTAT);
00845 pgstat_send(tsmsg, len);
00846 }
00847
00848
00849
00850
00851 static void
00852 pgstat_send_funcstats(void)
00853 {
00854
00855 static const PgStat_FunctionCounts all_zeroes;
00856
00857 PgStat_MsgFuncstat msg;
00858 PgStat_BackendFunctionEntry *entry;
00859 HASH_SEQ_STATUS fstat;
00860
00861 if (pgStatFunctions == NULL)
00862 return;
00863
00864 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_FUNCSTAT);
00865 msg.m_databaseid = MyDatabaseId;
00866 msg.m_nentries = 0;
00867
00868 hash_seq_init(&fstat, pgStatFunctions);
00869 while ((entry = (PgStat_BackendFunctionEntry *) hash_seq_search(&fstat)) != NULL)
00870 {
00871 PgStat_FunctionEntry *m_ent;
00872
00873
00874 if (memcmp(&entry->f_counts, &all_zeroes,
00875 sizeof(PgStat_FunctionCounts)) == 0)
00876 continue;
00877
00878
00879 m_ent = &msg.m_entry[msg.m_nentries];
00880 m_ent->f_id = entry->f_id;
00881 m_ent->f_numcalls = entry->f_counts.f_numcalls;
00882 m_ent->f_total_time = INSTR_TIME_GET_MICROSEC(entry->f_counts.f_total_time);
00883 m_ent->f_self_time = INSTR_TIME_GET_MICROSEC(entry->f_counts.f_self_time);
00884
00885 if (++msg.m_nentries >= PGSTAT_NUM_FUNCENTRIES)
00886 {
00887 pgstat_send(&msg, offsetof(PgStat_MsgFuncstat, m_entry[0]) +
00888 msg.m_nentries * sizeof(PgStat_FunctionEntry));
00889 msg.m_nentries = 0;
00890 }
00891
00892
00893 MemSet(&entry->f_counts, 0, sizeof(PgStat_FunctionCounts));
00894 }
00895
00896 if (msg.m_nentries > 0)
00897 pgstat_send(&msg, offsetof(PgStat_MsgFuncstat, m_entry[0]) +
00898 msg.m_nentries * sizeof(PgStat_FunctionEntry));
00899
00900 have_function_stats = false;
00901 }
00902
00903
00904
00905
00906
00907
00908
00909
00910 void
00911 pgstat_vacuum_stat(void)
00912 {
00913 HTAB *htab;
00914 PgStat_MsgTabpurge msg;
00915 PgStat_MsgFuncpurge f_msg;
00916 HASH_SEQ_STATUS hstat;
00917 PgStat_StatDBEntry *dbentry;
00918 PgStat_StatTabEntry *tabentry;
00919 PgStat_StatFuncEntry *funcentry;
00920 int len;
00921
00922 if (pgStatSock == PGINVALID_SOCKET)
00923 return;
00924
00925
00926
00927
00928
00929 backend_read_statsfile();
00930
00931
00932
00933
00934 htab = pgstat_collect_oids(DatabaseRelationId);
00935
00936
00937
00938
00939
00940 hash_seq_init(&hstat, pgStatDBHash);
00941 while ((dbentry = (PgStat_StatDBEntry *) hash_seq_search(&hstat)) != NULL)
00942 {
00943 Oid dbid = dbentry->databaseid;
00944
00945 CHECK_FOR_INTERRUPTS();
00946
00947
00948 if (OidIsValid(dbid) &&
00949 hash_search(htab, (void *) &dbid, HASH_FIND, NULL) == NULL)
00950 pgstat_drop_database(dbid);
00951 }
00952
00953
00954 hash_destroy(htab);
00955
00956
00957
00958
00959 dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash,
00960 (void *) &MyDatabaseId,
00961 HASH_FIND, NULL);
00962 if (dbentry == NULL || dbentry->tables == NULL)
00963 return;
00964
00965
00966
00967
00968 htab = pgstat_collect_oids(RelationRelationId);
00969
00970
00971
00972
00973 msg.m_nentries = 0;
00974
00975
00976
00977
00978 hash_seq_init(&hstat, dbentry->tables);
00979 while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&hstat)) != NULL)
00980 {
00981 Oid tabid = tabentry->tableid;
00982
00983 CHECK_FOR_INTERRUPTS();
00984
00985 if (hash_search(htab, (void *) &tabid, HASH_FIND, NULL) != NULL)
00986 continue;
00987
00988
00989
00990
00991 msg.m_tableid[msg.m_nentries++] = tabid;
00992
00993
00994
00995
00996 if (msg.m_nentries >= PGSTAT_NUM_TABPURGE)
00997 {
00998 len = offsetof(PgStat_MsgTabpurge, m_tableid[0])
00999 +msg.m_nentries * sizeof(Oid);
01000
01001 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE);
01002 msg.m_databaseid = MyDatabaseId;
01003 pgstat_send(&msg, len);
01004
01005 msg.m_nentries = 0;
01006 }
01007 }
01008
01009
01010
01011
01012 if (msg.m_nentries > 0)
01013 {
01014 len = offsetof(PgStat_MsgTabpurge, m_tableid[0])
01015 +msg.m_nentries * sizeof(Oid);
01016
01017 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE);
01018 msg.m_databaseid = MyDatabaseId;
01019 pgstat_send(&msg, len);
01020 }
01021
01022
01023 hash_destroy(htab);
01024
01025
01026
01027
01028
01029 if (dbentry->functions != NULL &&
01030 hash_get_num_entries(dbentry->functions) > 0)
01031 {
01032 htab = pgstat_collect_oids(ProcedureRelationId);
01033
01034 pgstat_setheader(&f_msg.m_hdr, PGSTAT_MTYPE_FUNCPURGE);
01035 f_msg.m_databaseid = MyDatabaseId;
01036 f_msg.m_nentries = 0;
01037
01038 hash_seq_init(&hstat, dbentry->functions);
01039 while ((funcentry = (PgStat_StatFuncEntry *) hash_seq_search(&hstat)) != NULL)
01040 {
01041 Oid funcid = funcentry->functionid;
01042
01043 CHECK_FOR_INTERRUPTS();
01044
01045 if (hash_search(htab, (void *) &funcid, HASH_FIND, NULL) != NULL)
01046 continue;
01047
01048
01049
01050
01051 f_msg.m_functionid[f_msg.m_nentries++] = funcid;
01052
01053
01054
01055
01056 if (f_msg.m_nentries >= PGSTAT_NUM_FUNCPURGE)
01057 {
01058 len = offsetof(PgStat_MsgFuncpurge, m_functionid[0])
01059 +f_msg.m_nentries * sizeof(Oid);
01060
01061 pgstat_send(&f_msg, len);
01062
01063 f_msg.m_nentries = 0;
01064 }
01065 }
01066
01067
01068
01069
01070 if (f_msg.m_nentries > 0)
01071 {
01072 len = offsetof(PgStat_MsgFuncpurge, m_functionid[0])
01073 +f_msg.m_nentries * sizeof(Oid);
01074
01075 pgstat_send(&f_msg, len);
01076 }
01077
01078 hash_destroy(htab);
01079 }
01080 }
01081
01082
01083
01084
01085
01086
01087
01088
01089
01090
01091
01092 static HTAB *
01093 pgstat_collect_oids(Oid catalogid)
01094 {
01095 HTAB *htab;
01096 HASHCTL hash_ctl;
01097 Relation rel;
01098 HeapScanDesc scan;
01099 HeapTuple tup;
01100
01101 memset(&hash_ctl, 0, sizeof(hash_ctl));
01102 hash_ctl.keysize = sizeof(Oid);
01103 hash_ctl.entrysize = sizeof(Oid);
01104 hash_ctl.hash = oid_hash;
01105 hash_ctl.hcxt = CurrentMemoryContext;
01106 htab = hash_create("Temporary table of OIDs",
01107 PGSTAT_TAB_HASH_SIZE,
01108 &hash_ctl,
01109 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
01110
01111 rel = heap_open(catalogid, AccessShareLock);
01112 scan = heap_beginscan(rel, SnapshotNow, 0, NULL);
01113 while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL)
01114 {
01115 Oid thisoid = HeapTupleGetOid(tup);
01116
01117 CHECK_FOR_INTERRUPTS();
01118
01119 (void) hash_search(htab, (void *) &thisoid, HASH_ENTER, NULL);
01120 }
01121 heap_endscan(scan);
01122 heap_close(rel, AccessShareLock);
01123
01124 return htab;
01125 }
01126
01127
01128
01129
01130
01131
01132
01133
01134
01135
01136 void
01137 pgstat_drop_database(Oid databaseid)
01138 {
01139 PgStat_MsgDropdb msg;
01140
01141 if (pgStatSock == PGINVALID_SOCKET)
01142 return;
01143
01144 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_DROPDB);
01145 msg.m_databaseid = databaseid;
01146 pgstat_send(&msg, sizeof(msg));
01147 }
01148
01149
01150
01151
01152
01153
01154
01155
01156
01157
01158
01159
01160
01161 #ifdef NOT_USED
01162 void
01163 pgstat_drop_relation(Oid relid)
01164 {
01165 PgStat_MsgTabpurge msg;
01166 int len;
01167
01168 if (pgStatSock == PGINVALID_SOCKET)
01169 return;
01170
01171 msg.m_tableid[0] = relid;
01172 msg.m_nentries = 1;
01173
01174 len = offsetof(PgStat_MsgTabpurge, m_tableid[0]) +sizeof(Oid);
01175
01176 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE);
01177 msg.m_databaseid = MyDatabaseId;
01178 pgstat_send(&msg, len);
01179 }
01180 #endif
01181
01182
01183
01184
01185
01186
01187
01188
01189 void
01190 pgstat_reset_counters(void)
01191 {
01192 PgStat_MsgResetcounter msg;
01193
01194 if (pgStatSock == PGINVALID_SOCKET)
01195 return;
01196
01197 if (!superuser())
01198 ereport(ERROR,
01199 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01200 errmsg("must be superuser to reset statistics counters")));
01201
01202 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETCOUNTER);
01203 msg.m_databaseid = MyDatabaseId;
01204 pgstat_send(&msg, sizeof(msg));
01205 }
01206
01207
01208
01209
01210
01211
01212
01213 void
01214 pgstat_reset_shared_counters(const char *target)
01215 {
01216 PgStat_MsgResetsharedcounter msg;
01217
01218 if (pgStatSock == PGINVALID_SOCKET)
01219 return;
01220
01221 if (!superuser())
01222 ereport(ERROR,
01223 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01224 errmsg("must be superuser to reset statistics counters")));
01225
01226 if (strcmp(target, "bgwriter") == 0)
01227 msg.m_resettarget = RESET_BGWRITER;
01228 else
01229 ereport(ERROR,
01230 (errcode(ERRCODE_INVALID_PARAMETER_VALUE),
01231 errmsg("unrecognized reset target: \"%s\"", target),
01232 errhint("Target must be \"bgwriter\".")));
01233
01234 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSHAREDCOUNTER);
01235 pgstat_send(&msg, sizeof(msg));
01236 }
01237
01238
01239
01240
01241
01242
01243
01244 void
01245 pgstat_reset_single_counter(Oid objoid, PgStat_Single_Reset_Type type)
01246 {
01247 PgStat_MsgResetsinglecounter msg;
01248
01249 if (pgStatSock == PGINVALID_SOCKET)
01250 return;
01251
01252 if (!superuser())
01253 ereport(ERROR,
01254 (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
01255 errmsg("must be superuser to reset statistics counters")));
01256
01257 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETSINGLECOUNTER);
01258 msg.m_databaseid = MyDatabaseId;
01259 msg.m_resettype = type;
01260 msg.m_objectid = objoid;
01261
01262 pgstat_send(&msg, sizeof(msg));
01263 }
01264
01265
01266
01267
01268
01269
01270
01271
01272
01273 void
01274 pgstat_report_autovac(Oid dboid)
01275 {
01276 PgStat_MsgAutovacStart msg;
01277
01278 if (pgStatSock == PGINVALID_SOCKET)
01279 return;
01280
01281 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_AUTOVAC_START);
01282 msg.m_databaseid = dboid;
01283 msg.m_start_time = GetCurrentTimestamp();
01284
01285 pgstat_send(&msg, sizeof(msg));
01286 }
01287
01288
01289
01290
01291
01292
01293
01294
01295 void
01296 pgstat_report_vacuum(Oid tableoid, bool shared, PgStat_Counter tuples)
01297 {
01298 PgStat_MsgVacuum msg;
01299
01300 if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
01301 return;
01302
01303 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM);
01304 msg.m_databaseid = shared ? InvalidOid : MyDatabaseId;
01305 msg.m_tableoid = tableoid;
01306 msg.m_autovacuum = IsAutoVacuumWorkerProcess();
01307 msg.m_vacuumtime = GetCurrentTimestamp();
01308 msg.m_tuples = tuples;
01309 pgstat_send(&msg, sizeof(msg));
01310 }
01311
01312
01313
01314
01315
01316
01317
01318 void
01319 pgstat_report_analyze(Relation rel,
01320 PgStat_Counter livetuples, PgStat_Counter deadtuples)
01321 {
01322 PgStat_MsgAnalyze msg;
01323
01324 if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
01325 return;
01326
01327
01328
01329
01330
01331
01332
01333
01334
01335
01336
01337 if (rel->pgstat_info != NULL)
01338 {
01339 PgStat_TableXactStatus *trans;
01340
01341 for (trans = rel->pgstat_info->trans; trans; trans = trans->upper)
01342 {
01343 livetuples -= trans->tuples_inserted - trans->tuples_deleted;
01344 deadtuples -= trans->tuples_updated + trans->tuples_deleted;
01345 }
01346
01347 deadtuples -= rel->pgstat_info->t_counts.t_delta_dead_tuples;
01348
01349 livetuples = Max(livetuples, 0);
01350 deadtuples = Max(deadtuples, 0);
01351 }
01352
01353 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE);
01354 msg.m_databaseid = rel->rd_rel->relisshared ? InvalidOid : MyDatabaseId;
01355 msg.m_tableoid = RelationGetRelid(rel);
01356 msg.m_autovacuum = IsAutoVacuumWorkerProcess();
01357 msg.m_analyzetime = GetCurrentTimestamp();
01358 msg.m_live_tuples = livetuples;
01359 msg.m_dead_tuples = deadtuples;
01360 pgstat_send(&msg, sizeof(msg));
01361 }
01362
01363
01364
01365
01366
01367
01368
01369 void
01370 pgstat_report_recovery_conflict(int reason)
01371 {
01372 PgStat_MsgRecoveryConflict msg;
01373
01374 if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
01375 return;
01376
01377 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RECOVERYCONFLICT);
01378 msg.m_databaseid = MyDatabaseId;
01379 msg.m_reason = reason;
01380 pgstat_send(&msg, sizeof(msg));
01381 }
01382
01383
01384
01385
01386
01387
01388
01389 void
01390 pgstat_report_deadlock(void)
01391 {
01392 PgStat_MsgDeadlock msg;
01393
01394 if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
01395 return;
01396
01397 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_DEADLOCK);
01398 msg.m_databaseid = MyDatabaseId;
01399 pgstat_send(&msg, sizeof(msg));
01400 }
01401
01402
01403
01404
01405
01406
01407
01408 void
01409 pgstat_report_tempfile(size_t filesize)
01410 {
01411 PgStat_MsgTempFile msg;
01412
01413 if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
01414 return;
01415
01416 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TEMPFILE);
01417 msg.m_databaseid = MyDatabaseId;
01418 msg.m_filesize = filesize;
01419 pgstat_send(&msg, sizeof(msg));
01420 }
01421
01422
01423
01424
01425
01426
01427
01428
01429 void
01430 pgstat_ping(void)
01431 {
01432 PgStat_MsgDummy msg;
01433
01434 if (pgStatSock == PGINVALID_SOCKET)
01435 return;
01436
01437 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_DUMMY);
01438 pgstat_send(&msg, sizeof(msg));
01439 }
01440
01441
01442
01443
01444
01445
01446
01447 static void
01448 pgstat_send_inquiry(TimestampTz clock_time, TimestampTz cutoff_time, Oid databaseid)
01449 {
01450 PgStat_MsgInquiry msg;
01451
01452 pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_INQUIRY);
01453 msg.clock_time = clock_time;
01454 msg.cutoff_time = cutoff_time;
01455 msg.databaseid = databaseid;
01456 pgstat_send(&msg, sizeof(msg));
01457 }
01458
01459
01460
01461
01462
01463
01464 void
01465 pgstat_init_function_usage(FunctionCallInfoData *fcinfo,
01466 PgStat_FunctionCallUsage *fcu)
01467 {
01468 PgStat_BackendFunctionEntry *htabent;
01469 bool found;
01470
01471 if (pgstat_track_functions <= fcinfo->flinfo->fn_stats)
01472 {
01473
01474 fcu->fs = NULL;
01475 return;
01476 }
01477
01478 if (!pgStatFunctions)
01479 {
01480
01481 HASHCTL hash_ctl;
01482
01483 memset(&hash_ctl, 0, sizeof(hash_ctl));
01484 hash_ctl.keysize = sizeof(Oid);
01485 hash_ctl.entrysize = sizeof(PgStat_BackendFunctionEntry);
01486 hash_ctl.hash = oid_hash;
01487 pgStatFunctions = hash_create("Function stat entries",
01488 PGSTAT_FUNCTION_HASH_SIZE,
01489 &hash_ctl,
01490 HASH_ELEM | HASH_FUNCTION);
01491 }
01492
01493
01494 htabent = hash_search(pgStatFunctions, &fcinfo->flinfo->fn_oid,
01495 HASH_ENTER, &found);
01496 if (!found)
01497 MemSet(&htabent->f_counts, 0, sizeof(PgStat_FunctionCounts));
01498
01499 fcu->fs = &htabent->f_counts;
01500
01501
01502 fcu->save_f_total_time = htabent->f_counts.f_total_time;
01503
01504
01505 fcu->save_total = total_func_time;
01506
01507
01508 INSTR_TIME_SET_CURRENT(fcu->f_start);
01509 }
01510
01511
01512
01513
01514
01515
01516
01517 PgStat_BackendFunctionEntry *
01518 find_funcstat_entry(Oid func_id)
01519 {
01520 if (pgStatFunctions == NULL)
01521 return NULL;
01522
01523 return (PgStat_BackendFunctionEntry *) hash_search(pgStatFunctions,
01524 (void *) &func_id,
01525 HASH_FIND, NULL);
01526 }
01527
01528
01529
01530
01531
01532
01533
01534
01535
01536
01537 void
01538 pgstat_end_function_usage(PgStat_FunctionCallUsage *fcu, bool finalize)
01539 {
01540 PgStat_FunctionCounts *fs = fcu->fs;
01541 instr_time f_total;
01542 instr_time f_others;
01543 instr_time f_self;
01544
01545
01546 if (fs == NULL)
01547 return;
01548
01549
01550 INSTR_TIME_SET_CURRENT(f_total);
01551 INSTR_TIME_SUBTRACT(f_total, fcu->f_start);
01552
01553
01554 f_others = total_func_time;
01555 INSTR_TIME_SUBTRACT(f_others, fcu->save_total);
01556 f_self = f_total;
01557 INSTR_TIME_SUBTRACT(f_self, f_others);
01558
01559
01560 INSTR_TIME_ADD(total_func_time, f_self);
01561
01562
01563
01564
01565
01566
01567
01568
01569 INSTR_TIME_ADD(f_total, fcu->save_f_total_time);
01570
01571
01572 if (finalize)
01573 fs->f_numcalls++;
01574 fs->f_total_time = f_total;
01575 INSTR_TIME_ADD(fs->f_self_time, f_self);
01576
01577
01578 have_function_stats = true;
01579 }
01580
01581
01582
01583
01584
01585
01586
01587
01588
01589
01590
01591
01592
01593
01594 void
01595 pgstat_initstats(Relation rel)
01596 {
01597 Oid rel_id = rel->rd_id;
01598 char relkind = rel->rd_rel->relkind;
01599
01600
01601 if (!(relkind == RELKIND_RELATION ||
01602 relkind == RELKIND_MATVIEW ||
01603 relkind == RELKIND_INDEX ||
01604 relkind == RELKIND_TOASTVALUE ||
01605 relkind == RELKIND_SEQUENCE))
01606 {
01607 rel->pgstat_info = NULL;
01608 return;
01609 }
01610
01611 if (pgStatSock == PGINVALID_SOCKET || !pgstat_track_counts)
01612 {
01613
01614 rel->pgstat_info = NULL;
01615 return;
01616 }
01617
01618
01619
01620
01621
01622 if (rel->pgstat_info != NULL &&
01623 rel->pgstat_info->t_id == rel_id)
01624 return;
01625
01626
01627 rel->pgstat_info = get_tabstat_entry(rel_id, rel->rd_rel->relisshared);
01628 }
01629
01630
01631
01632
01633 static PgStat_TableStatus *
01634 get_tabstat_entry(Oid rel_id, bool isshared)
01635 {
01636 PgStat_TableStatus *entry;
01637 TabStatusArray *tsa;
01638 TabStatusArray *prev_tsa;
01639 int i;
01640
01641
01642
01643
01644 prev_tsa = NULL;
01645 for (tsa = pgStatTabList; tsa != NULL; prev_tsa = tsa, tsa = tsa->tsa_next)
01646 {
01647 for (i = 0; i < tsa->tsa_used; i++)
01648 {
01649 entry = &tsa->tsa_entries[i];
01650 if (entry->t_id == rel_id)
01651 return entry;
01652 }
01653
01654 if (tsa->tsa_used < TABSTAT_QUANTUM)
01655 {
01656
01657
01658
01659
01660
01661 entry = &tsa->tsa_entries[tsa->tsa_used++];
01662 entry->t_id = rel_id;
01663 entry->t_shared = isshared;
01664 return entry;
01665 }
01666 }
01667
01668
01669
01670
01671 tsa = (TabStatusArray *) MemoryContextAllocZero(TopMemoryContext,
01672 sizeof(TabStatusArray));
01673 if (prev_tsa)
01674 prev_tsa->tsa_next = tsa;
01675 else
01676 pgStatTabList = tsa;
01677
01678
01679
01680
01681 entry = &tsa->tsa_entries[tsa->tsa_used++];
01682 entry->t_id = rel_id;
01683 entry->t_shared = isshared;
01684 return entry;
01685 }
01686
01687
01688
01689
01690
01691
01692 PgStat_TableStatus *
01693 find_tabstat_entry(Oid rel_id)
01694 {
01695 PgStat_TableStatus *entry;
01696 TabStatusArray *tsa;
01697 int i;
01698
01699 for (tsa = pgStatTabList; tsa != NULL; tsa = tsa->tsa_next)
01700 {
01701 for (i = 0; i < tsa->tsa_used; i++)
01702 {
01703 entry = &tsa->tsa_entries[i];
01704 if (entry->t_id == rel_id)
01705 return entry;
01706 }
01707 }
01708
01709
01710 return NULL;
01711 }
01712
01713
01714
01715
01716 static PgStat_SubXactStatus *
01717 get_tabstat_stack_level(int nest_level)
01718 {
01719 PgStat_SubXactStatus *xact_state;
01720
01721 xact_state = pgStatXactStack;
01722 if (xact_state == NULL || xact_state->nest_level != nest_level)
01723 {
01724 xact_state = (PgStat_SubXactStatus *)
01725 MemoryContextAlloc(TopTransactionContext,
01726 sizeof(PgStat_SubXactStatus));
01727 xact_state->nest_level = nest_level;
01728 xact_state->prev = pgStatXactStack;
01729 xact_state->first = NULL;
01730 pgStatXactStack = xact_state;
01731 }
01732 return xact_state;
01733 }
01734
01735
01736
01737
01738 static void
01739 add_tabstat_xact_level(PgStat_TableStatus *pgstat_info, int nest_level)
01740 {
01741 PgStat_SubXactStatus *xact_state;
01742 PgStat_TableXactStatus *trans;
01743
01744
01745
01746
01747
01748 xact_state = get_tabstat_stack_level(nest_level);
01749
01750
01751 trans = (PgStat_TableXactStatus *)
01752 MemoryContextAllocZero(TopTransactionContext,
01753 sizeof(PgStat_TableXactStatus));
01754 trans->nest_level = nest_level;
01755 trans->upper = pgstat_info->trans;
01756 trans->parent = pgstat_info;
01757 trans->next = xact_state->first;
01758 xact_state->first = trans;
01759 pgstat_info->trans = trans;
01760 }
01761
01762
01763
01764
01765 void
01766 pgstat_count_heap_insert(Relation rel, int n)
01767 {
01768 PgStat_TableStatus *pgstat_info = rel->pgstat_info;
01769
01770 if (pgstat_info != NULL)
01771 {
01772
01773 int nest_level = GetCurrentTransactionNestLevel();
01774
01775 if (pgstat_info->trans == NULL ||
01776 pgstat_info->trans->nest_level != nest_level)
01777 add_tabstat_xact_level(pgstat_info, nest_level);
01778
01779 pgstat_info->trans->tuples_inserted += n;
01780 }
01781 }
01782
01783
01784
01785
01786 void
01787 pgstat_count_heap_update(Relation rel, bool hot)
01788 {
01789 PgStat_TableStatus *pgstat_info = rel->pgstat_info;
01790
01791 if (pgstat_info != NULL)
01792 {
01793
01794 int nest_level = GetCurrentTransactionNestLevel();
01795
01796 if (pgstat_info->trans == NULL ||
01797 pgstat_info->trans->nest_level != nest_level)
01798 add_tabstat_xact_level(pgstat_info, nest_level);
01799
01800 pgstat_info->trans->tuples_updated++;
01801
01802
01803 if (hot)
01804 pgstat_info->t_counts.t_tuples_hot_updated++;
01805 }
01806 }
01807
01808
01809
01810
01811 void
01812 pgstat_count_heap_delete(Relation rel)
01813 {
01814 PgStat_TableStatus *pgstat_info = rel->pgstat_info;
01815
01816 if (pgstat_info != NULL)
01817 {
01818
01819 int nest_level = GetCurrentTransactionNestLevel();
01820
01821 if (pgstat_info->trans == NULL ||
01822 pgstat_info->trans->nest_level != nest_level)
01823 add_tabstat_xact_level(pgstat_info, nest_level);
01824
01825 pgstat_info->trans->tuples_deleted++;
01826 }
01827 }
01828
01829
01830
01831
01832
01833
01834
01835
01836
01837 void
01838 pgstat_update_heap_dead_tuples(Relation rel, int delta)
01839 {
01840 PgStat_TableStatus *pgstat_info = rel->pgstat_info;
01841
01842 if (pgstat_info != NULL)
01843 pgstat_info->t_counts.t_delta_dead_tuples -= delta;
01844 }
01845
01846
01847
01848
01849
01850
01851
01852
01853 void
01854 AtEOXact_PgStat(bool isCommit)
01855 {
01856 PgStat_SubXactStatus *xact_state;
01857
01858
01859
01860
01861
01862 if (isCommit)
01863 pgStatXactCommit++;
01864 else
01865 pgStatXactRollback++;
01866
01867
01868
01869
01870
01871
01872 xact_state = pgStatXactStack;
01873 if (xact_state != NULL)
01874 {
01875 PgStat_TableXactStatus *trans;
01876
01877 Assert(xact_state->nest_level == 1);
01878 Assert(xact_state->prev == NULL);
01879 for (trans = xact_state->first; trans != NULL; trans = trans->next)
01880 {
01881 PgStat_TableStatus *tabstat;
01882
01883 Assert(trans->nest_level == 1);
01884 Assert(trans->upper == NULL);
01885 tabstat = trans->parent;
01886 Assert(tabstat->trans == trans);
01887
01888 tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
01889 tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
01890 tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
01891 if (isCommit)
01892 {
01893
01894 tabstat->t_counts.t_delta_live_tuples +=
01895 trans->tuples_inserted - trans->tuples_deleted;
01896
01897 tabstat->t_counts.t_delta_dead_tuples +=
01898 trans->tuples_updated + trans->tuples_deleted;
01899
01900 tabstat->t_counts.t_changed_tuples +=
01901 trans->tuples_inserted + trans->tuples_updated +
01902 trans->tuples_deleted;
01903 }
01904 else
01905 {
01906
01907 tabstat->t_counts.t_delta_dead_tuples +=
01908 trans->tuples_inserted + trans->tuples_updated;
01909
01910 }
01911 tabstat->trans = NULL;
01912 }
01913 }
01914 pgStatXactStack = NULL;
01915
01916
01917 pgstat_clear_snapshot();
01918 }
01919
01920
01921
01922
01923
01924
01925
01926 void
01927 AtEOSubXact_PgStat(bool isCommit, int nestDepth)
01928 {
01929 PgStat_SubXactStatus *xact_state;
01930
01931
01932
01933
01934
01935 xact_state = pgStatXactStack;
01936 if (xact_state != NULL &&
01937 xact_state->nest_level >= nestDepth)
01938 {
01939 PgStat_TableXactStatus *trans;
01940 PgStat_TableXactStatus *next_trans;
01941
01942
01943 pgStatXactStack = xact_state->prev;
01944
01945 for (trans = xact_state->first; trans != NULL; trans = next_trans)
01946 {
01947 PgStat_TableStatus *tabstat;
01948
01949 next_trans = trans->next;
01950 Assert(trans->nest_level == nestDepth);
01951 tabstat = trans->parent;
01952 Assert(tabstat->trans == trans);
01953 if (isCommit)
01954 {
01955 if (trans->upper && trans->upper->nest_level == nestDepth - 1)
01956 {
01957 trans->upper->tuples_inserted += trans->tuples_inserted;
01958 trans->upper->tuples_updated += trans->tuples_updated;
01959 trans->upper->tuples_deleted += trans->tuples_deleted;
01960 tabstat->trans = trans->upper;
01961 pfree(trans);
01962 }
01963 else
01964 {
01965
01966
01967
01968
01969
01970
01971
01972
01973 PgStat_SubXactStatus *upper_xact_state;
01974
01975 upper_xact_state = get_tabstat_stack_level(nestDepth - 1);
01976 trans->next = upper_xact_state->first;
01977 upper_xact_state->first = trans;
01978 trans->nest_level = nestDepth - 1;
01979 }
01980 }
01981 else
01982 {
01983
01984
01985
01986
01987
01988
01989 tabstat->t_counts.t_tuples_inserted += trans->tuples_inserted;
01990 tabstat->t_counts.t_tuples_updated += trans->tuples_updated;
01991 tabstat->t_counts.t_tuples_deleted += trans->tuples_deleted;
01992
01993 tabstat->t_counts.t_delta_dead_tuples +=
01994 trans->tuples_inserted + trans->tuples_updated;
01995 tabstat->trans = trans->upper;
01996 pfree(trans);
01997 }
01998 }
01999 pfree(xact_state);
02000 }
02001 }
02002
02003
02004
02005
02006
02007
02008
02009
02010
02011 void
02012 AtPrepare_PgStat(void)
02013 {
02014 PgStat_SubXactStatus *xact_state;
02015
02016 xact_state = pgStatXactStack;
02017 if (xact_state != NULL)
02018 {
02019 PgStat_TableXactStatus *trans;
02020
02021 Assert(xact_state->nest_level == 1);
02022 Assert(xact_state->prev == NULL);
02023 for (trans = xact_state->first; trans != NULL; trans = trans->next)
02024 {
02025 PgStat_TableStatus *tabstat;
02026 TwoPhasePgStatRecord record;
02027
02028 Assert(trans->nest_level == 1);
02029 Assert(trans->upper == NULL);
02030 tabstat = trans->parent;
02031 Assert(tabstat->trans == trans);
02032
02033 record.tuples_inserted = trans->tuples_inserted;
02034 record.tuples_updated = trans->tuples_updated;
02035 record.tuples_deleted = trans->tuples_deleted;
02036 record.t_id = tabstat->t_id;
02037 record.t_shared = tabstat->t_shared;
02038
02039 RegisterTwoPhaseRecord(TWOPHASE_RM_PGSTAT_ID, 0,
02040 &record, sizeof(TwoPhasePgStatRecord));
02041 }
02042 }
02043 }
02044
02045
02046
02047
02048
02049
02050
02051
02052
02053
02054
02055
02056 void
02057 PostPrepare_PgStat(void)
02058 {
02059 PgStat_SubXactStatus *xact_state;
02060
02061
02062
02063
02064
02065 xact_state = pgStatXactStack;
02066 if (xact_state != NULL)
02067 {
02068 PgStat_TableXactStatus *trans;
02069
02070 for (trans = xact_state->first; trans != NULL; trans = trans->next)
02071 {
02072 PgStat_TableStatus *tabstat;
02073
02074 tabstat = trans->parent;
02075 tabstat->trans = NULL;
02076 }
02077 }
02078 pgStatXactStack = NULL;
02079
02080
02081 pgstat_clear_snapshot();
02082 }
02083
02084
02085
02086
02087
02088
02089 void
02090 pgstat_twophase_postcommit(TransactionId xid, uint16 info,
02091 void *recdata, uint32 len)
02092 {
02093 TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
02094 PgStat_TableStatus *pgstat_info;
02095
02096
02097 pgstat_info = get_tabstat_entry(rec->t_id, rec->t_shared);
02098
02099
02100 pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
02101 pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
02102 pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
02103 pgstat_info->t_counts.t_delta_live_tuples +=
02104 rec->tuples_inserted - rec->tuples_deleted;
02105 pgstat_info->t_counts.t_delta_dead_tuples +=
02106 rec->tuples_updated + rec->tuples_deleted;
02107 pgstat_info->t_counts.t_changed_tuples +=
02108 rec->tuples_inserted + rec->tuples_updated +
02109 rec->tuples_deleted;
02110 }
02111
02112
02113
02114
02115
02116
02117
02118 void
02119 pgstat_twophase_postabort(TransactionId xid, uint16 info,
02120 void *recdata, uint32 len)
02121 {
02122 TwoPhasePgStatRecord *rec = (TwoPhasePgStatRecord *) recdata;
02123 PgStat_TableStatus *pgstat_info;
02124
02125
02126 pgstat_info = get_tabstat_entry(rec->t_id, rec->t_shared);
02127
02128
02129 pgstat_info->t_counts.t_tuples_inserted += rec->tuples_inserted;
02130 pgstat_info->t_counts.t_tuples_updated += rec->tuples_updated;
02131 pgstat_info->t_counts.t_tuples_deleted += rec->tuples_deleted;
02132 pgstat_info->t_counts.t_delta_dead_tuples +=
02133 rec->tuples_inserted + rec->tuples_updated;
02134 }
02135
02136
02137
02138
02139
02140
02141
02142
02143
02144
02145
02146 PgStat_StatDBEntry *
02147 pgstat_fetch_stat_dbentry(Oid dbid)
02148 {
02149
02150
02151
02152
02153 backend_read_statsfile();
02154
02155
02156
02157
02158 return (PgStat_StatDBEntry *) hash_search(pgStatDBHash,
02159 (void *) &dbid,
02160 HASH_FIND, NULL);
02161 }
02162
02163
02164
02165
02166
02167
02168
02169
02170
02171
02172
02173 PgStat_StatTabEntry *
02174 pgstat_fetch_stat_tabentry(Oid relid)
02175 {
02176 Oid dbid;
02177 PgStat_StatDBEntry *dbentry;
02178 PgStat_StatTabEntry *tabentry;
02179
02180
02181
02182
02183
02184 backend_read_statsfile();
02185
02186
02187
02188
02189 dbid = MyDatabaseId;
02190 dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash,
02191 (void *) &dbid,
02192 HASH_FIND, NULL);
02193 if (dbentry != NULL && dbentry->tables != NULL)
02194 {
02195 tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables,
02196 (void *) &relid,
02197 HASH_FIND, NULL);
02198 if (tabentry)
02199 return tabentry;
02200 }
02201
02202
02203
02204
02205 dbid = InvalidOid;
02206 dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash,
02207 (void *) &dbid,
02208 HASH_FIND, NULL);
02209 if (dbentry != NULL && dbentry->tables != NULL)
02210 {
02211 tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables,
02212 (void *) &relid,
02213 HASH_FIND, NULL);
02214 if (tabentry)
02215 return tabentry;
02216 }
02217
02218 return NULL;
02219 }
02220
02221
02222
02223
02224
02225
02226
02227
02228
02229 PgStat_StatFuncEntry *
02230 pgstat_fetch_stat_funcentry(Oid func_id)
02231 {
02232 PgStat_StatDBEntry *dbentry;
02233 PgStat_StatFuncEntry *funcentry = NULL;
02234
02235
02236 backend_read_statsfile();
02237
02238
02239 dbentry = pgstat_fetch_stat_dbentry(MyDatabaseId);
02240 if (dbentry != NULL && dbentry->functions != NULL)
02241 {
02242 funcentry = (PgStat_StatFuncEntry *) hash_search(dbentry->functions,
02243 (void *) &func_id,
02244 HASH_FIND, NULL);
02245 }
02246
02247 return funcentry;
02248 }
02249
02250
02251
02252
02253
02254
02255
02256
02257
02258
02259
02260
02261 PgBackendStatus *
02262 pgstat_fetch_stat_beentry(int beid)
02263 {
02264 pgstat_read_current_status();
02265
02266 if (beid < 1 || beid > localNumBackends)
02267 return NULL;
02268
02269 return &localBackendStatusTable[beid - 1];
02270 }
02271
02272
02273
02274
02275
02276
02277
02278
02279
02280 int
02281 pgstat_fetch_stat_numbackends(void)
02282 {
02283 pgstat_read_current_status();
02284
02285 return localNumBackends;
02286 }
02287
02288
02289
02290
02291
02292
02293
02294
02295
02296 PgStat_GlobalStats *
02297 pgstat_fetch_global(void)
02298 {
02299 backend_read_statsfile();
02300
02301 return &globalStats;
02302 }
02303
02304
02305
02306
02307
02308
02309
02310 static PgBackendStatus *BackendStatusArray = NULL;
02311 static PgBackendStatus *MyBEEntry = NULL;
02312 static char *BackendClientHostnameBuffer = NULL;
02313 static char *BackendAppnameBuffer = NULL;
02314 static char *BackendActivityBuffer = NULL;
02315 static Size BackendActivityBufferSize = 0;
02316
02317
02318
02319
02320
02321 Size
02322 BackendStatusShmemSize(void)
02323 {
02324 Size size;
02325
02326 size = mul_size(sizeof(PgBackendStatus), MaxBackends);
02327 size = add_size(size,
02328 mul_size(NAMEDATALEN, MaxBackends));
02329 size = add_size(size,
02330 mul_size(pgstat_track_activity_query_size, MaxBackends));
02331 size = add_size(size,
02332 mul_size(NAMEDATALEN, MaxBackends));
02333 return size;
02334 }
02335
02336
02337
02338
02339
02340 void
02341 CreateSharedBackendStatus(void)
02342 {
02343 Size size;
02344 bool found;
02345 int i;
02346 char *buffer;
02347
02348
02349 size = mul_size(sizeof(PgBackendStatus), MaxBackends);
02350 BackendStatusArray = (PgBackendStatus *)
02351 ShmemInitStruct("Backend Status Array", size, &found);
02352
02353 if (!found)
02354 {
02355
02356
02357
02358 MemSet(BackendStatusArray, 0, size);
02359 }
02360
02361
02362 size = mul_size(NAMEDATALEN, MaxBackends);
02363 BackendAppnameBuffer = (char *)
02364 ShmemInitStruct("Backend Application Name Buffer", size, &found);
02365
02366 if (!found)
02367 {
02368 MemSet(BackendAppnameBuffer, 0, size);
02369
02370
02371 buffer = BackendAppnameBuffer;
02372 for (i = 0; i < MaxBackends; i++)
02373 {
02374 BackendStatusArray[i].st_appname = buffer;
02375 buffer += NAMEDATALEN;
02376 }
02377 }
02378
02379
02380 size = mul_size(NAMEDATALEN, MaxBackends);
02381 BackendClientHostnameBuffer = (char *)
02382 ShmemInitStruct("Backend Client Host Name Buffer", size, &found);
02383
02384 if (!found)
02385 {
02386 MemSet(BackendClientHostnameBuffer, 0, size);
02387
02388
02389 buffer = BackendClientHostnameBuffer;
02390 for (i = 0; i < MaxBackends; i++)
02391 {
02392 BackendStatusArray[i].st_clienthostname = buffer;
02393 buffer += NAMEDATALEN;
02394 }
02395 }
02396
02397
02398 BackendActivityBufferSize = mul_size(pgstat_track_activity_query_size,
02399 MaxBackends);
02400 BackendActivityBuffer = (char *)
02401 ShmemInitStruct("Backend Activity Buffer",
02402 BackendActivityBufferSize,
02403 &found);
02404
02405 if (!found)
02406 {
02407 MemSet(BackendActivityBuffer, 0, size);
02408
02409
02410 buffer = BackendActivityBuffer;
02411 for (i = 0; i < MaxBackends; i++)
02412 {
02413 BackendStatusArray[i].st_activity = buffer;
02414 buffer += pgstat_track_activity_query_size;
02415 }
02416 }
02417 }
02418
02419
02420
02421
02422
02423
02424
02425
02426
02427
02428
02429
02430 void
02431 pgstat_initialize(void)
02432 {
02433
02434 Assert(MyBackendId >= 1 && MyBackendId <= MaxBackends);
02435 MyBEEntry = &BackendStatusArray[MyBackendId - 1];
02436
02437
02438 on_shmem_exit(pgstat_beshutdown_hook, 0);
02439 }
02440
02441
02442
02443
02444
02445
02446
02447
02448
02449
02450 void
02451 pgstat_bestart(void)
02452 {
02453 TimestampTz proc_start_timestamp;
02454 Oid userid;
02455 SockAddr clientaddr;
02456 volatile PgBackendStatus *beentry;
02457
02458
02459
02460
02461
02462
02463
02464
02465 if (MyProcPort)
02466 proc_start_timestamp = MyProcPort->SessionStartTime;
02467 else
02468 proc_start_timestamp = GetCurrentTimestamp();
02469 userid = GetSessionUserId();
02470
02471
02472
02473
02474
02475
02476 if (MyProcPort)
02477 memcpy(&clientaddr, &MyProcPort->raddr, sizeof(clientaddr));
02478 else
02479 MemSet(&clientaddr, 0, sizeof(clientaddr));
02480
02481
02482
02483
02484
02485
02486
02487 beentry = MyBEEntry;
02488 do
02489 {
02490 beentry->st_changecount++;
02491 } while ((beentry->st_changecount & 1) == 0);
02492
02493 beentry->st_procpid = MyProcPid;
02494 beentry->st_proc_start_timestamp = proc_start_timestamp;
02495 beentry->st_activity_start_timestamp = 0;
02496 beentry->st_state_start_timestamp = 0;
02497 beentry->st_xact_start_timestamp = 0;
02498 beentry->st_databaseid = MyDatabaseId;
02499 beentry->st_userid = userid;
02500 beentry->st_clientaddr = clientaddr;
02501 beentry->st_clienthostname[0] = '\0';
02502 beentry->st_waiting = false;
02503 beentry->st_state = STATE_UNDEFINED;
02504 beentry->st_appname[0] = '\0';
02505 beentry->st_activity[0] = '\0';
02506
02507 beentry->st_clienthostname[NAMEDATALEN - 1] = '\0';
02508 beentry->st_appname[NAMEDATALEN - 1] = '\0';
02509 beentry->st_activity[pgstat_track_activity_query_size - 1] = '\0';
02510
02511 beentry->st_changecount++;
02512 Assert((beentry->st_changecount & 1) == 0);
02513
02514 if (MyProcPort && MyProcPort->remote_hostname)
02515 strlcpy(beentry->st_clienthostname, MyProcPort->remote_hostname, NAMEDATALEN);
02516
02517
02518 if (application_name)
02519 pgstat_report_appname(application_name);
02520 }
02521
02522
02523
02524
02525
02526
02527
02528
02529
02530
02531 static void
02532 pgstat_beshutdown_hook(int code, Datum arg)
02533 {
02534 volatile PgBackendStatus *beentry = MyBEEntry;
02535
02536
02537
02538
02539
02540
02541
02542 if (OidIsValid(MyDatabaseId))
02543 pgstat_report_stat(true);
02544
02545
02546
02547
02548
02549
02550 beentry->st_changecount++;
02551
02552 beentry->st_procpid = 0;
02553
02554 beentry->st_changecount++;
02555 Assert((beentry->st_changecount & 1) == 0);
02556 }
02557
02558
02559
02560
02561
02562
02563
02564
02565
02566
02567
02568
02569
02570 void
02571 pgstat_report_activity(BackendState state, const char *cmd_str)
02572 {
02573 volatile PgBackendStatus *beentry = MyBEEntry;
02574 TimestampTz start_timestamp;
02575 TimestampTz current_timestamp;
02576 int len = 0;
02577
02578 TRACE_POSTGRESQL_STATEMENT_STATUS(cmd_str);
02579
02580 if (!beentry)
02581 return;
02582
02583 if (!pgstat_track_activities)
02584 {
02585 if (beentry->st_state != STATE_DISABLED)
02586 {
02587
02588
02589
02590
02591
02592 beentry->st_changecount++;
02593 beentry->st_state = STATE_DISABLED;
02594 beentry->st_state_start_timestamp = 0;
02595 beentry->st_activity[0] = '\0';
02596 beentry->st_activity_start_timestamp = 0;
02597
02598 beentry->st_xact_start_timestamp = 0;
02599 beentry->st_waiting = false;
02600 beentry->st_changecount++;
02601 Assert((beentry->st_changecount & 1) == 0);
02602 }
02603 return;
02604 }
02605
02606
02607
02608
02609
02610 start_timestamp = GetCurrentStatementStartTimestamp();
02611 if (cmd_str != NULL)
02612 {
02613 len = pg_mbcliplen(cmd_str, strlen(cmd_str),
02614 pgstat_track_activity_query_size - 1);
02615 }
02616 current_timestamp = GetCurrentTimestamp();
02617
02618
02619
02620
02621 beentry->st_changecount++;
02622
02623 beentry->st_state = state;
02624 beentry->st_state_start_timestamp = current_timestamp;
02625
02626 if (cmd_str != NULL)
02627 {
02628 memcpy((char *) beentry->st_activity, cmd_str, len);
02629 beentry->st_activity[len] = '\0';
02630 beentry->st_activity_start_timestamp = start_timestamp;
02631 }
02632
02633 beentry->st_changecount++;
02634 Assert((beentry->st_changecount & 1) == 0);
02635 }
02636
02637
02638
02639
02640
02641
02642
02643 void
02644 pgstat_report_appname(const char *appname)
02645 {
02646 volatile PgBackendStatus *beentry = MyBEEntry;
02647 int len;
02648
02649 if (!beentry)
02650 return;
02651
02652
02653 len = pg_mbcliplen(appname, strlen(appname), NAMEDATALEN - 1);
02654
02655
02656
02657
02658
02659
02660 beentry->st_changecount++;
02661
02662 memcpy((char *) beentry->st_appname, appname, len);
02663 beentry->st_appname[len] = '\0';
02664
02665 beentry->st_changecount++;
02666 Assert((beentry->st_changecount & 1) == 0);
02667 }
02668
02669
02670
02671
02672
02673 void
02674 pgstat_report_xact_timestamp(TimestampTz tstamp)
02675 {
02676 volatile PgBackendStatus *beentry = MyBEEntry;
02677
02678 if (!pgstat_track_activities || !beentry)
02679 return;
02680
02681
02682
02683
02684
02685
02686 beentry->st_changecount++;
02687 beentry->st_xact_start_timestamp = tstamp;
02688 beentry->st_changecount++;
02689 Assert((beentry->st_changecount & 1) == 0);
02690 }
02691
02692
02693
02694
02695
02696
02697
02698
02699
02700
02701 void
02702 pgstat_report_waiting(bool waiting)
02703 {
02704 volatile PgBackendStatus *beentry = MyBEEntry;
02705
02706 if (!pgstat_track_activities || !beentry)
02707 return;
02708
02709
02710
02711
02712
02713
02714 beentry->st_waiting = waiting;
02715 }
02716
02717
02718
02719
02720
02721
02722
02723
02724
02725 static void
02726 pgstat_read_current_status(void)
02727 {
02728 volatile PgBackendStatus *beentry;
02729 PgBackendStatus *localtable;
02730 PgBackendStatus *localentry;
02731 char *localappname,
02732 *localactivity;
02733 int i;
02734
02735 Assert(!pgStatRunningInCollector);
02736 if (localBackendStatusTable)
02737 return;
02738
02739 pgstat_setup_memcxt();
02740
02741 localtable = (PgBackendStatus *)
02742 MemoryContextAlloc(pgStatLocalContext,
02743 sizeof(PgBackendStatus) * MaxBackends);
02744 localappname = (char *)
02745 MemoryContextAlloc(pgStatLocalContext,
02746 NAMEDATALEN * MaxBackends);
02747 localactivity = (char *)
02748 MemoryContextAlloc(pgStatLocalContext,
02749 pgstat_track_activity_query_size * MaxBackends);
02750 localNumBackends = 0;
02751
02752 beentry = BackendStatusArray;
02753 localentry = localtable;
02754 for (i = 1; i <= MaxBackends; i++)
02755 {
02756
02757
02758
02759
02760
02761
02762
02763 for (;;)
02764 {
02765 int save_changecount = beentry->st_changecount;
02766
02767 localentry->st_procpid = beentry->st_procpid;
02768 if (localentry->st_procpid > 0)
02769 {
02770 memcpy(localentry, (char *) beentry, sizeof(PgBackendStatus));
02771
02772
02773
02774
02775
02776 strcpy(localappname, (char *) beentry->st_appname);
02777 localentry->st_appname = localappname;
02778 strcpy(localactivity, (char *) beentry->st_activity);
02779 localentry->st_activity = localactivity;
02780 }
02781
02782 if (save_changecount == beentry->st_changecount &&
02783 (save_changecount & 1) == 0)
02784 break;
02785
02786
02787 CHECK_FOR_INTERRUPTS();
02788 }
02789
02790 beentry++;
02791
02792 if (localentry->st_procpid > 0)
02793 {
02794 localentry++;
02795 localappname += NAMEDATALEN;
02796 localactivity += pgstat_track_activity_query_size;
02797 localNumBackends++;
02798 }
02799 }
02800
02801
02802 localBackendStatusTable = localtable;
02803 }
02804
02805
02806
02807
02808
02809
02810
02811
02812
02813
02814
02815
02816
02817
02818
02819
02820
02821
02822
02823
02824
02825 const char *
02826 pgstat_get_backend_current_activity(int pid, bool checkUser)
02827 {
02828 PgBackendStatus *beentry;
02829 int i;
02830
02831 beentry = BackendStatusArray;
02832 for (i = 1; i <= MaxBackends; i++)
02833 {
02834
02835
02836
02837
02838
02839
02840
02841
02842
02843
02844 volatile PgBackendStatus *vbeentry = beentry;
02845 bool found;
02846
02847 for (;;)
02848 {
02849 int save_changecount = vbeentry->st_changecount;
02850
02851 found = (vbeentry->st_procpid == pid);
02852
02853 if (save_changecount == vbeentry->st_changecount &&
02854 (save_changecount & 1) == 0)
02855 break;
02856
02857
02858 CHECK_FOR_INTERRUPTS();
02859 }
02860
02861 if (found)
02862 {
02863
02864 if (checkUser && !superuser() && beentry->st_userid != GetUserId())
02865 return "<insufficient privilege>";
02866 else if (*(beentry->st_activity) == '\0')
02867 return "<command string not enabled>";
02868 else
02869 return beentry->st_activity;
02870 }
02871
02872 beentry++;
02873 }
02874
02875
02876 return "<backend information not available>";
02877 }
02878
02879
02880
02881
02882
02883
02884
02885
02886
02887
02888
02889
02890
02891
02892
02893
02894
02895 const char *
02896 pgstat_get_crashed_backend_activity(int pid, char *buffer, int buflen)
02897 {
02898 volatile PgBackendStatus *beentry;
02899 int i;
02900
02901 beentry = BackendStatusArray;
02902
02903
02904
02905
02906
02907 if (beentry == NULL || BackendActivityBuffer == NULL)
02908 return NULL;
02909
02910 for (i = 1; i <= MaxBackends; i++)
02911 {
02912 if (beentry->st_procpid == pid)
02913 {
02914
02915 const char *activity = beentry->st_activity;
02916 const char *activity_last;
02917
02918
02919
02920
02921
02922
02923
02924 activity_last = BackendActivityBuffer + BackendActivityBufferSize
02925 - pgstat_track_activity_query_size;
02926
02927 if (activity < BackendActivityBuffer ||
02928 activity > activity_last)
02929 return NULL;
02930
02931
02932 if (activity[0] == '\0')
02933 return NULL;
02934
02935
02936
02937
02938
02939
02940 ascii_safe_strlcpy(buffer, activity,
02941 Min(buflen, pgstat_track_activity_query_size));
02942
02943 return buffer;
02944 }
02945
02946 beentry++;
02947 }
02948
02949
02950 return NULL;
02951 }
02952
02953
02954
02955
02956
02957
02958
02959
02960
02961
02962
02963
02964
02965
02966 static void
02967 pgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype)
02968 {
02969 hdr->m_type = mtype;
02970 }
02971
02972
02973
02974
02975
02976
02977
02978
02979 static void
02980 pgstat_send(void *msg, int len)
02981 {
02982 int rc;
02983
02984 if (pgStatSock == PGINVALID_SOCKET)
02985 return;
02986
02987 ((PgStat_MsgHdr *) msg)->m_size = len;
02988
02989
02990 do
02991 {
02992 rc = send(pgStatSock, msg, len, 0);
02993 } while (rc < 0 && errno == EINTR);
02994
02995 #ifdef USE_ASSERT_CHECKING
02996
02997 if (rc < 0)
02998 elog(LOG, "could not send to statistics collector: %m");
02999 #endif
03000 }
03001
03002
03003
03004
03005
03006
03007
03008 void
03009 pgstat_send_bgwriter(void)
03010 {
03011
03012 static const PgStat_MsgBgWriter all_zeroes;
03013
03014
03015
03016
03017
03018
03019 if (memcmp(&BgWriterStats, &all_zeroes, sizeof(PgStat_MsgBgWriter)) == 0)
03020 return;
03021
03022
03023
03024
03025 pgstat_setheader(&BgWriterStats.m_hdr, PGSTAT_MTYPE_BGWRITER);
03026 pgstat_send(&BgWriterStats, sizeof(BgWriterStats));
03027
03028
03029
03030
03031 MemSet(&BgWriterStats, 0, sizeof(BgWriterStats));
03032 }
03033
03034
03035
03036
03037
03038
03039
03040
03041
03042
03043
03044 NON_EXEC_STATIC void
03045 PgstatCollectorMain(int argc, char *argv[])
03046 {
03047 int len;
03048 PgStat_Msg msg;
03049 int wr;
03050
03051 IsUnderPostmaster = true;
03052
03053 MyProcPid = getpid();
03054
03055 MyStartTime = time(NULL);
03056
03057
03058
03059
03060
03061
03062
03063 #ifdef HAVE_SETSID
03064 if (setsid() < 0)
03065 elog(FATAL, "setsid() failed: %m");
03066 #endif
03067
03068 InitializeLatchSupport();
03069
03070
03071 InitLatch(&pgStatLatch);
03072
03073
03074
03075
03076
03077
03078 pqsignal(SIGHUP, pgstat_sighup_handler);
03079 pqsignal(SIGINT, SIG_IGN);
03080 pqsignal(SIGTERM, SIG_IGN);
03081 pqsignal(SIGQUIT, pgstat_exit);
03082 pqsignal(SIGALRM, SIG_IGN);
03083 pqsignal(SIGPIPE, SIG_IGN);
03084 pqsignal(SIGUSR1, SIG_IGN);
03085 pqsignal(SIGUSR2, SIG_IGN);
03086 pqsignal(SIGCHLD, SIG_DFL);
03087 pqsignal(SIGTTIN, SIG_DFL);
03088 pqsignal(SIGTTOU, SIG_DFL);
03089 pqsignal(SIGCONT, SIG_DFL);
03090 pqsignal(SIGWINCH, SIG_DFL);
03091 PG_SETMASK(&UnBlockSig);
03092
03093
03094
03095
03096 init_ps_display("stats collector process", "", "", "");
03097
03098
03099
03100
03101
03102 pgStatRunningInCollector = true;
03103 pgStatDBHash = pgstat_read_statsfiles(InvalidOid, true, true);
03104
03105
03106
03107
03108
03109
03110
03111
03112
03113
03114
03115
03116
03117
03118
03119 for (;;)
03120 {
03121
03122 ResetLatch(&pgStatLatch);
03123
03124
03125
03126
03127 if (need_exit)
03128 break;
03129
03130
03131
03132
03133
03134 while (!need_exit)
03135 {
03136
03137
03138
03139 if (got_SIGHUP)
03140 {
03141 got_SIGHUP = false;
03142 ProcessConfigFile(PGC_SIGHUP);
03143 }
03144
03145
03146
03147
03148
03149 if (pgstat_write_statsfile_needed())
03150 pgstat_write_statsfiles(false, false);
03151
03152
03153
03154
03155
03156
03157
03158
03159
03160 #ifdef WIN32
03161 pgwin32_noblock = 1;
03162 #endif
03163
03164 len = recv(pgStatSock, (char *) &msg,
03165 sizeof(PgStat_Msg), 0);
03166
03167 #ifdef WIN32
03168 pgwin32_noblock = 0;
03169 #endif
03170
03171 if (len < 0)
03172 {
03173 if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR)
03174 break;
03175 ereport(ERROR,
03176 (errcode_for_socket_access(),
03177 errmsg("could not read statistics message: %m")));
03178 }
03179
03180
03181
03182
03183 if (len < sizeof(PgStat_MsgHdr))
03184 continue;
03185
03186
03187
03188
03189 if (msg.msg_hdr.m_size != len)
03190 continue;
03191
03192
03193
03194
03195 switch (msg.msg_hdr.m_type)
03196 {
03197 case PGSTAT_MTYPE_DUMMY:
03198 break;
03199
03200 case PGSTAT_MTYPE_INQUIRY:
03201 pgstat_recv_inquiry((PgStat_MsgInquiry *) &msg, len);
03202 break;
03203
03204 case PGSTAT_MTYPE_TABSTAT:
03205 pgstat_recv_tabstat((PgStat_MsgTabstat *) &msg, len);
03206 break;
03207
03208 case PGSTAT_MTYPE_TABPURGE:
03209 pgstat_recv_tabpurge((PgStat_MsgTabpurge *) &msg, len);
03210 break;
03211
03212 case PGSTAT_MTYPE_DROPDB:
03213 pgstat_recv_dropdb((PgStat_MsgDropdb *) &msg, len);
03214 break;
03215
03216 case PGSTAT_MTYPE_RESETCOUNTER:
03217 pgstat_recv_resetcounter((PgStat_MsgResetcounter *) &msg,
03218 len);
03219 break;
03220
03221 case PGSTAT_MTYPE_RESETSHAREDCOUNTER:
03222 pgstat_recv_resetsharedcounter(
03223 (PgStat_MsgResetsharedcounter *) &msg,
03224 len);
03225 break;
03226
03227 case PGSTAT_MTYPE_RESETSINGLECOUNTER:
03228 pgstat_recv_resetsinglecounter(
03229 (PgStat_MsgResetsinglecounter *) &msg,
03230 len);
03231 break;
03232
03233 case PGSTAT_MTYPE_AUTOVAC_START:
03234 pgstat_recv_autovac((PgStat_MsgAutovacStart *) &msg, len);
03235 break;
03236
03237 case PGSTAT_MTYPE_VACUUM:
03238 pgstat_recv_vacuum((PgStat_MsgVacuum *) &msg, len);
03239 break;
03240
03241 case PGSTAT_MTYPE_ANALYZE:
03242 pgstat_recv_analyze((PgStat_MsgAnalyze *) &msg, len);
03243 break;
03244
03245 case PGSTAT_MTYPE_BGWRITER:
03246 pgstat_recv_bgwriter((PgStat_MsgBgWriter *) &msg, len);
03247 break;
03248
03249 case PGSTAT_MTYPE_FUNCSTAT:
03250 pgstat_recv_funcstat((PgStat_MsgFuncstat *) &msg, len);
03251 break;
03252
03253 case PGSTAT_MTYPE_FUNCPURGE:
03254 pgstat_recv_funcpurge((PgStat_MsgFuncpurge *) &msg, len);
03255 break;
03256
03257 case PGSTAT_MTYPE_RECOVERYCONFLICT:
03258 pgstat_recv_recoveryconflict((PgStat_MsgRecoveryConflict *) &msg, len);
03259 break;
03260
03261 case PGSTAT_MTYPE_DEADLOCK:
03262 pgstat_recv_deadlock((PgStat_MsgDeadlock *) &msg, len);
03263 break;
03264
03265 case PGSTAT_MTYPE_TEMPFILE:
03266 pgstat_recv_tempfile((PgStat_MsgTempFile *) &msg, len);
03267 break;
03268
03269 default:
03270 break;
03271 }
03272 }
03273
03274
03275 #ifndef WIN32
03276 wr = WaitLatchOrSocket(&pgStatLatch,
03277 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE,
03278 pgStatSock,
03279 -1L);
03280 #else
03281
03282
03283
03284
03285
03286
03287
03288
03289
03290
03291
03292 wr = WaitLatchOrSocket(&pgStatLatch,
03293 WL_LATCH_SET | WL_POSTMASTER_DEATH | WL_SOCKET_READABLE | WL_TIMEOUT,
03294 pgStatSock,
03295 2 * 1000L );
03296 #endif
03297
03298
03299
03300
03301
03302 if (wr & WL_POSTMASTER_DEATH)
03303 break;
03304 }
03305
03306
03307
03308
03309 pgstat_write_statsfiles(true, true);
03310
03311 exit(0);
03312 }
03313
03314
03315
03316 static void
03317 pgstat_exit(SIGNAL_ARGS)
03318 {
03319 int save_errno = errno;
03320
03321 need_exit = true;
03322 SetLatch(&pgStatLatch);
03323
03324 errno = save_errno;
03325 }
03326
03327
03328 static void
03329 pgstat_sighup_handler(SIGNAL_ARGS)
03330 {
03331 int save_errno = errno;
03332
03333 got_SIGHUP = true;
03334 SetLatch(&pgStatLatch);
03335
03336 errno = save_errno;
03337 }
03338
03339
03340
03341
03342
03343
03344 static void
03345 reset_dbentry_counters(PgStat_StatDBEntry *dbentry)
03346 {
03347 HASHCTL hash_ctl;
03348
03349 dbentry->n_xact_commit = 0;
03350 dbentry->n_xact_rollback = 0;
03351 dbentry->n_blocks_fetched = 0;
03352 dbentry->n_blocks_hit = 0;
03353 dbentry->n_tuples_returned = 0;
03354 dbentry->n_tuples_fetched = 0;
03355 dbentry->n_tuples_inserted = 0;
03356 dbentry->n_tuples_updated = 0;
03357 dbentry->n_tuples_deleted = 0;
03358 dbentry->last_autovac_time = 0;
03359 dbentry->n_conflict_tablespace = 0;
03360 dbentry->n_conflict_lock = 0;
03361 dbentry->n_conflict_snapshot = 0;
03362 dbentry->n_conflict_bufferpin = 0;
03363 dbentry->n_conflict_startup_deadlock = 0;
03364 dbentry->n_temp_files = 0;
03365 dbentry->n_temp_bytes = 0;
03366 dbentry->n_deadlocks = 0;
03367 dbentry->n_block_read_time = 0;
03368 dbentry->n_block_write_time = 0;
03369
03370 dbentry->stat_reset_timestamp = GetCurrentTimestamp();
03371 dbentry->stats_timestamp = 0;
03372
03373 memset(&hash_ctl, 0, sizeof(hash_ctl));
03374 hash_ctl.keysize = sizeof(Oid);
03375 hash_ctl.entrysize = sizeof(PgStat_StatTabEntry);
03376 hash_ctl.hash = oid_hash;
03377 dbentry->tables = hash_create("Per-database table",
03378 PGSTAT_TAB_HASH_SIZE,
03379 &hash_ctl,
03380 HASH_ELEM | HASH_FUNCTION);
03381
03382 hash_ctl.keysize = sizeof(Oid);
03383 hash_ctl.entrysize = sizeof(PgStat_StatFuncEntry);
03384 hash_ctl.hash = oid_hash;
03385 dbentry->functions = hash_create("Per-database function",
03386 PGSTAT_FUNCTION_HASH_SIZE,
03387 &hash_ctl,
03388 HASH_ELEM | HASH_FUNCTION);
03389 }
03390
03391
03392
03393
03394
03395
03396 static PgStat_StatDBEntry *
03397 pgstat_get_db_entry(Oid databaseid, bool create)
03398 {
03399 PgStat_StatDBEntry *result;
03400 bool found;
03401 HASHACTION action = (create ? HASH_ENTER : HASH_FIND);
03402
03403
03404 result = (PgStat_StatDBEntry *) hash_search(pgStatDBHash,
03405 &databaseid,
03406 action, &found);
03407
03408 if (!create && !found)
03409 return NULL;
03410
03411
03412
03413
03414
03415 if (!found)
03416 reset_dbentry_counters(result);
03417
03418 return result;
03419 }
03420
03421
03422
03423
03424
03425
03426
03427 static PgStat_StatTabEntry *
03428 pgstat_get_tab_entry(PgStat_StatDBEntry *dbentry, Oid tableoid, bool create)
03429 {
03430 PgStat_StatTabEntry *result;
03431 bool found;
03432 HASHACTION action = (create ? HASH_ENTER : HASH_FIND);
03433
03434
03435 result = (PgStat_StatTabEntry *) hash_search(dbentry->tables,
03436 &tableoid,
03437 action, &found);
03438
03439 if (!create && !found)
03440 return NULL;
03441
03442
03443 if (!found)
03444 {
03445 result->numscans = 0;
03446 result->tuples_returned = 0;
03447 result->tuples_fetched = 0;
03448 result->tuples_inserted = 0;
03449 result->tuples_updated = 0;
03450 result->tuples_deleted = 0;
03451 result->tuples_hot_updated = 0;
03452 result->n_live_tuples = 0;
03453 result->n_dead_tuples = 0;
03454 result->changes_since_analyze = 0;
03455 result->blocks_fetched = 0;
03456 result->blocks_hit = 0;
03457 result->vacuum_timestamp = 0;
03458 result->vacuum_count = 0;
03459 result->autovac_vacuum_timestamp = 0;
03460 result->autovac_vacuum_count = 0;
03461 result->analyze_timestamp = 0;
03462 result->analyze_count = 0;
03463 result->autovac_analyze_timestamp = 0;
03464 result->autovac_analyze_count = 0;
03465 }
03466
03467 return result;
03468 }
03469
03470
03471
03472
03473
03474
03475
03476
03477
03478
03479
03480
03481
03482
03483
03484
03485 static void
03486 pgstat_write_statsfiles(bool permanent, bool allDbs)
03487 {
03488 HASH_SEQ_STATUS hstat;
03489 PgStat_StatDBEntry *dbentry;
03490 FILE *fpout;
03491 int32 format_id;
03492 const char *tmpfile = permanent ? PGSTAT_STAT_PERMANENT_TMPFILE : pgstat_stat_tmpname;
03493 const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
03494 int rc;
03495
03496 elog(DEBUG2, "writing statsfile '%s'", statfile);
03497
03498
03499
03500
03501 fpout = AllocateFile(tmpfile, PG_BINARY_W);
03502 if (fpout == NULL)
03503 {
03504 ereport(LOG,
03505 (errcode_for_file_access(),
03506 errmsg("could not open temporary statistics file \"%s\": %m",
03507 tmpfile)));
03508 return;
03509 }
03510
03511
03512
03513
03514 globalStats.stats_timestamp = GetCurrentTimestamp();
03515
03516
03517
03518
03519 format_id = PGSTAT_FILE_FORMAT_ID;
03520 rc = fwrite(&format_id, sizeof(format_id), 1, fpout);
03521 (void) rc;
03522
03523
03524
03525
03526 rc = fwrite(&globalStats, sizeof(globalStats), 1, fpout);
03527 (void) rc;
03528
03529
03530
03531
03532 hash_seq_init(&hstat, pgStatDBHash);
03533 while ((dbentry = (PgStat_StatDBEntry *) hash_seq_search(&hstat)) != NULL)
03534 {
03535
03536
03537
03538
03539
03540
03541
03542 if (allDbs || pgstat_db_requested(dbentry->databaseid))
03543 {
03544 dbentry->stats_timestamp = globalStats.stats_timestamp;
03545 pgstat_write_db_statsfile(dbentry, permanent);
03546 }
03547
03548
03549
03550
03551
03552 fputc('D', fpout);
03553 rc = fwrite(dbentry, offsetof(PgStat_StatDBEntry, tables), 1, fpout);
03554 (void) rc;
03555 }
03556
03557
03558
03559
03560
03561
03562 fputc('E', fpout);
03563
03564 if (ferror(fpout))
03565 {
03566 ereport(LOG,
03567 (errcode_for_file_access(),
03568 errmsg("could not write temporary statistics file \"%s\": %m",
03569 tmpfile)));
03570 FreeFile(fpout);
03571 unlink(tmpfile);
03572 }
03573 else if (FreeFile(fpout) < 0)
03574 {
03575 ereport(LOG,
03576 (errcode_for_file_access(),
03577 errmsg("could not close temporary statistics file \"%s\": %m",
03578 tmpfile)));
03579 unlink(tmpfile);
03580 }
03581 else if (rename(tmpfile, statfile) < 0)
03582 {
03583 ereport(LOG,
03584 (errcode_for_file_access(),
03585 errmsg("could not rename temporary statistics file \"%s\" to \"%s\": %m",
03586 tmpfile, statfile)));
03587 unlink(tmpfile);
03588 }
03589
03590 if (permanent)
03591 unlink(pgstat_stat_filename);
03592
03593
03594
03595
03596
03597 if (!slist_is_empty(&last_statrequests))
03598 {
03599 slist_mutable_iter iter;
03600
03601 slist_foreach_modify(iter, &last_statrequests)
03602 {
03603 DBWriteRequest *req;
03604
03605 req = slist_container(DBWriteRequest, next, iter.cur);
03606 pfree(req);
03607 }
03608
03609 slist_init(&last_statrequests);
03610 }
03611 }
03612
03613
03614
03615
03616
03617 static void
03618 get_dbstat_filename(bool permanent, bool tempname, Oid databaseid,
03619 char *filename, int len)
03620 {
03621 int printed;
03622
03623 printed = snprintf(filename, len, "%s/db_%u.%s",
03624 permanent ? PGSTAT_STAT_PERMANENT_DIRECTORY :
03625 pgstat_stat_directory,
03626 databaseid,
03627 tempname ? "tmp" : "stat");
03628 if (printed > len)
03629 elog(ERROR, "overlength pgstat path");
03630 }
03631
03632
03633
03634
03635
03636
03637
03638
03639
03640
03641
03642 static void
03643 pgstat_write_db_statsfile(PgStat_StatDBEntry *dbentry, bool permanent)
03644 {
03645 HASH_SEQ_STATUS tstat;
03646 HASH_SEQ_STATUS fstat;
03647 PgStat_StatTabEntry *tabentry;
03648 PgStat_StatFuncEntry *funcentry;
03649 FILE *fpout;
03650 int32 format_id;
03651 Oid dbid = dbentry->databaseid;
03652 int rc;
03653 char tmpfile[MAXPGPATH];
03654 char statfile[MAXPGPATH];
03655
03656 get_dbstat_filename(permanent, true, dbid, tmpfile, MAXPGPATH);
03657 get_dbstat_filename(permanent, false, dbid, statfile, MAXPGPATH);
03658
03659 elog(DEBUG2, "writing statsfile '%s'", statfile);
03660
03661
03662
03663
03664 fpout = AllocateFile(tmpfile, PG_BINARY_W);
03665 if (fpout == NULL)
03666 {
03667 ereport(LOG,
03668 (errcode_for_file_access(),
03669 errmsg("could not open temporary statistics file \"%s\": %m",
03670 tmpfile)));
03671 return;
03672 }
03673
03674
03675
03676
03677 format_id = PGSTAT_FILE_FORMAT_ID;
03678 rc = fwrite(&format_id, sizeof(format_id), 1, fpout);
03679 (void) rc;
03680
03681
03682
03683
03684 hash_seq_init(&tstat, dbentry->tables);
03685 while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&tstat)) != NULL)
03686 {
03687 fputc('T', fpout);
03688 rc = fwrite(tabentry, sizeof(PgStat_StatTabEntry), 1, fpout);
03689 (void) rc;
03690 }
03691
03692
03693
03694
03695 hash_seq_init(&fstat, dbentry->functions);
03696 while ((funcentry = (PgStat_StatFuncEntry *) hash_seq_search(&fstat)) != NULL)
03697 {
03698 fputc('F', fpout);
03699 rc = fwrite(funcentry, sizeof(PgStat_StatFuncEntry), 1, fpout);
03700 (void) rc;
03701 }
03702
03703
03704
03705
03706
03707
03708 fputc('E', fpout);
03709
03710 if (ferror(fpout))
03711 {
03712 ereport(LOG,
03713 (errcode_for_file_access(),
03714 errmsg("could not write temporary statistics file \"%s\": %m",
03715 tmpfile)));
03716 FreeFile(fpout);
03717 unlink(tmpfile);
03718 }
03719 else if (FreeFile(fpout) < 0)
03720 {
03721 ereport(LOG,
03722 (errcode_for_file_access(),
03723 errmsg("could not close temporary statistics file \"%s\": %m",
03724 tmpfile)));
03725 unlink(tmpfile);
03726 }
03727 else if (rename(tmpfile, statfile) < 0)
03728 {
03729 ereport(LOG,
03730 (errcode_for_file_access(),
03731 errmsg("could not rename temporary statistics file \"%s\" to \"%s\": %m",
03732 tmpfile, statfile)));
03733 unlink(tmpfile);
03734 }
03735
03736 if (permanent)
03737 {
03738 get_dbstat_filename(false, false, dbid, statfile, MAXPGPATH);
03739
03740 elog(DEBUG2, "removing temporary stat file '%s'", statfile);
03741 unlink(statfile);
03742 }
03743 }
03744
03745
03746
03747
03748
03749
03750
03751
03752
03753
03754
03755
03756
03757
03758 static HTAB *
03759 pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
03760 {
03761 PgStat_StatDBEntry *dbentry;
03762 PgStat_StatDBEntry dbbuf;
03763 HASHCTL hash_ctl;
03764 HTAB *dbhash;
03765 FILE *fpin;
03766 int32 format_id;
03767 bool found;
03768 const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
03769
03770
03771
03772
03773 pgstat_setup_memcxt();
03774
03775
03776
03777
03778 memset(&hash_ctl, 0, sizeof(hash_ctl));
03779 hash_ctl.keysize = sizeof(Oid);
03780 hash_ctl.entrysize = sizeof(PgStat_StatDBEntry);
03781 hash_ctl.hash = oid_hash;
03782 hash_ctl.hcxt = pgStatLocalContext;
03783 dbhash = hash_create("Databases hash", PGSTAT_DB_HASH_SIZE, &hash_ctl,
03784 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
03785
03786
03787
03788
03789
03790 memset(&globalStats, 0, sizeof(globalStats));
03791
03792
03793
03794
03795
03796 globalStats.stat_reset_timestamp = GetCurrentTimestamp();
03797
03798
03799
03800
03801
03802
03803
03804
03805
03806
03807 if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
03808 {
03809 if (errno != ENOENT)
03810 ereport(pgStatRunningInCollector ? LOG : WARNING,
03811 (errcode_for_file_access(),
03812 errmsg("could not open statistics file \"%s\": %m",
03813 statfile)));
03814 return dbhash;
03815 }
03816
03817
03818
03819
03820 if (fread(&format_id, 1, sizeof(format_id), fpin) != sizeof(format_id) ||
03821 format_id != PGSTAT_FILE_FORMAT_ID)
03822 {
03823 ereport(pgStatRunningInCollector ? LOG : WARNING,
03824 (errmsg("corrupted statistics file \"%s\"", statfile)));
03825 goto done;
03826 }
03827
03828
03829
03830
03831 if (fread(&globalStats, 1, sizeof(globalStats), fpin) != sizeof(globalStats))
03832 {
03833 ereport(pgStatRunningInCollector ? LOG : WARNING,
03834 (errmsg("corrupted statistics file \"%s\"", statfile)));
03835 goto done;
03836 }
03837
03838
03839
03840
03841
03842 for (;;)
03843 {
03844 switch (fgetc(fpin))
03845 {
03846
03847
03848
03849
03850 case 'D':
03851 if (fread(&dbbuf, 1, offsetof(PgStat_StatDBEntry, tables),
03852 fpin) != offsetof(PgStat_StatDBEntry, tables))
03853 {
03854 ereport(pgStatRunningInCollector ? LOG : WARNING,
03855 (errmsg("corrupted statistics file \"%s\"",
03856 statfile)));
03857 goto done;
03858 }
03859
03860
03861
03862
03863 dbentry = (PgStat_StatDBEntry *) hash_search(dbhash,
03864 (void *) &dbbuf.databaseid,
03865 HASH_ENTER,
03866 &found);
03867 if (found)
03868 {
03869 ereport(pgStatRunningInCollector ? LOG : WARNING,
03870 (errmsg("corrupted statistics file \"%s\"",
03871 statfile)));
03872 goto done;
03873 }
03874
03875 memcpy(dbentry, &dbbuf, sizeof(PgStat_StatDBEntry));
03876 dbentry->tables = NULL;
03877 dbentry->functions = NULL;
03878
03879
03880
03881
03882
03883 if (onlydb != InvalidOid)
03884 {
03885 if (dbbuf.databaseid != onlydb &&
03886 dbbuf.databaseid != InvalidOid)
03887 break;
03888 }
03889
03890 memset(&hash_ctl, 0, sizeof(hash_ctl));
03891 hash_ctl.keysize = sizeof(Oid);
03892 hash_ctl.entrysize = sizeof(PgStat_StatTabEntry);
03893 hash_ctl.hash = oid_hash;
03894 hash_ctl.hcxt = pgStatLocalContext;
03895 dbentry->tables = hash_create("Per-database table",
03896 PGSTAT_TAB_HASH_SIZE,
03897 &hash_ctl,
03898 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
03899
03900 hash_ctl.keysize = sizeof(Oid);
03901 hash_ctl.entrysize = sizeof(PgStat_StatFuncEntry);
03902 hash_ctl.hash = oid_hash;
03903 hash_ctl.hcxt = pgStatLocalContext;
03904 dbentry->functions = hash_create("Per-database function",
03905 PGSTAT_FUNCTION_HASH_SIZE,
03906 &hash_ctl,
03907 HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
03908
03909
03910
03911
03912
03913
03914
03915 if (deep)
03916 pgstat_read_db_statsfile(dbentry->databaseid,
03917 dbentry->tables,
03918 dbentry->functions,
03919 permanent);
03920
03921 break;
03922
03923 case 'E':
03924 goto done;
03925
03926 default:
03927 ereport(pgStatRunningInCollector ? LOG : WARNING,
03928 (errmsg("corrupted statistics file \"%s\"",
03929 statfile)));
03930 goto done;
03931 }
03932 }
03933
03934 done:
03935 FreeFile(fpin);
03936
03937
03938 if (permanent)
03939 {
03940 elog(DEBUG2, "removing permanent stats file '%s'", statfile);
03941 unlink(statfile);
03942 }
03943
03944 return dbhash;
03945 }
03946
03947
03948
03949
03950
03951
03952
03953
03954
03955
03956
03957
03958 static void
03959 pgstat_read_db_statsfile(Oid databaseid, HTAB *tabhash, HTAB *funchash,
03960 bool permanent)
03961 {
03962 PgStat_StatTabEntry *tabentry;
03963 PgStat_StatTabEntry tabbuf;
03964 PgStat_StatFuncEntry funcbuf;
03965 PgStat_StatFuncEntry *funcentry;
03966 FILE *fpin;
03967 int32 format_id;
03968 bool found;
03969 char statfile[MAXPGPATH];
03970
03971 get_dbstat_filename(permanent, false, databaseid, statfile, MAXPGPATH);
03972
03973
03974
03975
03976
03977
03978
03979
03980
03981
03982 if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
03983 {
03984 if (errno != ENOENT)
03985 ereport(pgStatRunningInCollector ? LOG : WARNING,
03986 (errcode_for_file_access(),
03987 errmsg("could not open statistics file \"%s\": %m",
03988 statfile)));
03989 return;
03990 }
03991
03992
03993
03994
03995 if (fread(&format_id, 1, sizeof(format_id), fpin) != sizeof(format_id) ||
03996 format_id != PGSTAT_FILE_FORMAT_ID)
03997 {
03998 ereport(pgStatRunningInCollector ? LOG : WARNING,
03999 (errmsg("corrupted statistics file \"%s\"", statfile)));
04000 goto done;
04001 }
04002
04003
04004
04005
04006
04007 for (;;)
04008 {
04009 switch (fgetc(fpin))
04010 {
04011
04012
04013
04014 case 'T':
04015 if (fread(&tabbuf, 1, sizeof(PgStat_StatTabEntry),
04016 fpin) != sizeof(PgStat_StatTabEntry))
04017 {
04018 ereport(pgStatRunningInCollector ? LOG : WARNING,
04019 (errmsg("corrupted statistics file \"%s\"",
04020 statfile)));
04021 goto done;
04022 }
04023
04024
04025
04026
04027 if (tabhash == NULL)
04028 break;
04029
04030 tabentry = (PgStat_StatTabEntry *) hash_search(tabhash,
04031 (void *) &tabbuf.tableid,
04032 HASH_ENTER, &found);
04033
04034 if (found)
04035 {
04036 ereport(pgStatRunningInCollector ? LOG : WARNING,
04037 (errmsg("corrupted statistics file \"%s\"",
04038 statfile)));
04039 goto done;
04040 }
04041
04042 memcpy(tabentry, &tabbuf, sizeof(tabbuf));
04043 break;
04044
04045
04046
04047
04048 case 'F':
04049 if (fread(&funcbuf, 1, sizeof(PgStat_StatFuncEntry),
04050 fpin) != sizeof(PgStat_StatFuncEntry))
04051 {
04052 ereport(pgStatRunningInCollector ? LOG : WARNING,
04053 (errmsg("corrupted statistics file \"%s\"",
04054 statfile)));
04055 goto done;
04056 }
04057
04058
04059
04060
04061 if (funchash == NULL)
04062 break;
04063
04064 funcentry = (PgStat_StatFuncEntry *) hash_search(funchash,
04065 (void *) &funcbuf.functionid,
04066 HASH_ENTER, &found);
04067
04068 if (found)
04069 {
04070 ereport(pgStatRunningInCollector ? LOG : WARNING,
04071 (errmsg("corrupted statistics file \"%s\"",
04072 statfile)));
04073 goto done;
04074 }
04075
04076 memcpy(funcentry, &funcbuf, sizeof(funcbuf));
04077 break;
04078
04079
04080
04081
04082 case 'E':
04083 goto done;
04084
04085 default:
04086 ereport(pgStatRunningInCollector ? LOG : WARNING,
04087 (errmsg("corrupted statistics file \"%s\"",
04088 statfile)));
04089 goto done;
04090 }
04091 }
04092
04093 done:
04094 FreeFile(fpin);
04095
04096 if (permanent)
04097 {
04098 elog(DEBUG2, "removing permanent stats file '%s'", statfile);
04099 unlink(statfile);
04100 }
04101
04102 return;
04103 }
04104
04105
04106
04107
04108
04109
04110
04111
04112
04113
04114
04115
04116
04117
04118
04119
04120
04121
04122 static bool
04123 pgstat_read_db_statsfile_timestamp(Oid databaseid, bool permanent,
04124 TimestampTz *ts)
04125 {
04126 PgStat_StatDBEntry dbentry;
04127 PgStat_GlobalStats myGlobalStats;
04128 FILE *fpin;
04129 int32 format_id;
04130 const char *statfile = permanent ? PGSTAT_STAT_PERMANENT_FILENAME : pgstat_stat_filename;
04131
04132
04133
04134
04135
04136 if ((fpin = AllocateFile(statfile, PG_BINARY_R)) == NULL)
04137 {
04138 if (errno != ENOENT)
04139 ereport(pgStatRunningInCollector ? LOG : WARNING,
04140 (errcode_for_file_access(),
04141 errmsg("could not open statistics file \"%s\": %m",
04142 statfile)));
04143 return false;
04144 }
04145
04146
04147
04148
04149 if (fread(&format_id, 1, sizeof(format_id), fpin) != sizeof(format_id) ||
04150 format_id != PGSTAT_FILE_FORMAT_ID)
04151 {
04152 ereport(pgStatRunningInCollector ? LOG : WARNING,
04153 (errmsg("corrupted statistics file \"%s\"", statfile)));
04154 FreeFile(fpin);
04155 return false;
04156 }
04157
04158
04159
04160
04161 if (fread(&myGlobalStats, 1, sizeof(myGlobalStats),
04162 fpin) != sizeof(myGlobalStats))
04163 {
04164 ereport(pgStatRunningInCollector ? LOG : WARNING,
04165 (errmsg("corrupted statistics file \"%s\"", statfile)));
04166 FreeFile(fpin);
04167 return false;
04168 }
04169
04170
04171 *ts = myGlobalStats.stats_timestamp;
04172
04173
04174
04175
04176
04177 for (;;)
04178 {
04179 switch (fgetc(fpin))
04180 {
04181
04182
04183
04184
04185 case 'D':
04186 if (fread(&dbentry, 1, offsetof(PgStat_StatDBEntry, tables),
04187 fpin) != offsetof(PgStat_StatDBEntry, tables))
04188 {
04189 ereport(pgStatRunningInCollector ? LOG : WARNING,
04190 (errmsg("corrupted statistics file \"%s\"",
04191 statfile)));
04192 goto done;
04193 }
04194
04195
04196
04197
04198
04199 if (dbentry.databaseid == databaseid)
04200 {
04201 *ts = dbentry.stats_timestamp;
04202 goto done;
04203 }
04204
04205 break;
04206
04207 case 'E':
04208 goto done;
04209
04210 default:
04211 ereport(pgStatRunningInCollector ? LOG : WARNING,
04212 (errmsg("corrupted statistics file \"%s\"",
04213 statfile)));
04214 goto done;
04215 }
04216 }
04217
04218 done:
04219 FreeFile(fpin);
04220 return true;
04221 }
04222
04223
04224
04225
04226
04227
04228 static void
04229 backend_read_statsfile(void)
04230 {
04231 TimestampTz min_ts = 0;
04232 TimestampTz ref_ts = 0;
04233 int count;
04234
04235
04236 if (pgStatDBHash)
04237 return;
04238 Assert(!pgStatRunningInCollector);
04239
04240
04241
04242
04243
04244
04245 for (count = 0; count < PGSTAT_POLL_LOOP_COUNT; count++)
04246 {
04247 bool ok;
04248 TimestampTz file_ts = 0;
04249 TimestampTz cur_ts;
04250
04251 CHECK_FOR_INTERRUPTS();
04252
04253 ok = pgstat_read_db_statsfile_timestamp(MyDatabaseId, false, &file_ts);
04254
04255 cur_ts = GetCurrentTimestamp();
04256
04257 if (count == 0 || cur_ts < ref_ts)
04258 {
04259
04260
04261
04262
04263
04264
04265
04266
04267
04268
04269
04270
04271
04272
04273
04274
04275 ref_ts = cur_ts;
04276 if (IsAutoVacuumWorkerProcess())
04277 min_ts = TimestampTzPlusMilliseconds(ref_ts,
04278 -PGSTAT_RETRY_DELAY);
04279 else
04280 min_ts = TimestampTzPlusMilliseconds(ref_ts,
04281 -PGSTAT_STAT_INTERVAL);
04282 }
04283
04284
04285
04286
04287
04288
04289
04290
04291 if (ok && file_ts > cur_ts)
04292 {
04293
04294
04295
04296
04297
04298 if (file_ts >= TimestampTzPlusMilliseconds(cur_ts, 1000))
04299 {
04300 char *filetime;
04301 char *mytime;
04302
04303
04304 filetime = pstrdup(timestamptz_to_str(file_ts));
04305 mytime = pstrdup(timestamptz_to_str(cur_ts));
04306 elog(LOG, "stats collector's time %s is later than backend local time %s",
04307 filetime, mytime);
04308 pfree(filetime);
04309 pfree(mytime);
04310 }
04311
04312 pgstat_send_inquiry(cur_ts, min_ts, MyDatabaseId);
04313 break;
04314 }
04315
04316
04317 if (ok && file_ts >= min_ts)
04318 break;
04319
04320
04321 if ((count % PGSTAT_INQ_LOOP_COUNT) == 0)
04322 pgstat_send_inquiry(cur_ts, min_ts, MyDatabaseId);
04323
04324 pg_usleep(PGSTAT_RETRY_DELAY * 1000L);
04325 }
04326
04327 if (count >= PGSTAT_POLL_LOOP_COUNT)
04328 elog(WARNING, "pgstat wait timeout");
04329
04330
04331
04332
04333
04334 if (IsAutoVacuumLauncherProcess())
04335 pgStatDBHash = pgstat_read_statsfiles(InvalidOid, false, false);
04336 else
04337 pgStatDBHash = pgstat_read_statsfiles(MyDatabaseId, false, true);
04338 }
04339
04340
04341
04342
04343
04344
04345
04346
04347 static void
04348 pgstat_setup_memcxt(void)
04349 {
04350 if (!pgStatLocalContext)
04351 pgStatLocalContext = AllocSetContextCreate(TopMemoryContext,
04352 "Statistics snapshot",
04353 ALLOCSET_SMALL_MINSIZE,
04354 ALLOCSET_SMALL_INITSIZE,
04355 ALLOCSET_SMALL_MAXSIZE);
04356 }
04357
04358
04359
04360
04361
04362
04363
04364
04365
04366
04367
04368
04369 void
04370 pgstat_clear_snapshot(void)
04371 {
04372
04373 if (pgStatLocalContext)
04374 MemoryContextDelete(pgStatLocalContext);
04375
04376
04377 pgStatLocalContext = NULL;
04378 pgStatDBHash = NULL;
04379 localBackendStatusTable = NULL;
04380 localNumBackends = 0;
04381 }
04382
04383
04384
04385
04386
04387
04388
04389
04390 static void
04391 pgstat_recv_inquiry(PgStat_MsgInquiry *msg, int len)
04392 {
04393 slist_iter iter;
04394 DBWriteRequest *newreq;
04395 PgStat_StatDBEntry *dbentry;
04396
04397 elog(DEBUG2, "received inquiry for %d", msg->databaseid);
04398
04399
04400
04401
04402
04403
04404
04405
04406
04407
04408 slist_foreach(iter, &last_statrequests)
04409 {
04410 DBWriteRequest *req = slist_container(DBWriteRequest, next, iter.cur);
04411
04412 if (req->databaseid != msg->databaseid)
04413 continue;
04414
04415 if (msg->cutoff_time > req->request_time)
04416 req->request_time = msg->cutoff_time;
04417 return;
04418 }
04419
04420
04421
04422
04423 newreq = palloc(sizeof(DBWriteRequest));
04424
04425 newreq->databaseid = msg->databaseid;
04426 newreq->request_time = msg->clock_time;
04427 slist_push_head(&last_statrequests, &newreq->next);
04428
04429
04430
04431
04432
04433
04434
04435
04436
04437 dbentry = pgstat_get_db_entry(msg->databaseid, false);
04438 if ((dbentry != NULL) && (msg->clock_time < dbentry->stats_timestamp))
04439 {
04440 TimestampTz cur_ts = GetCurrentTimestamp();
04441
04442 if (cur_ts < dbentry->stats_timestamp)
04443 {
04444
04445
04446
04447
04448 char *writetime;
04449 char *mytime;
04450
04451
04452 writetime = pstrdup(timestamptz_to_str(dbentry->stats_timestamp));
04453 mytime = pstrdup(timestamptz_to_str(cur_ts));
04454 elog(LOG,
04455 "stats_timestamp %s is later than collector's time %s for db %d",
04456 writetime, mytime, dbentry->databaseid);
04457 pfree(writetime);
04458 pfree(mytime);
04459
04460 newreq->request_time = cur_ts;
04461 dbentry->stats_timestamp = cur_ts - 1;
04462 }
04463 }
04464 }
04465
04466
04467
04468
04469
04470
04471
04472
04473 static void
04474 pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len)
04475 {
04476 PgStat_StatDBEntry *dbentry;
04477 PgStat_StatTabEntry *tabentry;
04478 int i;
04479 bool found;
04480
04481 dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
04482
04483
04484
04485
04486 dbentry->n_xact_commit += (PgStat_Counter) (msg->m_xact_commit);
04487 dbentry->n_xact_rollback += (PgStat_Counter) (msg->m_xact_rollback);
04488 dbentry->n_block_read_time += msg->m_block_read_time;
04489 dbentry->n_block_write_time += msg->m_block_write_time;
04490
04491
04492
04493
04494 for (i = 0; i < msg->m_nentries; i++)
04495 {
04496 PgStat_TableEntry *tabmsg = &(msg->m_entry[i]);
04497
04498 tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables,
04499 (void *) &(tabmsg->t_id),
04500 HASH_ENTER, &found);
04501
04502 if (!found)
04503 {
04504
04505
04506
04507
04508 tabentry->numscans = tabmsg->t_counts.t_numscans;
04509 tabentry->tuples_returned = tabmsg->t_counts.t_tuples_returned;
04510 tabentry->tuples_fetched = tabmsg->t_counts.t_tuples_fetched;
04511 tabentry->tuples_inserted = tabmsg->t_counts.t_tuples_inserted;
04512 tabentry->tuples_updated = tabmsg->t_counts.t_tuples_updated;
04513 tabentry->tuples_deleted = tabmsg->t_counts.t_tuples_deleted;
04514 tabentry->tuples_hot_updated = tabmsg->t_counts.t_tuples_hot_updated;
04515 tabentry->n_live_tuples = tabmsg->t_counts.t_delta_live_tuples;
04516 tabentry->n_dead_tuples = tabmsg->t_counts.t_delta_dead_tuples;
04517 tabentry->changes_since_analyze = tabmsg->t_counts.t_changed_tuples;
04518 tabentry->blocks_fetched = tabmsg->t_counts.t_blocks_fetched;
04519 tabentry->blocks_hit = tabmsg->t_counts.t_blocks_hit;
04520
04521 tabentry->vacuum_timestamp = 0;
04522 tabentry->vacuum_count = 0;
04523 tabentry->autovac_vacuum_timestamp = 0;
04524 tabentry->autovac_vacuum_count = 0;
04525 tabentry->analyze_timestamp = 0;
04526 tabentry->analyze_count = 0;
04527 tabentry->autovac_analyze_timestamp = 0;
04528 tabentry->autovac_analyze_count = 0;
04529 }
04530 else
04531 {
04532
04533
04534
04535 tabentry->numscans += tabmsg->t_counts.t_numscans;
04536 tabentry->tuples_returned += tabmsg->t_counts.t_tuples_returned;
04537 tabentry->tuples_fetched += tabmsg->t_counts.t_tuples_fetched;
04538 tabentry->tuples_inserted += tabmsg->t_counts.t_tuples_inserted;
04539 tabentry->tuples_updated += tabmsg->t_counts.t_tuples_updated;
04540 tabentry->tuples_deleted += tabmsg->t_counts.t_tuples_deleted;
04541 tabentry->tuples_hot_updated += tabmsg->t_counts.t_tuples_hot_updated;
04542 tabentry->n_live_tuples += tabmsg->t_counts.t_delta_live_tuples;
04543 tabentry->n_dead_tuples += tabmsg->t_counts.t_delta_dead_tuples;
04544 tabentry->changes_since_analyze += tabmsg->t_counts.t_changed_tuples;
04545 tabentry->blocks_fetched += tabmsg->t_counts.t_blocks_fetched;
04546 tabentry->blocks_hit += tabmsg->t_counts.t_blocks_hit;
04547 }
04548
04549
04550 tabentry->n_live_tuples = Max(tabentry->n_live_tuples, 0);
04551
04552 tabentry->n_dead_tuples = Max(tabentry->n_dead_tuples, 0);
04553
04554
04555
04556
04557 dbentry->n_tuples_returned += tabmsg->t_counts.t_tuples_returned;
04558 dbentry->n_tuples_fetched += tabmsg->t_counts.t_tuples_fetched;
04559 dbentry->n_tuples_inserted += tabmsg->t_counts.t_tuples_inserted;
04560 dbentry->n_tuples_updated += tabmsg->t_counts.t_tuples_updated;
04561 dbentry->n_tuples_deleted += tabmsg->t_counts.t_tuples_deleted;
04562 dbentry->n_blocks_fetched += tabmsg->t_counts.t_blocks_fetched;
04563 dbentry->n_blocks_hit += tabmsg->t_counts.t_blocks_hit;
04564 }
04565 }
04566
04567
04568
04569
04570
04571
04572
04573
04574 static void
04575 pgstat_recv_tabpurge(PgStat_MsgTabpurge *msg, int len)
04576 {
04577 PgStat_StatDBEntry *dbentry;
04578 int i;
04579
04580 dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
04581
04582
04583
04584
04585 if (!dbentry || !dbentry->tables)
04586 return;
04587
04588
04589
04590
04591 for (i = 0; i < msg->m_nentries; i++)
04592 {
04593
04594 (void) hash_search(dbentry->tables,
04595 (void *) &(msg->m_tableid[i]),
04596 HASH_REMOVE, NULL);
04597 }
04598 }
04599
04600
04601
04602
04603
04604
04605
04606
04607 static void
04608 pgstat_recv_dropdb(PgStat_MsgDropdb *msg, int len)
04609 {
04610 Oid dbid = msg->m_databaseid;
04611 PgStat_StatDBEntry *dbentry;
04612
04613
04614
04615
04616 dbentry = pgstat_get_db_entry(dbid, false);
04617
04618
04619
04620
04621 if (dbentry)
04622 {
04623 char statfile[MAXPGPATH];
04624
04625 get_dbstat_filename(true, false, dbid, statfile, MAXPGPATH);
04626
04627 elog(DEBUG2, "removing %s", statfile);
04628 unlink(statfile);
04629
04630 if (dbentry->tables != NULL)
04631 hash_destroy(dbentry->tables);
04632 if (dbentry->functions != NULL)
04633 hash_destroy(dbentry->functions);
04634
04635 if (hash_search(pgStatDBHash,
04636 (void *) &dbid,
04637 HASH_REMOVE, NULL) == NULL)
04638 ereport(ERROR,
04639 (errmsg("database hash table corrupted during cleanup --- abort")));
04640 }
04641 }
04642
04643
04644
04645
04646
04647
04648
04649
04650 static void
04651 pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len)
04652 {
04653 PgStat_StatDBEntry *dbentry;
04654
04655
04656
04657
04658 dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
04659
04660 if (!dbentry)
04661 return;
04662
04663
04664
04665
04666
04667 if (dbentry->tables != NULL)
04668 hash_destroy(dbentry->tables);
04669 if (dbentry->functions != NULL)
04670 hash_destroy(dbentry->functions);
04671
04672 dbentry->tables = NULL;
04673 dbentry->functions = NULL;
04674
04675
04676
04677
04678
04679 reset_dbentry_counters(dbentry);
04680 }
04681
04682
04683
04684
04685
04686
04687
04688 static void
04689 pgstat_recv_resetsharedcounter(PgStat_MsgResetsharedcounter *msg, int len)
04690 {
04691 if (msg->m_resettarget == RESET_BGWRITER)
04692 {
04693
04694 memset(&globalStats, 0, sizeof(globalStats));
04695 globalStats.stat_reset_timestamp = GetCurrentTimestamp();
04696 }
04697
04698
04699
04700
04701
04702 }
04703
04704
04705
04706
04707
04708
04709
04710 static void
04711 pgstat_recv_resetsinglecounter(PgStat_MsgResetsinglecounter *msg, int len)
04712 {
04713 PgStat_StatDBEntry *dbentry;
04714
04715 dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
04716
04717 if (!dbentry)
04718 return;
04719
04720
04721 dbentry->stat_reset_timestamp = GetCurrentTimestamp();
04722
04723
04724 if (msg->m_resettype == RESET_TABLE)
04725 (void) hash_search(dbentry->tables, (void *) &(msg->m_objectid),
04726 HASH_REMOVE, NULL);
04727 else if (msg->m_resettype == RESET_FUNCTION)
04728 (void) hash_search(dbentry->functions, (void *) &(msg->m_objectid),
04729 HASH_REMOVE, NULL);
04730 }
04731
04732
04733
04734
04735
04736
04737
04738 static void
04739 pgstat_recv_autovac(PgStat_MsgAutovacStart *msg, int len)
04740 {
04741 PgStat_StatDBEntry *dbentry;
04742
04743
04744
04745
04746 dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
04747
04748 dbentry->last_autovac_time = msg->m_start_time;
04749 }
04750
04751
04752
04753
04754
04755
04756
04757 static void
04758 pgstat_recv_vacuum(PgStat_MsgVacuum *msg, int len)
04759 {
04760 PgStat_StatDBEntry *dbentry;
04761 PgStat_StatTabEntry *tabentry;
04762
04763
04764
04765
04766 dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
04767
04768 tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
04769
04770 tabentry->n_live_tuples = msg->m_tuples;
04771
04772 tabentry->n_dead_tuples = 0;
04773
04774 if (msg->m_autovacuum)
04775 {
04776 tabentry->autovac_vacuum_timestamp = msg->m_vacuumtime;
04777 tabentry->autovac_vacuum_count++;
04778 }
04779 else
04780 {
04781 tabentry->vacuum_timestamp = msg->m_vacuumtime;
04782 tabentry->vacuum_count++;
04783 }
04784 }
04785
04786
04787
04788
04789
04790
04791
04792 static void
04793 pgstat_recv_analyze(PgStat_MsgAnalyze *msg, int len)
04794 {
04795 PgStat_StatDBEntry *dbentry;
04796 PgStat_StatTabEntry *tabentry;
04797
04798
04799
04800
04801 dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
04802
04803 tabentry = pgstat_get_tab_entry(dbentry, msg->m_tableoid, true);
04804
04805 tabentry->n_live_tuples = msg->m_live_tuples;
04806 tabentry->n_dead_tuples = msg->m_dead_tuples;
04807
04808
04809
04810
04811
04812 tabentry->changes_since_analyze = 0;
04813
04814 if (msg->m_autovacuum)
04815 {
04816 tabentry->autovac_analyze_timestamp = msg->m_analyzetime;
04817 tabentry->autovac_analyze_count++;
04818 }
04819 else
04820 {
04821 tabentry->analyze_timestamp = msg->m_analyzetime;
04822 tabentry->analyze_count++;
04823 }
04824 }
04825
04826
04827
04828
04829
04830
04831
04832
04833 static void
04834 pgstat_recv_bgwriter(PgStat_MsgBgWriter *msg, int len)
04835 {
04836 globalStats.timed_checkpoints += msg->m_timed_checkpoints;
04837 globalStats.requested_checkpoints += msg->m_requested_checkpoints;
04838 globalStats.checkpoint_write_time += msg->m_checkpoint_write_time;
04839 globalStats.checkpoint_sync_time += msg->m_checkpoint_sync_time;
04840 globalStats.buf_written_checkpoints += msg->m_buf_written_checkpoints;
04841 globalStats.buf_written_clean += msg->m_buf_written_clean;
04842 globalStats.maxwritten_clean += msg->m_maxwritten_clean;
04843 globalStats.buf_written_backend += msg->m_buf_written_backend;
04844 globalStats.buf_fsync_backend += msg->m_buf_fsync_backend;
04845 globalStats.buf_alloc += msg->m_buf_alloc;
04846 }
04847
04848
04849
04850
04851
04852
04853
04854 static void
04855 pgstat_recv_recoveryconflict(PgStat_MsgRecoveryConflict *msg, int len)
04856 {
04857 PgStat_StatDBEntry *dbentry;
04858
04859 dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
04860
04861 switch (msg->m_reason)
04862 {
04863 case PROCSIG_RECOVERY_CONFLICT_DATABASE:
04864
04865
04866
04867
04868
04869 break;
04870 case PROCSIG_RECOVERY_CONFLICT_TABLESPACE:
04871 dbentry->n_conflict_tablespace++;
04872 break;
04873 case PROCSIG_RECOVERY_CONFLICT_LOCK:
04874 dbentry->n_conflict_lock++;
04875 break;
04876 case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT:
04877 dbentry->n_conflict_snapshot++;
04878 break;
04879 case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN:
04880 dbentry->n_conflict_bufferpin++;
04881 break;
04882 case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK:
04883 dbentry->n_conflict_startup_deadlock++;
04884 break;
04885 }
04886 }
04887
04888
04889
04890
04891
04892
04893
04894 static void
04895 pgstat_recv_deadlock(PgStat_MsgDeadlock *msg, int len)
04896 {
04897 PgStat_StatDBEntry *dbentry;
04898
04899 dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
04900
04901 dbentry->n_deadlocks++;
04902 }
04903
04904
04905
04906
04907
04908
04909
04910 static void
04911 pgstat_recv_tempfile(PgStat_MsgTempFile *msg, int len)
04912 {
04913 PgStat_StatDBEntry *dbentry;
04914
04915 dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
04916
04917 dbentry->n_temp_bytes += msg->m_filesize;
04918 dbentry->n_temp_files += 1;
04919 }
04920
04921
04922
04923
04924
04925
04926
04927 static void
04928 pgstat_recv_funcstat(PgStat_MsgFuncstat *msg, int len)
04929 {
04930 PgStat_FunctionEntry *funcmsg = &(msg->m_entry[0]);
04931 PgStat_StatDBEntry *dbentry;
04932 PgStat_StatFuncEntry *funcentry;
04933 int i;
04934 bool found;
04935
04936 dbentry = pgstat_get_db_entry(msg->m_databaseid, true);
04937
04938
04939
04940
04941 for (i = 0; i < msg->m_nentries; i++, funcmsg++)
04942 {
04943 funcentry = (PgStat_StatFuncEntry *) hash_search(dbentry->functions,
04944 (void *) &(funcmsg->f_id),
04945 HASH_ENTER, &found);
04946
04947 if (!found)
04948 {
04949
04950
04951
04952
04953 funcentry->f_numcalls = funcmsg->f_numcalls;
04954 funcentry->f_total_time = funcmsg->f_total_time;
04955 funcentry->f_self_time = funcmsg->f_self_time;
04956 }
04957 else
04958 {
04959
04960
04961
04962 funcentry->f_numcalls += funcmsg->f_numcalls;
04963 funcentry->f_total_time += funcmsg->f_total_time;
04964 funcentry->f_self_time += funcmsg->f_self_time;
04965 }
04966 }
04967 }
04968
04969
04970
04971
04972
04973
04974
04975 static void
04976 pgstat_recv_funcpurge(PgStat_MsgFuncpurge *msg, int len)
04977 {
04978 PgStat_StatDBEntry *dbentry;
04979 int i;
04980
04981 dbentry = pgstat_get_db_entry(msg->m_databaseid, false);
04982
04983
04984
04985
04986 if (!dbentry || !dbentry->functions)
04987 return;
04988
04989
04990
04991
04992 for (i = 0; i < msg->m_nentries; i++)
04993 {
04994
04995 (void) hash_search(dbentry->functions,
04996 (void *) &(msg->m_functionid[i]),
04997 HASH_REMOVE, NULL);
04998 }
04999 }
05000
05001
05002
05003
05004
05005
05006
05007 static bool
05008 pgstat_write_statsfile_needed(void)
05009 {
05010 if (!slist_is_empty(&last_statrequests))
05011 return true;
05012
05013
05014 return false;
05015 }
05016
05017
05018
05019
05020
05021
05022
05023 static bool
05024 pgstat_db_requested(Oid databaseid)
05025 {
05026 slist_iter iter;
05027
05028
05029 slist_foreach(iter, &last_statrequests)
05030 {
05031 DBWriteRequest *req = slist_container(DBWriteRequest, next, iter.cur);
05032
05033 if (req->databaseid == databaseid)
05034 return true;
05035 }
05036
05037 return false;
05038 }