| #!/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) |