📄 pgstat.c
字号:
/* ---------- * pgstat.c * * All the statistics collector stuff hacked up in one big, ugly file. * * TODO: - Separate collector, postmaster and backend stuff * into different files. * * - Add some automatic call for pgstat vacuuming. * * - Add a pgstat config column to pg_database, so this * entire thing can be enabled/disabled on a per db base. * * Copyright (c) 2001-2003, PostgreSQL Global Development Group * * $Header: /cvsroot/pgsql/src/backend/postmaster/pgstat.c,v 1.45.2.2 2003/11/15 17:24:19 tgl Exp $ * ---------- */#include "postgres.h"#include <unistd.h>#include <fcntl.h>#include <sys/param.h>#include <sys/time.h>#include <sys/types.h>#include <sys/socket.h>#include <netdb.h>#include <netinet/in.h>#include <arpa/inet.h>#include <errno.h>#include <signal.h>#include "pgstat.h"#include "access/xact.h"#include "access/heapam.h"#include "catalog/catname.h"#include "catalog/pg_shadow.h"#include "catalog/pg_database.h"#include "libpq/pqsignal.h"#include "libpq/libpq.h"#include "mb/pg_wchar.h"#include "miscadmin.h"#include "utils/memutils.h"#include "storage/backendid.h"#include "storage/ipc.h"#include "storage/pg_shmem.h"#include "utils/rel.h"#include "utils/hsearch.h"#include "utils/ps_status.h"#include "utils/syscache.h"/* ---------- * GUC parameters * ---------- */bool pgstat_collect_startcollector = true;bool pgstat_collect_resetonpmstart = true;bool pgstat_collect_querystring = false;bool pgstat_collect_tuplelevel = false;bool pgstat_collect_blocklevel = false;/* ---------- * Other global variables * ---------- */bool pgstat_is_running = false;/* ---------- * Local data * ---------- */static int pgStatSock = -1;static int pgStatPipe[2];static struct sockaddr_storage pgStatAddr;static int pgStatPmPipe[2] = {-1, -1};static int pgStatPid;static time_t last_pgstat_start_time;static long pgStatNumMessages = 0;static bool pgStatRunningInCollector = FALSE;static int pgStatTabstatAlloc = 0;static int pgStatTabstatUsed = 0;static PgStat_MsgTabstat **pgStatTabstatMessages = NULL;#define TABSTAT_QUANTUM 4 /* we alloc this many at a time */static int pgStatXactCommit = 0;static int pgStatXactRollback = 0;static TransactionId pgStatDBHashXact = InvalidTransactionId;static HTAB *pgStatDBHash = NULL;static HTAB *pgStatBeDead = NULL;static PgStat_StatBeEntry *pgStatBeTable = NULL;static int pgStatNumBackends = 0;static char pgStat_tmpfname[MAXPGPATH];static char pgStat_fname[MAXPGPATH];/* ---------- * Local function forward declarations * ---------- */static void pgstat_main(void);static void pgstat_recvbuffer(void);static void pgstat_die(SIGNAL_ARGS);static int pgstat_add_backend(PgStat_MsgHdr *msg);static void pgstat_sub_backend(int procpid);static void pgstat_drop_database(Oid databaseid);static void pgstat_write_statsfile(void);static void pgstat_read_statsfile(HTAB **dbhash, Oid onlydb, PgStat_StatBeEntry **betab, int *numbackends);static void pgstat_setheader(PgStat_MsgHdr *hdr, int mtype);static void pgstat_send(void *msg, int len);static void pgstat_recv_bestart(PgStat_MsgBestart *msg, int len);static void pgstat_recv_beterm(PgStat_MsgBeterm *msg, int len);static void pgstat_recv_activity(PgStat_MsgActivity *msg, int len);static void pgstat_recv_tabstat(PgStat_MsgTabstat *msg, int len);static void pgstat_recv_tabpurge(PgStat_MsgTabpurge *msg, int len);static void pgstat_recv_dropdb(PgStat_MsgDropdb *msg, int len);static void pgstat_recv_resetcounter(PgStat_MsgResetcounter *msg, int len);/* ------------------------------------------------------------ * Public functions called from postmaster follow * ------------------------------------------------------------ *//* ---------- * pgstat_init() - * * Called from postmaster at startup. Create the resources required * by the statistics collector process. If unable to do so, do not * fail --- better to let the postmaster start with stats collection * disabled. * ---------- */voidpgstat_init(void){ ACCEPT_TYPE_ARG3 alen; struct addrinfo *addrs = NULL, *addr, hints; int ret; /* * Force start of collector daemon if something to collect */ if (pgstat_collect_querystring || pgstat_collect_tuplelevel || pgstat_collect_blocklevel) pgstat_collect_startcollector = true; /* * Initialize the filenames for the status reports. */ snprintf(pgStat_tmpfname, MAXPGPATH, PGSTAT_STAT_TMPFILE, DataDir, getpid()); snprintf(pgStat_fname, MAXPGPATH, PGSTAT_STAT_FILENAME, DataDir); /* * If we don't have to start a collector or should reset the collected * statistics on postmaster start, simply remove the file. */ if (!pgstat_collect_startcollector || pgstat_collect_resetonpmstart) unlink(pgStat_fname); /* * Nothing else required if collector will not get started */ if (!pgstat_collect_startcollector) return; /* * Create the UDP socket for sending and receiving statistic messages */ hints.ai_flags = AI_PASSIVE; hints.ai_family = PF_UNSPEC; hints.ai_socktype = SOCK_DGRAM; hints.ai_protocol = 0; hints.ai_addrlen = 0; hints.ai_addr = NULL; hints.ai_canonname = NULL; hints.ai_next = NULL; ret = getaddrinfo_all("localhost", NULL, &hints, &addrs); if (ret || !addrs) { ereport(LOG, (errmsg("could not resolve \"localhost\": %s", gai_strerror(ret)))); goto startup_failed; } /* * On some platforms, getaddrinfo_all() may return multiple addresses * only one of which will actually work (eg, both IPv6 and IPv4 addresses * when kernel will reject IPv6). Worse, the failure may occur at the * bind() or perhaps even connect() stage. So we must loop through the * results till we find a working combination. We will generate LOG * messages, but no error, for bogus combinations. */ for (addr = addrs; addr; addr = addr->ai_next) {#ifdef HAVE_UNIX_SOCKETS /* Ignore AF_UNIX sockets, if any are returned. */ if (addr->ai_family == AF_UNIX) continue;#endif /* * Create the socket. */ if ((pgStatSock = socket(addr->ai_family, SOCK_DGRAM, 0)) < 0) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not create socket for statistics collector: %m"))); continue; } /* * Bind it to a kernel assigned port on localhost and get the assigned * port via getsockname(). */ if (bind(pgStatSock, addr->ai_addr, addr->ai_addrlen) < 0) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not bind socket for statistics collector: %m"))); closesocket(pgStatSock); pgStatSock = -1; continue; } alen = sizeof(pgStatAddr); if (getsockname(pgStatSock, (struct sockaddr *) &pgStatAddr, &alen) < 0) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not get address of socket for statistics collector: %m"))); closesocket(pgStatSock); pgStatSock = -1; continue; } /* * Connect the socket to its own address. This saves a few cycles by * not having to respecify the target address on every send. This also * provides a kernel-level check that only packets from this same * address will be received. */ if (connect(pgStatSock, (struct sockaddr *) &pgStatAddr, alen) < 0) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not connect socket for statistics collector: %m"))); closesocket(pgStatSock); pgStatSock = -1; continue; } /* If we get here, we have a working socket */ break; } /* Did we find a working address? */ if (!addr || pgStatSock < 0) { ereport(LOG, (errcode_for_socket_access(), errmsg("disabling statistics collector for lack of working socket"))); goto startup_failed; } /* * Set the socket to non-blocking IO. This ensures that if the * collector falls behind (despite the buffering process), statistics * messages will be discarded; backends won't block waiting to send * messages to the collector. */ if (FCNTL_NONBLOCK(pgStatSock) < 0) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not set statistics collector socket to nonblocking mode: %m"))); goto startup_failed; } /* * Create the pipe that controls the statistics collector shutdown */ if (pipe(pgStatPmPipe) < 0) { ereport(LOG, (errcode_for_socket_access(), errmsg("could not create pipe for statistics collector: %m"))); goto startup_failed; } freeaddrinfo_all(hints.ai_family, addrs); return;startup_failed: if (addrs) freeaddrinfo_all(hints.ai_family, addrs); if (pgStatSock >= 0) closesocket(pgStatSock); pgStatSock = -1; /* Adjust GUC variables to suppress useless activity */ pgstat_collect_startcollector = false; pgstat_collect_querystring = false; pgstat_collect_tuplelevel = false; pgstat_collect_blocklevel = false;}/* ---------- * pgstat_start() - * * Called from postmaster at startup or after an existing collector * died. Attempt to fire up a fresh statistics collector. * * Note: if fail, we will be called again from the postmaster main loop. * ---------- */voidpgstat_start(void){ time_t curtime; /* * Do nothing if no collector needed */ if (pgstat_is_running || !pgstat_collect_startcollector) return; /* * Do nothing if too soon since last collector start. This is a * safety valve to protect against continuous respawn attempts if the * collector is dying immediately at launch. Note that since we will * be re-called from the postmaster main loop, we will get another * chance later. */ curtime = time(NULL); if ((unsigned int) (curtime - last_pgstat_start_time) < (unsigned int) PGSTAT_RESTART_INTERVAL) return; last_pgstat_start_time = curtime; /* * Check that the socket is there, else pgstat_init failed. */ if (pgStatSock < 0) { ereport(LOG, (errmsg("statistics collector startup skipped"))); /* * We can only get here if someone tries to manually turn * pgstat_collect_startcollector on after it had been off. */ pgstat_collect_startcollector = false; return; } /* * Okay, fork off the collector. Remember its PID for * pgstat_ispgstat. */ fflush(stdout); fflush(stderr);#ifdef __BEOS__ /* Specific beos actions before backend startup */ beos_before_backend_startup();#endif switch ((pgStatPid = (int) fork())) { case -1:#ifdef __BEOS__ /* Specific beos actions */ beos_backend_startup_failed();#endif ereport(LOG, (errmsg("could not fork statistics buffer: %m"))); return; case 0: break; default: pgstat_is_running = true; return; } /* in postmaster child ... */#ifdef __BEOS__ /* Specific beos actions after backend startup */ beos_backend_startup();#endif IsUnderPostmaster = true; /* we are a postmaster subprocess now */ MyProcPid = getpid(); /* reset MyProcPid */ /* Lose the postmaster's on-exit routines */ on_exit_reset(); /* Close the postmaster's sockets, except for pgstat link */ ClosePostmasterPorts(false); /* Drop our connection to postmaster's shared memory, as well */ PGSharedMemoryDetach(); pgstat_main(); exit(0);}/* ---------- * pgstat_ispgstat() - * * Called from postmaster to check if a terminated child process * was the statistics collector. * ---------- */boolpgstat_ispgstat(int pid){ if (!pgstat_is_running) return false; if (pgStatPid != pid) return false; /* Oh dear ... */ pgstat_is_running = false; return true;}/* ---------- * pgstat_close_sockets() - * * Called when postmaster forks a non-pgstat child process, to close off * file descriptors that should not be held open in child processes. * ---------- */voidpgstat_close_sockets(void){ if (pgStatPmPipe[0] >= 0) closesocket(pgStatPmPipe[0]); pgStatPmPipe[0] = -1; if (pgStatPmPipe[1] >= 0) closesocket(pgStatPmPipe[1]); pgStatPmPipe[1] = -1;}/* ---------- * pgstat_beterm() - * * Called from postmaster to tell collector a backend terminated. * ---------- */voidpgstat_beterm(int pid){ PgStat_MsgBeterm msg; if (pgStatSock < 0) return; MemSet(&(msg.m_hdr), 0, sizeof(msg.m_hdr)); msg.m_hdr.m_type = PGSTAT_MTYPE_BETERM; msg.m_hdr.m_procpid = pid; pgstat_send(&msg, sizeof(msg));}/* ------------------------------------------------------------ * Public functions used by backends follow *------------------------------------------------------------ *//* ---------- * pgstat_bestart() - * * Tell the collector that this new backend is soon ready to process * queries. Called from tcop/postgres.c before entering the mainloop. * ---------- */voidpgstat_bestart(void){ PgStat_MsgBestart msg; if (pgStatSock < 0) return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_BESTART); pgstat_send(&msg, sizeof(msg));}/* ---------- * pgstat_report_activity() - * * Called in tcop/postgres.c to tell the collector what the backend * is actually doing (usually "<IDLE>" or the start of the query to * be executed). * ---------- */voidpgstat_report_activity(const char *what){ PgStat_MsgActivity msg; int len; if (!pgstat_collect_querystring || pgStatSock < 0) return; len = strlen(what); len = pg_mbcliplen((const unsigned char *) what, len, PGSTAT_ACTIVITY_SIZE - 1); memcpy(msg.m_what, what, len); msg.m_what[len] = '\0'; len += offsetof(PgStat_MsgActivity, m_what) +1; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ACTIVITY); pgstat_send(&msg, len);}/* ---------- * pgstat_report_tabstat() -
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -