📄 pgstat.c
字号:
argc = 3; StrNCpy(postgres_exec_path, argv[argc++], MAXPGPATH);}#endif /* EXEC_BACKEND *//* ---------- * pgstat_start() - * * Called from postmaster at startup or after an existing collector * died. Attempt to fire up a fresh statistics collector. * * Returns PID of child process, or 0 if fail. * * Note: if fail, we will be called again from the postmaster main loop. * ---------- */intpgstat_start(void){ time_t curtime; pid_t pgStatPid; /* * Do nothing if no collector needed */ if (!pgstat_collect_startcollector) return 0; /* * 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 0; 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 0; } /* * Okay, fork off the collector. */#ifdef EXEC_BACKEND switch ((pgStatPid = pgstat_forkexec(STAT_PROC_BUFFER)))#else switch ((pgStatPid = fork_process()))#endif { case -1: ereport(LOG, (errmsg("could not fork statistics buffer: %m"))); return 0;#ifndef EXEC_BACKEND case 0: /* in postmaster child ... */ /* Close the postmaster's sockets */ ClosePostmasterPorts(false); /* Drop our connection to postmaster's shared memory, as well */ PGSharedMemoryDetach(); PgstatBufferMain(0, NULL); break;#endif default: return (int) pgStatPid; } /* shouldn't get here */ return 0;}/* ---------- * pgstat_beterm() - * * Called from postmaster to tell collector a backend terminated. * ---------- */voidpgstat_beterm(int pid){ PgStat_MsgBeterm msg; if (pgStatSock < 0) return; /* can't use pgstat_setheader() because it's not called in a backend */ 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));}/* ---------- * pgstat_report_autovac() - * * Called from autovacuum.c to report startup of an autovacuum process. * We are called before InitPostgres is done, so can't rely on MyDatabaseId; * the db OID must be passed in, instead. * ---------- */voidpgstat_report_autovac(Oid dboid){ PgStat_MsgAutovacStart msg; if (pgStatSock < 0) return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_AUTOVAC_START); msg.m_databaseid = dboid; msg.m_start_time = GetCurrentTimestamp(); 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 InitPostgres. * ---------- */voidpgstat_bestart(void){ PgStat_MsgBestart msg; if (pgStatSock < 0) return; /* * We may not have a MyProcPort (eg, if this is the autovacuum process). * Send an all-zeroes client address, which is dealt with specially in * pg_stat_get_backend_client_addr and pg_stat_get_backend_client_port. */ pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_BESTART); msg.m_databaseid = MyDatabaseId; msg.m_userid = GetSessionUserId(); if (MyProcPort) memcpy(&msg.m_clientaddr, &MyProcPort->raddr, sizeof(msg.m_clientaddr)); else MemSet(&msg.m_clientaddr, 0, sizeof(msg.m_clientaddr)); pgstat_send(&msg, sizeof(msg)); /* * Set up a process-exit hook to ensure we flush the last batch of * statistics to the collector. */ on_shmem_exit(pgstat_beshutdown_hook, 0);}/* --------- * pgstat_report_vacuum() - * * Tell the collector about the table we just vacuumed. * --------- */voidpgstat_report_vacuum(Oid tableoid, bool shared, bool analyze, PgStat_Counter tuples){ PgStat_MsgVacuum msg; if (pgStatSock < 0 || !pgstat_collect_tuplelevel) return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_VACUUM); msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; msg.m_tableoid = tableoid; msg.m_analyze = analyze; msg.m_tuples = tuples; pgstat_send(&msg, sizeof(msg));}/* -------- * pgstat_report_analyze() - * * Tell the collector about the table we just analyzed. * -------- */voidpgstat_report_analyze(Oid tableoid, bool shared, PgStat_Counter livetuples, PgStat_Counter deadtuples){ PgStat_MsgAnalyze msg; if (pgStatSock < 0 || !pgstat_collect_tuplelevel) return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_ANALYZE); msg.m_databaseid = shared ? InvalidOid : MyDatabaseId; msg.m_tableoid = tableoid; msg.m_live_tuples = livetuples; msg.m_dead_tuples = deadtuples; pgstat_send(&msg, sizeof(msg));}/* * Flush any remaining statistics counts out to the collector at process * exit. Without this, operations triggered during backend exit (such as * temp table deletions) won't be counted. */static voidpgstat_beshutdown_hook(int code, Datum arg){ pgstat_report_tabstat();}/* ---------- * pgstat_report_activity() - * * Called from 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(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() - * * Called from tcop/postgres.c to send the so far collected * per table access statistics to the collector. * ---------- */voidpgstat_report_tabstat(void){ int i; if (pgStatSock < 0 || !(pgstat_collect_querystring || pgstat_collect_tuplelevel || pgstat_collect_blocklevel)) { /* Not reporting stats, so just flush whatever we have */ RegularTabStat.tsa_used = 0; SharedTabStat.tsa_used = 0; return; } /* * For each message buffer used during the last query set the header * fields and send it out. */ for (i = 0; i < RegularTabStat.tsa_used; i++) { PgStat_MsgTabstat *tsmsg = RegularTabStat.tsa_messages[i]; int n; int len; n = tsmsg->m_nentries; len = offsetof(PgStat_MsgTabstat, m_entry[0]) + n * sizeof(PgStat_TableEntry); tsmsg->m_xact_commit = pgStatXactCommit; tsmsg->m_xact_rollback = pgStatXactRollback; pgStatXactCommit = 0; pgStatXactRollback = 0; pgstat_setheader(&tsmsg->m_hdr, PGSTAT_MTYPE_TABSTAT); tsmsg->m_databaseid = MyDatabaseId; pgstat_send(tsmsg, len); } RegularTabStat.tsa_used = 0; /* Ditto, for shared relations */ for (i = 0; i < SharedTabStat.tsa_used; i++) { PgStat_MsgTabstat *tsmsg = SharedTabStat.tsa_messages[i]; int n; int len; n = tsmsg->m_nentries; len = offsetof(PgStat_MsgTabstat, m_entry[0]) + n * sizeof(PgStat_TableEntry); /* We don't report transaction commit/abort here */ tsmsg->m_xact_commit = 0; tsmsg->m_xact_rollback = 0; pgstat_setheader(&tsmsg->m_hdr, PGSTAT_MTYPE_TABSTAT); tsmsg->m_databaseid = InvalidOid; pgstat_send(tsmsg, len); } SharedTabStat.tsa_used = 0;}/* ---------- * pgstat_vacuum_tabstat() - * * Will tell the collector about objects he can get rid of. * ---------- */voidpgstat_vacuum_tabstat(void){ List *oidlist; Relation rel; HeapScanDesc scan; HeapTuple tup; PgStat_MsgTabpurge msg; HASH_SEQ_STATUS hstat; PgStat_StatDBEntry *dbentry; PgStat_StatTabEntry *tabentry; int len; if (pgStatSock < 0) return; /* * If not done for this transaction, read the statistics collector stats * file into some hash tables. */ backend_read_statsfile(); /* * Read pg_database and make a list of OIDs of all existing databases */ oidlist = NIL; rel = heap_open(DatabaseRelationId, AccessShareLock); scan = heap_beginscan(rel, SnapshotNow, 0, NULL); while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL) { oidlist = lappend_oid(oidlist, HeapTupleGetOid(tup)); } heap_endscan(scan); heap_close(rel, AccessShareLock); /* * Search the database hash table for dead databases and tell the * collector to drop them. */ hash_seq_init(&hstat, pgStatDBHash); while ((dbentry = (PgStat_StatDBEntry *) hash_seq_search(&hstat)) != NULL) { Oid dbid = dbentry->databaseid; if (!list_member_oid(oidlist, dbid)) pgstat_drop_database(dbid); } /* Clean up */ list_free(oidlist); /* * Lookup our own database entry; if not found, nothing more to do. */ dbentry = (PgStat_StatDBEntry *) hash_search(pgStatDBHash, (void *) &MyDatabaseId, HASH_FIND, NULL); if (dbentry == NULL || dbentry->tables == NULL) return; /* * Similarly to above, make a list of all known relations in this DB. */ oidlist = NIL; rel = heap_open(RelationRelationId, AccessShareLock); scan = heap_beginscan(rel, SnapshotNow, 0, NULL); while ((tup = heap_getnext(scan, ForwardScanDirection)) != NULL) { oidlist = lappend_oid(oidlist, HeapTupleGetOid(tup)); } heap_endscan(scan); heap_close(rel, AccessShareLock); /* * Initialize our messages table counter to zero */ msg.m_nentries = 0; /* * Check for all tables listed in stats hashtable if they still exist. */ hash_seq_init(&hstat, dbentry->tables); while ((tabentry = (PgStat_StatTabEntry *) hash_seq_search(&hstat)) != NULL) { if (list_member_oid(oidlist, tabentry->tableid)) continue; /* * Not there, so add this table's Oid to the message */ msg.m_tableid[msg.m_nentries++] = tabentry->tableid; /* * If the message is full, send it out and reinitialize to empty */ if (msg.m_nentries >= PGSTAT_NUM_TABPURGE) { len = offsetof(PgStat_MsgTabpurge, m_tableid[0]) +msg.m_nentries * sizeof(Oid); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE); msg.m_databaseid = MyDatabaseId; pgstat_send(&msg, len); msg.m_nentries = 0; } } /* * Send the rest */ if (msg.m_nentries > 0) { len = offsetof(PgStat_MsgTabpurge, m_tableid[0]) +msg.m_nentries * sizeof(Oid); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE); msg.m_databaseid = MyDatabaseId; pgstat_send(&msg, len); } /* Clean up */ list_free(oidlist);}/* ---------- * pgstat_drop_database() - * * Tell the collector that we just dropped a database. * (If the message gets lost, we will still clean the dead DB eventually * via future invocations of pgstat_vacuum_tabstat().) * ---------- */static voidpgstat_drop_database(Oid databaseid){ PgStat_MsgDropdb msg; if (pgStatSock < 0) return; pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_DROPDB); msg.m_databaseid = databaseid; pgstat_send(&msg, sizeof(msg));}/* ---------- * pgstat_drop_relation() - * * Tell the collector that we just dropped a relation. * (If the message gets lost, we will still clean the dead entry eventually * via future invocations of pgstat_vacuum_tabstat().) * ---------- */voidpgstat_drop_relation(Oid relid){ PgStat_MsgTabpurge msg; int len; if (pgStatSock < 0) return; msg.m_tableid[0] = relid; msg.m_nentries = 1; len = offsetof(PgStat_MsgTabpurge, m_tableid[0]) + sizeof(Oid); pgstat_setheader(&msg.m_hdr, PGSTAT_MTYPE_TABPURGE); msg.m_databaseid = MyDatabaseId; pgstat_send(&msg, len);}/* ---------- * pgstat_reset_counters() - * * Tell the statistics collector to reset counters for our database. * ---------- */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -