Package pysys :: Package process :: Module monitor
[frames] | no frames]

Source Code for Module pysys.process.monitor

  1  #!/usr/bin/env python 
  2  # PySys System Test Framework, Copyright (C) 2006-2013  M.B.Grieve 
  3   
  4  # This library is free software; you can redistribute it and/or 
  5  # modify it under the terms of the GNU Lesser General Public 
  6  # License as published by the Free Software Foundation; either 
  7  # version 2.1 of the License, or (at your option) any later version. 
  8   
  9  # This library is distributed in the hope that it will be useful, 
 10  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
 11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
 12  # Lesser General Public License for more details. 
 13   
 14  # You should have received a copy of the GNU Lesser General Public 
 15  # License along with this library; if not, write to the Free Software 
 16  # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 
 17   
 18  # Contact: moraygrieve@users.sourceforge.net 
 19   
 20  import os, sys, string, time, thread 
 21   
 22  from pysys.constants import * 
 23   
 24   
25 -class ProcessMonitor:
26 """Process monitor for the logging of process statistics. 27 28 The process monitor uses either the win32pdh module (windows systems) or the ps command line utility 29 (unix systems) to obtain and log to file statistics on a given process as determined by the process id. 30 Usage of the class is to create an instance specifying the process id, the logging interval and the log 31 file. Once created, the process monitor is started and stopped via its L{start} and L{stop} methods. 32 Process monitors are started as a separate thread, so control passes back to the caller of the start method 33 immediately. 34 35 On windows systems, statistics obtained include the CPU usage (%), the working set (memory pages allocated), 36 the virtual bytes (virtual address space including shared memory segments), the private bytes (virtual 37 address space not including shared memory segments), the number of process threads and the number of 38 handles. All memory values are quoted in KBytes and the CPU precentage represents the usage over all available 39 processors. A CPU usage of 100% represents a single CPU fully utilized; it is therefore possible to obtain CPU 40 usage figures of over 100% on multi-core processors. The format of the log file is tab separated, with 41 timestamps used to denote the time each measurement was obtained, e.g. :: 42 43 Time CPU Working Virtual Private Threads Handles 44 ------------------------------------------------------------------------ 45 09/16/08 14:20:44 80 125164 212948 118740 44 327 46 09/16/08 14:20:49 86 125676 213972 120128 44 328 47 09/16/08 14:20:54 84 125520 212948 119116 44 328 48 09/16/08 14:20:59 78 125244 212948 119132 44 328 49 50 51 On unix systems, statistics obtained include the CPU usage (%), the resident memory (via the rss format specifier 52 to ps), and the virtual memory (via the vsz format spepcifier to ps). All memory values are quoted in KBytes and 53 the CPU precentage represents the usage over all available processors. A CPU usage of 100% represents a single 54 CPU fully utilized; it is therefore possible to obtain CPU usage figures of over 100% on multi-core processors. 55 The format of the log file is tab separated, with timestamps used to denote the time each measurement was obtained, 56 e.g. :: 57 58 Time CPU Resident Virtual 59 ---------------------------------------------------- 60 09/16/08 14:24:10 69.5 89056 1421672 61 09/16/08 14:24:20 73.1 101688 1436804 62 09/16/08 14:24:30 82.9 102196 1436516 63 09/16/08 14:24:40 89.1 102428 1436372 64 09/16/08 14:24:50 94.2 104404 1438420 65 66 67 Both windows and unix operating systems support the numProcessors argument in the variable argument list in order 68 to normalise the CPU statistics gathered by the number of available CPUs. 69 70 """ 71
72 - def __init__(self, pid, interval, file=None, **kwargs):
73 """Construct an instance of the process monitor. 74 75 @param pid: The process id to monitor 76 @param interval: The interval in seconds to record the process statistics 77 @param file: The full path to the file to log the process statistics 78 @param kwargs: Keyword arguments to allow platform specific configurations 79 80 """ 81 self.pid = pid 82 self.interval = interval 83 if file: 84 self.file = open(file, 'w', 0) 85 else: 86 self.file = sys.stdout 87 88 # normalise the CPU readings by the supplied factor 89 self.numProcessors=1 90 if kwargs.has_key("numProcessors"): 91 self.numProcessors = int(kwargs["numProcessors"])
92 93
94 - def __findChildren(self, psList, parentPid):
95 children = [] 96 children.append(int(parentPid)) 97 98 for i in range(1, len(psList)): 99 pid = int(string.split(psList[i])[0]) 100 ppid = int(string.split(psList[i])[1]) 101 if ppid == parentPid: 102 children[len(children):] = self.__findChildren(psList, pid) 103 104 return children
105 106
107 - def __linuxLogProfile(self, pid, interval, file, includeChildren=True):
108 # sleep - fixes weird problem of thread hanging? 109 time.sleep(1) 110 111 # get the child process tree for this process 112 if (includeChildren): 113 fp = os.popen("ps -o pid,ppid") 114 psList = fp.readlines() 115 fp.close() 116 pidTree = self.__findChildren(psList, pid) 117 else: 118 pidTree = [pid] 119 120 # perform the repeated collection of data for the profile. 121 while self.active: 122 data = [0, 0, 0] 123 fp = os.popen("ps -o pid,pcpu,rss,vsz") 124 info = fp.readlines() 125 fp.close() 126 127 for i in range(1, len(info)): 128 if int(string.split(info[i])[0]) in pidTree: 129 data[0] = data[0] + float(string.split(info[i])[1]) 130 data[1] = int(string.split(info[i])[2]) 131 data[2] = int(string.split(info[i])[3]) 132 133 currentTime = time.strftime("%m/%d/%y %H:%M:%S", time.gmtime(time.time())) 134 file.write( "%s\t%f\t%d\t%d\n" % (currentTime, data[0]/self.numProcessors, data[1], data[2]) ) 135 time.sleep(interval) 136 137 # clean up 138 if file != sys.stdout: file.close()
139 140
141 - def __solarisLogProfile(self, pid, interval, file):
142 # perform the repeated collection of data for the profile. 143 data = [-1, -1, -1] 144 while self.active: 145 try: 146 fp = os.popen("ps -p %s -o pcpu,rss,vsz" % (pid)) 147 info = fp.readlines()[1] 148 for i in range(len(data)): 149 data[i] = string.split(info)[i] 150 fp.close() 151 except: 152 fp.close() 153 currentTime = time.strftime("%m/%d/%y %H:%M:%S", time.gmtime(time.time())) 154 file.write( "%s\t%s\t%s\t%s\n" % (currentTime, data[0]/self.numProcessors, data[1], data[2]) ) 155 time.sleep(interval)
156 157 158 # public methods to start and stop a process monitor thread
159 - def running(self):
160 """Return the running status of the process monitor. 161 162 @return: The running status (True | False) 163 @rtype: integer 164 """ 165 return self.active
166 167
168 - def start(self):
169 """Start the process monitor. 170 171 """ 172 self.active = 1 173 if PLATFORM == 'sunos': 174 thread.start_new_thread(self.__solarisLogProfile, (self.pid, self.interval, self.file)) 175 elif PLATFORM in ['linux','darwin']: 176 thread.start_new_thread(self.__linuxLogProfile, (self.pid, self.interval, self.file))
177 178
179 - def stop(self):
180 """Stop the process monitor. 181 182 """ 183 self.active = 0
184 185 186 187 188 # used to run class from the command line 189 if __name__ == "__main__": 190 if len(sys.argv) < 5: 191 print "Usage: monprocess.py <pid> <interval> <duration> <filename>" 192 else: 193 try: 194 pid = int(sys.argv[1]) 195 interval = int(sys.argv[2]) 196 duration = int(sys.argv[3]) 197 file = sys.argv[4] 198 except: 199 print "Process ID, interval and duration should be valid integers" 200 sys.exit(-1) 201 202 monitor = ProcessMonitor(pid, interval, file) 203 monitor.start() 204 time.sleep(duration) 205 monitor.stop() 206