📄 pgstat.c
字号:
voidpgstat_reset_counters(void){ PgStat_MsgResetcounter msg; if (pgStatSock < 0) return; if (!superuser()) ereport(ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), errmsg("must be superuser to reset statistics counters"))); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_RESETCOUNTER); msg.m_databaseid = MyDatabaseId; pgstat_send(&msg, sizeof(msg));}/* ---------- * pgstat_ping() - * * Send some junk data to the collector to increase traffic. * ---------- */voidpgstat_ping(void){ PgStat_MsgDummy msg; if (pgStatSock < 0) return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_DUMMY); pgstat_send(&msg, sizeof(msg));}/* * Enlarge a TabStatArray */static voidmore_tabstat_space(TabStatArray *tsarr){ PgStat_MsgTabstat *newMessages; PgStat_MsgTabstat **msgArray; int newAlloc; int i; AssertArg(PointerIsValid(tsarr)); newAlloc = tsarr->tsa_alloc + TABSTAT_QUANTUM; /* Create (another) quantum of message buffers */ newMessages = (PgStat_MsgTabstat *) MemoryContextAllocZero(TopMemoryContext, sizeof(PgStat_MsgTabstat) * TABSTAT_QUANTUM); /* Create or enlarge the pointer array */ if (tsarr->tsa_messages == NULL) msgArray = (PgStat_MsgTabstat **) MemoryContextAlloc(TopMemoryContext, sizeof(PgStat_MsgTabstat *) * newAlloc); else msgArray = (PgStat_MsgTabstat **) repalloc(tsarr->tsa_messages, sizeof(PgStat_MsgTabstat *) * newAlloc); for (i = 0; i < TABSTAT_QUANTUM; i++) msgArray[tsarr->tsa_alloc + i] = newMessages++; tsarr->tsa_messages = msgArray; tsarr->tsa_alloc = newAlloc; Assert(tsarr->tsa_used < tsarr->tsa_alloc);}/* ---------- * pgstat_initstats() - * * Called from various places usually dealing with initialization * of Relation or Scan structures. The data placed into these * structures from here tell where later to count for buffer reads, * scans and tuples fetched. * ---------- */voidpgstat_initstats(PgStat_Info *stats, Relation rel){ Oid rel_id = rel->rd_id; PgStat_TableEntry *useent; TabStatArray *tsarr; PgStat_MsgTabstat *tsmsg; int mb; int i; /* * Initialize data not to count at all. */ stats->tabentry = NULL; if (pgStatSock < 0 || !(pgstat_collect_tuplelevel || pgstat_collect_blocklevel)) return; tsarr = rel->rd_rel->relisshared ? &SharedTabStat : &RegularTabStat; /* * Search the already-used message slots for this relation. */ for (mb = 0; mb < tsarr->tsa_used; mb++) { tsmsg = tsarr->tsa_messages[mb]; for (i = tsmsg->m_nentries; --i >= 0;) { if (tsmsg->m_entry[i].t_id == rel_id) { stats->tabentry = (void *) &(tsmsg->m_entry[i]); return; } } if (tsmsg->m_nentries >= PGSTAT_NUM_TABENTRIES) continue; /* * Not found, but found a message buffer with an empty slot instead. * Fine, let's use this one. */ i = tsmsg->m_nentries++; useent = &tsmsg->m_entry[i]; MemSet(useent, 0, sizeof(PgStat_TableEntry)); useent->t_id = rel_id; stats->tabentry = (void *) useent; return; } /* * If we ran out of message buffers, we just allocate more. */ if (tsarr->tsa_used >= tsarr->tsa_alloc) more_tabstat_space(tsarr); /* * Use the first entry of the next message buffer. */ mb = tsarr->tsa_used++; tsmsg = tsarr->tsa_messages[mb]; tsmsg->m_nentries = 1; useent = &tsmsg->m_entry[0]; MemSet(useent, 0, sizeof(PgStat_TableEntry)); useent->t_id = rel_id; stats->tabentry = (void *) useent;}/* ---------- * pgstat_count_xact_commit() - * * Called from access/transam/xact.c to count transaction commits. * ---------- */voidpgstat_count_xact_commit(void){ if (!(pgstat_collect_querystring || pgstat_collect_tuplelevel || pgstat_collect_blocklevel)) return; pgStatXactCommit++; /* * If there was no relation activity yet, just make one existing message * buffer used without slots, causing the next report to tell new * xact-counters. */ if (RegularTabStat.tsa_alloc == 0) more_tabstat_space(&RegularTabStat); if (RegularTabStat.tsa_used == 0) { RegularTabStat.tsa_used++; RegularTabStat.tsa_messages[0]->m_nentries = 0; }}/* ---------- * pgstat_count_xact_rollback() - * * Called from access/transam/xact.c to count transaction rollbacks. * ---------- */voidpgstat_count_xact_rollback(void){ if (!(pgstat_collect_querystring || pgstat_collect_tuplelevel || pgstat_collect_blocklevel)) return; pgStatXactRollback++; /* * If there was no relation activity yet, just make one existing message * buffer used without slots, causing the next report to tell new * xact-counters. */ if (RegularTabStat.tsa_alloc == 0) more_tabstat_space(&RegularTabStat); if (RegularTabStat.tsa_used == 0) { RegularTabStat.tsa_used++; RegularTabStat.tsa_messages[0]->m_nentries = 0; }}/* ---------- * pgstat_fetch_stat_dbentry() - * * Support function for the SQL-callable pgstat* functions. Returns * the collected statistics for one database or NULL. NULL doesn't mean * that the database doesn't exist, it is just not yet known by the * collector, so the caller is better off to report ZERO instead. * ---------- */PgStat_StatDBEntry *pgstat_fetch_stat_dbentry(Oid dbid){ /* * If not done for this transaction, read the statistics collector stats * file into some hash tables. */ backend_read_statsfile(); /* * Lookup the requested database; return NULL if not found */ return (PgStat_StatDBEntry *) hash_search(pgStatDBHash, (void *) &dbid, HASH_FIND, NULL);}/* ---------- * pgstat_fetch_stat_tabentry() - * * Support function for the SQL-callable pgstat* functions. Returns * the collected statistics for one table or NULL. NULL doesn't mean * that the table doesn't exist, it is just not yet known by the * collector, so the caller is better off to report ZERO instead. * ---------- */PgStat_StatTabEntry *pgstat_fetch_stat_tabentry(Oid relid){ Oid dbid; PgStat_StatDBEntry *dbentry; PgStat_StatTabEntry *tabentry; /* * If not done for this transaction, read the statistics collector stats * file into some hash tables. */ backend_read_statsfile(); /* * Lookup our database, then look in its table hash table. */ dbid = MyDatabaseId; dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, (void *) &dbid, HASH_FIND, NULL); if (dbentry != NULL && dbentry->tables != NULL) { tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables, (void *) &relid, HASH_FIND, NULL); if (tabentry) return tabentry; } /* * If we didn't find it, maybe it's a shared table. */ dbid = InvalidOid; dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, (void *) &dbid, HASH_FIND, NULL); if (dbentry != NULL && dbentry->tables != NULL) { tabentry = (PgStat_StatTabEntry *) hash_search(dbentry->tables, (void *) &relid, HASH_FIND, NULL); if (tabentry) return tabentry; } return NULL;}/* ---------- * pgstat_fetch_stat_beentry() - * * Support function for the SQL-callable pgstat* functions. Returns * the actual activity slot of one active backend. The caller is * responsible for a check if the actual user is permitted to see * that info (especially the querystring). * ---------- */PgStat_StatBeEntry *pgstat_fetch_stat_beentry(int beid){ backend_read_statsfile(); if (beid < 1 || beid > pgStatNumBackends) return NULL; return &pgStatBeTable[beid - 1];}/* ---------- * pgstat_fetch_stat_numbackends() - * * Support function for the SQL-callable pgstat* functions. Returns * the maximum current backend id. * ---------- */intpgstat_fetch_stat_numbackends(void){ backend_read_statsfile(); return pgStatNumBackends;}/* ------------------------------------------------------------ * Local support functions follow * ------------------------------------------------------------ *//* ---------- * pgstat_setheader() - * * Set common header fields in a statistics message * ---------- */static voidpgstat_setheader(PgStat_MsgHdr *hdr, StatMsgType mtype){ hdr->m_type = mtype; hdr->m_backendid = MyBackendId; hdr->m_procpid = MyProcPid;}/* ---------- * pgstat_send() - * * Send out one statistics message to the collector * ---------- */static voidpgstat_send(void *msg, int len){ if (pgStatSock < 0) return; ((PgStat_MsgHdr *) msg)->m_size = len;#ifdef USE_ASSERT_CHECKING if (send(pgStatSock, msg, len, 0) < 0) elog(LOG, "could not send to statistics collector: %m");#else send(pgStatSock, msg, len, 0); /* We deliberately ignore any error from send() */#endif}/* ---------- * PgstatBufferMain() - * * Start up the statistics buffer process. This is the body of the * postmaster child process. * * The argc/argv parameters are valid only in EXEC_BACKEND case. * ---------- */NON_EXEC_STATIC voidPgstatBufferMain(int argc, char *argv[]){ IsUnderPostmaster = true; /* we are a postmaster subprocess now */ MyProcPid = getpid(); /* reset MyProcPid */ /* Lose the postmaster's on-exit routines */ on_exit_reset(); /* * Ignore all signals usually bound to some action in the postmaster, * except for SIGCHLD and SIGQUIT --- see pgstat_recvbuffer. */ pqsignal(SIGHUP, SIG_IGN); pqsignal(SIGINT, SIG_IGN); pqsignal(SIGTERM, SIG_IGN); pqsignal(SIGQUIT, pgstat_exit); pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SIG_IGN); pqsignal(SIGCHLD, pgstat_die); pqsignal(SIGTTIN, SIG_DFL); pqsignal(SIGTTOU, SIG_DFL); pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL); /* unblock will happen in pgstat_recvbuffer */#ifdef EXEC_BACKEND pgstat_parseArgs(argc, argv);#endif /* * Start a buffering process to read from the socket, so we have a little * more time to process incoming messages. * * NOTE: the process structure is: postmaster is parent of buffer process * is parent of collector process. This way, the buffer can detect * collector failure via SIGCHLD, whereas otherwise it wouldn't notice * collector failure until it tried to write on the pipe. That would mean * that after the postmaster started a new collector, we'd have two buffer * processes competing to read from the UDP socket --- not good. */ if (pgpipe(pgStatPipe) < 0) ereport(ERROR, (errcode_for_socket_access(), errmsg("could not create pipe for statistics buffer: %m"))); /* child becomes collector process */#ifdef EXEC_BACKEND pgStatCollectorPid = pgstat_forkexec(STAT_PROC_COLLECTOR);#else pgStatCollectorPid = fork();#endif switch (pgStatCollectorPid) { case -1: ereport(ERROR, (errmsg("could not fork statistics collector: %m")));#ifndef EXEC_BACKEND case 0: /* child becomes collector process */ PgstatCollectorMain(0, NULL); break;#endif default: /* parent becomes buffer process */ closesocket(pgStatPipe[0]); pgstat_recvbuffer(); } exit(0);}/* ---------- * PgstatCollectorMain() - * * Start up the statistics collector itself. This is the body of the * postmaster grandchild process. * * The argc/argv parameters are valid only in EXEC_BACKEND case. * ---------- */NON_EXEC_STATIC voidPgstatCollectorMain(int argc, char *argv[]){ PgStat_Msg msg; fd_set rfds; int readPipe; int nready; int len = 0; struct timeval timeout; struct timeval next_statwrite; bool need_statwrite; HASHCTL hash_ctl; MyProcPid = getpid(); /* reset MyProcPid */ /* * Reset signal handling. With the exception of restoring default SIGCHLD * and SIGQUIT handling, this is a no-op in the non-EXEC_BACKEND case * because we'll have inherited these settings from the buffer process; * but it's not a no-op for EXEC_BACKEND. */ pqsignal(SIGHUP, SIG_IGN); pqsignal(SIGINT, SIG_IGN); pqsignal(SIGTERM, SIG_IGN);#ifndef WIN32 pqsignal(SIGQUIT, SIG_IGN);#else /* kluge to allow buffer process to kill collector; FIXME */ pqsignal(SIGQUIT, pgstat_exit);#endif pqsignal(SIGALRM, SIG_IGN); pqsignal(SIGPIPE, SIG_IGN); pqsignal(SIGUSR1, SIG_IGN); pqsignal(SIGUSR2, SIG_IGN); pqsignal(SIGCHLD, SIG_DFL); pqsignal(SIGTTIN, SIG_DFL); pqsignal(SIGTTOU, SIG_DFL); pqsignal(SIGCONT, SIG_DFL); pqsignal(SIGWINCH, SIG_DFL); PG_SETMASK(&UnBlockSig);#ifdef EXEC_BACKEND pgstat_parseArgs(argc, argv);#endif /* Close unwanted files */ closesocket(pgStatPipe[1]);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -