blob: 0594688d678f0ba14357a415792c44f03d92796d [file] [log] [blame]
#!/usr/bin/env python
# coding: UTF-8
# pt-proc-stat
#
# Copyright(c) 2015 Uptime Technologies, LLC.
import os, sys
libpath = os.path.abspath(os.path.dirname(sys.argv[0]) + "/../lib")
sys.path.append(libpath)
import copy
import getopt
import log
import re
import subprocess
import time
class ProcInfo:
pid = None
ppid = None
proc_stat = None
proc_io = None
proc_net = None
debug = None
def __init__(self, pid, pagesize=4096, debug=False):
self.pid = pid
self.pagesize = int(pagesize)
self.debug = debug
self.proc_stat = []
self.proc_io = {}
self.proc_net = {}
if self.debug is True:
log.setLevel(log.DEBUG)
def read(self):
base = "/proc/" + str(self.pid)
# /proc/pid/cmdline
try:
f = open(base + "/cmdline")
except IOError, err:
log.debug(err)
return False
self.proc_cmdline = None
for l in f.readlines():
self.proc_cmdline = l.split('\0')
self.name = self.proc_cmdline[0]
f.close()
if self.proc_cmdline is None:
log.debug(base + "/cmdline is empty.")
return False
log.debug(base + "/cmdline ok.")
self.name = self.name.replace('postgres:', '')
self.name = self.name.replace('process', '')
self.name = re.sub('.*/', '', self.name)
self.name = re.sub('^ +', '', self.name)
self.name = re.sub(' +$', '', self.name)
self.name = re.sub(' +', ' ', self.name)
log.debug("name = %s" % self.name)
# /proc/pid/stat
try:
f = open(base + "/stat")
except IOError, err:
log.debug(err)
return False
log.debug(base + "/stat ok.")
for l in f.readlines():
self.proc_stat = l.split(' ')
log.debug(self.proc_stat)
self.ppid = int(self.proc_stat[3])
break
f.close()
log.debug("ppid = %d" % self.ppid)
# /proc/pid/io
try:
f = open(base + "/io")
except IOError, err:
log.debug(err)
return False
log.debug(base + "/io ok.")
for l in f.readlines():
key, value = l.rstrip('\r\n').split(': ')
log.debug("key = %s, value = %s" % (key, value))
self.proc_io[key] = int(value)
f.close()
return True
def print_stat(self, prev=None):
if prev is not None:
# disk
rbytes = ( self.proc_io["read_bytes"] - prev.proc_io["read_bytes"] ) / 1024
wbytes = ( self.proc_io["write_bytes"] - prev.proc_io["write_bytes"] ) / 1024
# net
rchar = ( self.proc_io["rchar"] - prev.proc_io["rchar"] ) / 1024 - rbytes
wchar = ( self.proc_io["wchar"] - prev.proc_io["wchar"] ) / 1024 - wbytes
# cpu
cpu_usr = int(self.proc_stat[13]) - int(prev.proc_stat[13])
cpu_sys = int(self.proc_stat[14]) - int(prev.proc_stat[14])
# mem
vsz = int(self.proc_stat[22]) / 1024 / 1024
rss = int(self.proc_stat[23]) * self.pagesize / 1024 / 1024
print("%20s[%5d] %4s %4d %4d %4d %4d %6d %6d %6d %6d" % (self.name[:20], int(self.pid), self.proc_stat[2], cpu_usr, cpu_sys, vsz, rss, int(rbytes), int(wbytes), int(rchar), int(wchar)))
else:
# disk
rbytes = ( self.proc_io["read_bytes"] ) / 1024
wbytes = ( self.proc_io["write_bytes"] ) / 1024
# net
rchar = ( self.proc_io["rchar"] ) / 1024 - rbytes
wchar = ( self.proc_io["wchar"] ) / 1024 - wbytes
# cpu
cpu_usr = int(self.proc_stat[13])
cpu_sys = int(self.proc_stat[14])
# mem
vsz = int(self.proc_stat[22]) / 1024 / 1024
rss = int(self.proc_stat[23]) * self.pagesize / 1024 / 1024
print("%20s[%5d] %4s %4d %4d %4d %4d %6d %6d %6d %6d" % (self.name[:20], int(self.pid), self.proc_stat[2], cpu_usr, cpu_sys, vsz, rss, int(rbytes), int(wbytes), int(rchar), int(wchar)))
class PostgresProcessStat():
pgdata = None
postmaster_pid = None
prev_stat = {}
stat = {}
update_count = 0
debug = False
def __init__(self, pgdata=None, pid=None, debug=False):
self.pgdata = pgdata
self.postmaster_pid = pid
self.debug = debug
return
def get_postmaster_pid(self):
if self.postmaster_pid is not None:
return self.postmaster_pid
# Get pid from postmaster.pid if available.
if self.pgdata is not None:
log.debug("reading " + self.pgdata + "/postmaster.pid to to determine the postmaster pid.")
try:
f = open(self.pgdata + "/postmaster.pid")
for l in f.readlines():
pid = int(l.replace('\n', ''))
break
f.close()
except IOError, err:
log.error(err.strerror + " (" + self.pgdata + "/postmaster.pid" + ")")
sys.exit(1)
if pid is not None:
log.debug("found postmaster pid " + str(pid))
return pid
# Find postmaster pid from /proc/<pid> files.
pid = None
for p in self._get_pids():
i = ProcInfo(p)
if i.read() is False:
continue
log.debug("name: %s" % i.name)
# Name of the process is "postmaster" or "postgres".
if re.search('postmaster', i.name) is not None or re.search('postgres', i.name) is not None:
pid = int(p)
log.debug("found postmaster pid " + str(pid))
if pid is None:
log.error("Cannot find postmaster pid.")
usage()
sys.exit(1)
self.postmaster_pid = pid
return pid
def get_postmaster_child_pids(self):
child_pids = []
for p in self._get_pids():
i = ProcInfo(p)
if i.read() is False:
continue
if i.ppid == self.get_postmaster_pid():
child_pids.append(p)
log.debug("child pids = " + str(child_pids))
return child_pids
def _get_pids(self):
pids = []
for p in os.listdir("/proc"):
if re.search('^\d+$', p) is not None:
pids.append(p)
log.debug("_get_pids = " + str(pids))
return pids
def update_stat(self):
pids = self.get_postmaster_child_pids()
self.prev_stat = {}
self.prev_stat = copy.deepcopy(self.stat)
self.stat = {}
# postmaster stat
i = ProcInfo(self.get_postmaster_pid())
if i.read() is False:
log.error("Cannot get postmaster stat.")
sys.exit(1)
self.stat[self.get_postmaster_pid()] = i
# child process stat
for p in pids:
i = ProcInfo(p)
if i.read() is False:
continue
log.debug("update_stat: %s %s" % (i.name, i.pid))
self.stat[p] = i
self.update_count = self.update_count + 1
log.debug("updae_stat: done count = %d" % self.update_count)
def print_stat(self):
os.system("date")
print("%20s[%5s] %4s %4s %4s %4s %4s %6s %6s %6s %6s" % ('PROCESS NAME', 'PID', 'STAT', 'USR', 'SYS', 'VSZ', 'RSS', 'READ', 'WRITE', 'READ2', 'WRITE2'))
if self.prev_stat is None or len(self.prev_stat) == 0:
log.debug("no prev records. printing the first records.")
# print process stat.
for i in sorted(self.stat):
self.stat[i].print_stat()
print("")
return True
log.debug("printing the diffs.")
# print process stat.
for i in sorted(self.stat):
if self.prev_stat.has_key(i) is True:
self.stat[i].print_stat(self.prev_stat[i])
else:
self.stat[i].print_stat()
print("")
return True
if __name__ == "__main2__":
# p = ProcInfo('21185', debug=True)
# p.read()
# p.print_stat()
# log.setLevel(log.DEBUG)
s = PostgresProcessStat(None)
# print(s._get_pids())
# print(s.get_postmaster_pid())
# print(s.get_postmaster_pid())
# print(s.get_postmaster_child_pids())
s.update_stat()
s.print_stat()
time.sleep(1)
s.update_stat()
s.print_stat()
def usage():
print ""
print "Usage: " + os.path.basename(sys.argv[0]) + " [option...] [delay [count]]"
print ""
print "Options:"
print " -D, --pgdata=DATADIR Location of the database storage area"
print " -P, --pid=PID Process ID of the postmaster"
print ""
print " --help Print this help."
print ""
if __name__ == "__main__":
try:
opts, args = getopt.getopt(sys.argv[1:], "D:P:",
["help", "debug", "pgdata=", "pid="])
except getopt.GetoptError, err:
log.error(str(err))
usage()
sys.exit(2)
postmaster_pid = None
pgdata = None
debug = None
for o, a in opts:
if o in ("-D", "--pgdata"):
pgdata = a
elif o in ("-P", "--pid"):
postmaster_pid = int(a)
elif o in ("--debug"):
debug = True
log.setLevel(log.DEBUG)
elif o in ("--help"):
usage()
sys.exit(0)
else:
log.error("unknown option: " + o + "," + a)
sys.exit(1)
delay = None
count = None
if len(args) == 2:
delay = int(args[0])
count = int(args[1])
elif len(args) == 1:
delay = int(args[0])
stat = PostgresProcessStat(pgdata=pgdata, pid=postmaster_pid, debug=debug)
log.debug("postmaster pid = " + str(postmaster_pid))
c = 0
done = False
while done is False:
stat.update_stat()
stat.print_stat()
c = c + 1
if count is not None and c >= count:
break
if delay is None:
break
try:
time.sleep(delay)
except KeyboardInterrupt, err:
log.info("Terminated.")
break
sys.exit(0)