Added examples of using pg_proctab.
authorMark Wong <markwkm@gmail.com>
Fri, 24 Apr 2009 05:56:17 +0000 (22:56 -0700)
committerMark Wong <markwkm@gmail.com>
Fri, 24 Apr 2009 05:56:17 +0000 (22:56 -0700)
contrib/create-ps_procstat-tables.sql [new file with mode: 0644]
contrib/create-ps_snap_stats.sql [new file with mode: 0644]
contrib/drop-ps_procstat-tables.sql [new file with mode: 0644]
contrib/ps-io-utilization.sh [new file with mode: 0755]
contrib/ps-processor-utilization.sh [new file with mode: 0755]
contrib/ps-report.pl [new file with mode: 0755]
contrib/ps_procstat-snap.sql [new file with mode: 0644]

diff --git a/contrib/create-ps_procstat-tables.sql b/contrib/create-ps_procstat-tables.sql
new file mode 100644 (file)
index 0000000..4ae212a
--- /dev/null
@@ -0,0 +1,147 @@
+CREATE TABLE ps_snaps(
+       snap BIGSERIAL PRIMARY KEY,
+       time TIMESTAMP WITH TIME ZONE DEFAULT NOW()
+);
+
+-- PostgreSQL Processes Stats
+CREATE TABLE ps_procstat(
+       snap BIGINT,
+       pid INTEGER,
+       comm VARCHAR,
+       fullcomm VARCHAR,
+       state CHAR,
+       ppid INTEGER,
+       pgrp INTEGER,
+       session INTEGER,
+       tty_nr INTEGER,
+       tpgid INTEGER,
+       flags INTEGER,
+       minflt BIGINT,
+       cminflt BIGINT,
+       majflt BIGINT,
+       cmajflt BIGINT,
+       utime BIGINT,
+       stime BIGINT,
+       cutime BIGINT,
+       cstime BIGINT,
+       priority BIGINT,
+       nice BIGINT,
+       num_threads BIGINT,
+       itrealvalue BIGINT,
+       starttime BIGINT,
+       vsize BIGINT,
+       rss BIGINT,
+       exit_signal INTEGER,
+       processor INTEGER,
+       rt_priority BIGINT,
+       policy BIGINT,
+       delayacct_blkio_ticks BIGINT,
+       uid INTEGER,
+       username VARCHAR,
+       rchar BIGINT,
+       wchar BIGINT,
+       syscr BIGINT,
+       syscw BIGINT,
+       reads BIGINT,
+       writes BIGINT,
+       cwrites BIGINT,
+       datid BIGINT,
+       datname NAME,
+       usesysid BIGINT,
+       usename NAME,
+       current_query TEXT,
+       waiting BOOLEAN,
+       query_start TIMESTAMP WITH TIME ZONE,
+       backend_start TIMESTAMP WITH TIME ZONE,
+       client_addr INET,
+       client_port INTEGER,
+       CONSTRAINT proc_snap
+               FOREIGN KEY (snap)
+               REFERENCES ps_snaps (snap)
+);
+
+-- PostgreSQL Database Stats
+CREATE TABLE ps_dbstat(
+       snap BIGINT,
+       datid BIGINT,
+       datname NAME,
+       numbackends INTEGER,
+       xact_commit BIGINT,
+       xact_rollback BIGINT,
+       blks_read BIGINT,
+       blks_hit BIGINT,
+       PRIMARY KEY (snap, datid),
+       CONSTRAINT db_snap
+               FOREIGN KEY (snap)
+               REFERENCES ps_snaps (snap)
+);
+
+-- PostgreSQL Table Stats
+CREATE TABLE ps_tablestat(
+       snap BIGINT,
+       relid BIGINT,
+       schemaname NAME,
+       relname NAME,
+       seq_scan BIGINT,
+       seq_tup_read BIGINT,
+       idx_scan BIGINT,
+       idx_tup_fetch BIGINT,
+       n_tup_ins BIGINT,
+       n_tup_upd BIGINT,
+       n_tup_del BIGINT,
+       last_vacuum TIMESTAMP WITH TIME ZONE,
+       last_autovacuum TIMESTAMP WITH TIME ZONE,
+       last_analyze TIMESTAMP WITH TIME ZONE,
+       last_autoanalyze TIMESTAMP WITH TIME ZONE,
+       PRIMARY KEY (snap, relid),
+       CONSTRAINT table_snap
+               FOREIGN KEY (snap)
+               REFERENCES ps_snaps (snap)
+);
+
+-- PostgreSQL Index Stats
+CREATE TABLE ps_indexstat(
+       snap BIGINT,
+       relid BIGINT,
+       indexrelid BIGINT,
+       schemaname NAME,
+       relname NAME,
+       indexrelname NAME,
+       idx_scan BIGINT,
+       idx_tup_read BIGINT,
+       idx_tup_fetch BIGINT,
+       PRIMARY KEY (snap, relid, indexrelid),
+       CONSTRAINT index_snap
+               FOREIGN KEY (snap)
+               REFERENCES ps_snaps (snap)
+);
+
+-- System Processor Stats
+CREATE TABLE ps_cpustat(
+       snap BIGINT PRIMARY KEY,
+       cpu_user BIGINT,
+       cpu_nice BIGINT,
+       cpu_system BIGINT,
+       cpu_idle BIGINT,
+       cpu_iowait BIGINT,
+       cpu_swap BIGINT,
+       CONSTRAINT cpu_snap
+               FOREIGN KEY (snap)
+               REFERENCES ps_snaps (snap)
+);
+
+-- System Memory Stats
+CREATE TABLE ps_memstat(
+       snap BIGINT PRIMARY KEY,
+       memused BIGINT,
+       memfree BIGINT,
+       memshared BIGINT,
+       membuffers BIGINT,
+       memcached BIGINT,
+       swapused BIGINT,
+       swapfree BIGINT,
+       swapcached BIGINT,
+       CONSTRAINT mem_snap
+               FOREIGN KEY (snap)
+               REFERENCES ps_snaps (snap)
+);
diff --git a/contrib/create-ps_snap_stats.sql b/contrib/create-ps_snap_stats.sql
new file mode 100644 (file)
index 0000000..a601888
--- /dev/null
@@ -0,0 +1,66 @@
+CREATE OR REPLACE FUNCTION ps_snap_stats() RETURNS BIGINT AS $$
+DECLARE
+       snapid BIGINT;
+BEGIN
+       -- Create the snapshot id.
+       INSERT INTO ps_snaps
+       DEFAULT VALUES
+       RETURNING snap
+       INTO snapid;
+       RAISE DEBUG 'Creating snapshot: %', snapid;
+
+       -- Get system stats.
+       INSERT INTO ps_cpustat(snap, cpu_user, cpu_nice, cpu_system, cpu_idle,
+                       cpu_iowait)
+       SELECT snapid, "user", nice, system, idle, iowait
+       FROM pg_cputime();
+
+       INSERT INTO ps_memstat(snap, memused, memfree, memshared, membuffers,
+                       memcached, swapused, swapfree, swapcached)
+       SELECT snapid, memused, memfree, memshared, membuffers, memcached,
+                       swapused, swapfree, swapcached
+       FROM pg_memusage();
+
+       -- Get database stats.
+       INSERT INTO ps_dbstat(snap, datid, datname, numbackends, xact_commit,
+                       xact_rollback, blks_read, blks_hit)
+       SELECT snapid, datid, datname, numbackends, xact_commit,
+                       xact_rollback, blks_read, blks_hit
+       FROM pg_catalog.pg_stat_database;
+
+       INSERT INTO ps_tablestat(snap, relid, schemaname, relname, seq_scan,
+                       seq_tup_read, idx_scan, idx_tup_fetch, n_tup_ins, n_tup_upd,
+                       n_tup_del, last_vacuum, last_autovacuum, last_analyze,
+                       last_autoanalyze)
+       SELECT snapid, relid, schemaname, relname, seq_scan,
+                       seq_tup_read, idx_scan, idx_tup_fetch, n_tup_ins, n_tup_upd,
+                       n_tup_del, last_vacuum, last_autovacuum, last_analyze,
+                       last_autoanalyze
+       FROM pg_catalog.pg_stat_all_tables;
+
+       INSERT INTO ps_indexstat(snap, relid, indexrelid, schemaname, relname,
+                       indexrelname, idx_scan, idx_tup_read, idx_tup_fetch)
+       SELECT snapid, relid, indexrelid, schemaname, relname,
+                       indexrelname, idx_scan, idx_tup_read, idx_tup_fetch
+       FROM pg_catalog.pg_stat_all_indexes;
+
+       -- Get process stats.
+       INSERT INTO ps_procstat(snap, pid, comm, fullcomm, state, ppid, pgrp,
+                       session, tty_nr, tpgid, flags, minflt, cminflt, majflt, cmajflt,
+                       utime, stime, cutime, cstime, priority, nice, num_threads,
+                       itrealvalue, starttime, vsize, rss, exit_signal, processor,
+                       rt_priority, policy, delayacct_blkio_ticks, uid, username,
+                       syscr, syscw, reads, writes, cwrites)
+       SELECT snapid, procpid, comm, fullcomm, state, ppid, pgrp, session, tty_nr,
+                       tpgid, flags, minflt, cminflt, majflt, cmajflt, utime, stime,
+                       cutime, cstime, priority, nice, num_threads, itrealvalue,
+                       starttime, vsize, rss, exit_signal, processor, rt_priority,
+                       policy, delayacct_blkio_ticks, uid, username, syscr, syscw,
+                       reads, writes, cwrites
+       FROM pg_stat_activity, pg_proctab()
+       WHERE procpid = pid;
+
+       -- Return the id of the snapshot just created.
+       RETURN snapid;
+END;
+$$ LANGUAGE plpgsql;
diff --git a/contrib/drop-ps_procstat-tables.sql b/contrib/drop-ps_procstat-tables.sql
new file mode 100644 (file)
index 0000000..9cdecaf
--- /dev/null
@@ -0,0 +1,7 @@
+DROP TABLE ps_indexstat;
+DROP TABLE ps_tablestat;
+DROP TABLE ps_dbstat;
+DROP TABLE ps_memstat;
+DROP TABLE ps_cpustat;
+DROP TABLE ps_procstat;
+DROP TABLE ps_snaps;
diff --git a/contrib/ps-io-utilization.sh b/contrib/ps-io-utilization.sh
new file mode 100755 (executable)
index 0000000..d7f769f
--- /dev/null
@@ -0,0 +1,38 @@
+#!/bin/bash
+
+if [ $# -ne 3 ]; then
+       echo "Usage: $0 <pid> <snapid1> <snapid2>"
+       exit 1
+fi
+
+PID=$1
+SNAP1=$2
+SNAP2=$3
+
+A=( `psql --no-align --tuples-only --field-separator ' ' --command "SELECT syscr, syscw, reads, writes, cwrites FROM ps_snaps a, ps_procstat b WHERE pid = ${PID} AND a.snap = b.snap AND a.snap = ${SNAP1}"` )
+
+R1=${A[0]}
+W1=${A[1]}
+RB1=${A[2]}
+WB1=${A[3]}
+CWB1=${A[4]}
+
+A=( `psql --no-align --tuples-only --field-separator ' ' --command "SELECT syscr, syscw, reads, writes, cwrites FROM ps_snaps a, ps_procstat b WHERE pid = ${PID} AND a.snap = b.snap AND a.snap = ${SNAP2}"` )
+
+R2=${A[0]}
+W2=${A[1]}
+RB2=${A[2]}
+WB2=${A[3]}
+CWB2=${A[4]}
+
+R=$(( ${R2} - ${R1} ))
+W=$(( ${W2} - ${W1} ))
+RB=$(( ${RB2} - ${RB1} ))
+WB=$(( ${WB2} - ${WB1} ))
+CWB=$(( ${CWB2} - ${CWB1} ))
+
+echo "Reads = ${R}"
+echo "Writes = ${W}"
+echo "Reads (Bytes) = ${RB}"
+echo "Writes (Bytes) = ${WB}"
+echo "Cancelled (Bytes) = ${CWB}"
diff --git a/contrib/ps-processor-utilization.sh b/contrib/ps-processor-utilization.sh
new file mode 100755 (executable)
index 0000000..c6ad2e8
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+
+# FIXME: Hardcoding, best way to get this?
+HZ=1000
+
+if [ $# -ne 3 ]; then
+       echo "Usage: $0 <pid> <snapid1> <snapid2>"
+       exit 1
+fi
+
+PID=$1
+SNAP1=$2
+SNAP2=$3
+
+A=( `psql --no-align --tuples-only --field-separator ' ' --command "SELECT stime, utime, stime + utime AS total, extract(epoch FROM time) FROM ps_snaps a, ps_procstat b WHERE pid = ${PID} AND a.snap = b.snap AND a.snap = ${SNAP1}"` )
+
+STIME1=${A[0]}
+UTIME1=${A[1]}
+TOTAL1=${A[2]}
+TIME1=${A[3]}
+
+A=( `psql --no-align --tuples-only --field-separator ' ' --command "SELECT stime, utime, stime + utime AS total, extract(epoch FROM time) FROM ps_snaps a, ps_procstat b WHERE pid = ${PID} AND a.snap = b.snap AND a.snap = ${SNAP2}"` )
+
+STIME2=${A[0]}
+UTIME2=${A[1]}
+TOTAL2=${A[2]}
+TIME2=${A[3]}
+
+# Get the time difference in ticks.
+TIMEDIFF=`echo "scale = 2; (${TIME2} - ${TIME1}) * ${HZ}" | bc -l`
+
+U=`echo "scale = 2; (${TOTAL2} - ${TOTAL1}) / ${TIMEDIFF} * 100" | bc -l`
+
+echo "Processor Utilization = ${U} %"
diff --git a/contrib/ps-report.pl b/contrib/ps-report.pl
new file mode 100755 (executable)
index 0000000..bee656d
--- /dev/null
@@ -0,0 +1,236 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+
+unless ($ENV{PGDATABASE}) {
+       print "PGDATABASE must be set in your environment to determine which \n";
+       print "database to report statistics for.\n";
+       exit(1);
+}
+
+if (scalar @ARGV != 3) {
+       print "Usage: $0 <pid> <snap1> <snap2>\n";
+       exit(1);
+}
+
+my $pid = $ARGV[0];
+my $snap1 = $ARGV[1];
+my $snap2 = $ARGV[2];
+
+my $psql = "psql --tuples-only --no-align --command";
+my $sql;
+my $temp;
+my @a1;
+my @a2;
+
+# Get the snapshot timestamps.
+$temp = "
+SELECT time
+FROM ps_snaps
+WHERE snap = %d
+";
+
+$sql = sprintf($temp, $snap1);
+my $start_time = `$psql "$sql"`;
+chomp $start_time;
+
+$sql = sprintf($temp, $snap2);
+my $end_time = `$psql "$sql"`;
+chomp $end_time;
+
+# Get database stats.
+
+$temp = "
+SELECT numbackends, xact_commit, xact_rollback, blks_read, blks_hit
+FROM ps_dbstat
+WHERE datname = '$ENV{PGDATABASE}'
+  AND snap = %d
+";
+
+$sql = sprintf($temp, $snap1);
+@a1 = split /\|/, `$psql "$sql"`;
+
+$sql = sprintf($temp, $snap2);
+@a2 = split /\|/, `$psql "$sql"`;
+
+my $xact_commit = $a2[0] - $a1[0];
+my $xact_rollback = $a2[1] - $a1[1];
+my $blks_read = $a2[3] - $a1[3];
+my $blks_hit = $a2[4] - $a1[4];
+
+print "Database       : $ENV{PGDATABASE}\n";
+print "Snapshot Start : $start_time\n";
+print "Snapshot End   : $end_time\n";
+print "\n";
+print "-------------------\n";
+print "Database Statistics\n";
+print "-------------------\n";
+print "Commits     : $xact_commit\n";
+print "Rollbacks   : $xact_rollback\n";
+print "Blocks Read : $blks_read\n";
+print "Blocks Hit  : $blks_hit\n";
+print "\n";
+
+# FIXME: This simple logic breakds if tables are added or dropped between
+# snapshots.
+
+$temp = "
+SELECT schemaname, relname, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch,
+       n_tup_ins, n_tup_upd, n_tup_del
+FROM ps_tablestat
+WHERE snap = %d
+ORDER BY schemaname, relname
+";
+
+$sql = sprintf($temp, $snap1);
+@a1 = split /\n/, `$psql "$sql"`;
+
+# Get the vaccum dates of the second snapshot.
+$temp = "
+SELECT schemaname, relname, seq_scan, seq_tup_read, idx_scan, idx_tup_fetch,
+       n_tup_ins, n_tup_upd, n_tup_del, last_vacuum, last_autovacuum,
+       last_analyze, last_autoanalyze
+FROM ps_tablestat
+WHERE snap = %d
+ORDER BY schemaname, relname
+";
+$sql = sprintf($temp, $snap2);
+@a2 = split /\n/, `$psql "$sql"`;
+
+unless (scalar @a1 == scalar @a2) {
+       print "Tables were added or dropped between snapshot $snap1 and $snap2\n";
+       print "This script can't handle that, aborting.\n";
+       exit(1);
+}
+
+my $length = 0;
+
+print "================\n";
+print "Table Statistics\n";
+print "================\n";
+
+# Run through all the names once just to see what the longest one is to
+# determine how to format the output.
+my @c = ();
+for (my $i = 0; $i < scalar @a1; $i++) {
+       my @b1 = split /\|/, $a1[$i];
+       my @b2 = split /\|/, $a2[$i];
+
+       # Some columns are NULL, set to 0.
+       for (my $i = 0; $i < 9; $i++) {
+               $b1[$i] = 0 unless ($b1[$i]);
+               $b2[$i] = 0 unless ($b2[$i]);
+       }
+       for (my $i = 9; $i < 13; $i++) {
+               $b2[$i] = 'N/A' unless ($b2[$i]);
+       }
+       my $name = "$b1[0].$b1[1]";
+       $length = length $name if (length $name > $length);
+       push @c, ([$name, $b2[2] - $b1[2], $b2[3] - $b1[3], $b2[4] - $b1[4],
+                       $b2[5] - $b1[5], $b2[6] - $b1[6], $b2[7] - $b1[7],
+                       $b2[8] - $b1[8], "$b2[9]", "$b2[10]", "$b2[11]", "$b2[12]"]);
+}
+
+my @header = ("Schema.Relation", "Seq Scan", "Seq Tup Read",
+               "Idx Scan", "Idx Tup Fetch", "N Tup Ins", "N Tup Upd", "N Tup Del",
+               "Last Vacuum", "Last Autovacuum", "Last Analyze", "Last Autoanalyze");
+my @l = ();
+my @d = ();
+foreach (@header) {
+       push @l, length $_;
+       my $dashes = '';
+       for (my $i = 0; $i < length $_; $i++) {
+               $dashes .= '-';
+       }
+       push @d, $dashes;
+}
+# Re-adjust the lengths for timestamps.
+for (my $i = 8; $i < 12; $i++) {
+       $l[$i] = 29;
+}
+# Re-adjust the dashes for the Schema.Relation to the longest one.
+$d[0] = '';
+for (my $i = 0; $i < $length; $i++) {
+       $d[0] .= '-';
+}
+my $header_format = "%-" . $length . "s %" . $l[1] . "s %" . $l[2] . "s %" .
+               $l[3] . "s %" . $l[4] . "s %" . $l[5] . "s %" . $l[6] . "s %" .
+               $l[7] . "s %" . $l[8] . "s %" . $l[9] . "s %" . $l[10] . "s %" .
+               $l[11] . "s\n";
+my $format = "%-" . $length . "s %" . $l[1] . "d %" . $l[2] . "d %" .
+               $l[3] . "d %" . $l[4] . "d %" . $l[5] . "d %" . $l[6] . "d %" .
+               $l[7] . "d %" . $l[8] . "s %" . $l[9] . "s %" . $l[10] . "s %" .
+               $l[11] . "s\n";
+
+# Now start displaying data.
+printf $header_format, @d;
+printf $header_format, @header;
+printf $header_format, @d;
+for (my $i = 0; $i < scalar @c; $i++) {
+       printf $format, @{$c[$i]};
+}
+print"\n";
+
+# Display index stats.
+
+# FIXME: This simple logic breakds if indexes are added or dropped between
+# snapshots.
+
+$temp = "
+SELECT schemaname, relname, indexrelname, idx_scan, idx_tup_read, idx_tup_fetch
+FROM ps_indexstat
+WHERE snap = %d
+ORDER BY schemaname, relname, indexrelname
+";
+
+$sql = sprintf($temp, $snap1);
+@a1 = split /\n/, `$psql "$sql"`;
+
+$sql = sprintf($temp, $snap2);
+@a2 = split /\n/, `$psql "$sql"`;
+
+print "================\n";
+print "Index Statistics\n";
+print "================\n";
+
+# Run through all the names once just to see what the longest one is to
+# determine how to format the output.
+@c = ();
+for (my $i = 0; $i < scalar @a1; $i++) {
+       my @b1 = split /\|/, $a1[$i];
+       my @b2 = split /\|/, $a2[$i];
+
+       my $name = "$b1[0].$b1[1].$b1[2]";
+       $length = length $name if (length $name > $length);
+       push @c, ([$name, $b2[3] - $b1[3], $b2[4] - $b1[4], $b2[5] - $b1[5]]);
+}
+@header = ("Schema.Relation.Index", "Idx Scan", "Idx Tup Read",
+               "Idx Tup Fetch");
+@l = ();
+@d = ();
+foreach (@header) {
+       push @l, length $_;
+       my $dashes = '';
+       for (my $i = 0; $i < length $_; $i++) {
+               $dashes .= '-';
+       }
+       push @d, $dashes;
+}
+# Re-adjust the dashes for the Schema.Relation.Index to the longest one.
+$d[0] = '';
+for (my $i = 0; $i < $length; $i++) {
+       $d[0] .= '-';
+}
+$header_format = "%-" . $length . "s %" . $l[1] . "s %" .  $l[2] . "s %" .
+               $l[3] . "s\n";
+$format = "%-" . $length . "s %" . $l[1] . "d %" .  $l[2] . "d %" . $l[3] .
+               "d\n";
+
+# Now start displaying data.
+printf $header_format, @d;
+printf $header_format, @header;
+printf $header_format, @d;
+for (my $i = 0; $i < scalar @c; $i++) {
+       printf $format, @{$c[$i]};
+}
diff --git a/contrib/ps_procstat-snap.sql b/contrib/ps_procstat-snap.sql
new file mode 100644 (file)
index 0000000..2cd9a28
--- /dev/null
@@ -0,0 +1,3 @@
+BEGIN;
+SELECT ps_snap_stats();
+COMMIT;