1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import os, sys, string, time, thread, logging, win32api, win32pdh
21
22 from pysys import log
23 from pysys.constants import *
24
25
27 """Process monitor for the logging of process statistics.
28
29 The process monitor uses either the win32pdh module (windows systems) or the ps command line utility
30 (unix systems) to obtain and log to file statistics on a given process as determined by the process id.
31 Usage of the class is to create an instance specifying the process id, the logging interval and the log
32 file. Once created, the process monitor is started and stopped via its L{start} and L{stop} methods.
33 Process monitors are started as a separate thread, so control passes back to the caller of the start method
34 immediately.
35
36 On windows systems, statistics obtained include the CPU usage (%), the working set (memory pages allocated),
37 the virtual bytes (virtual address space including shared memory segments), the private bytes (virtual
38 address space not including shared memory segments), the number of process threads and the number of
39 handles. All memory values are quoted in KBytes and the CPU precentage represents the usage over all available
40 processors. A CPU usage of 100% represents a single CPU fully utilized; it is therefore possible to obtain CPU
41 usage figures of over 100% on multi-core processors. The format of the log file is tab separated, with
42 timestamps used to denote the time each measurement was obtained, e.g. ::
43
44 Time CPU Working Virtual Private Threads Handles
45 ------------------------------------------------------------------------
46 09/16/08 14:20:44 80 125164 212948 118740 44 327
47 09/16/08 14:20:49 86 125676 213972 120128 44 328
48 09/16/08 14:20:54 84 125520 212948 119116 44 328
49 09/16/08 14:20:59 78 125244 212948 119132 44 328
50
51
52 On unix systems, statistics obtained include the CPU usage (%), the resident memory (via the rss format specifier
53 to ps), and the virtual memory (via the vsz format spepcifier to ps). All memory values are quoted in KBytes and
54 the CPU precentage represents the usage over all available processors. A CPU usage of 100% represents a single
55 CPU fully utilized; it is therefore possible to obtain CPU usage figures of over 100% on multi-core processors.
56 The format of the log file is tab separated, with timestamps used to denote the time each measurement was obtained,
57 e.g. ::
58
59 Time CPU Resident Virtual
60 ----------------------------------------------------
61 09/16/08 14:24:10 69.5 89056 1421672
62 09/16/08 14:24:20 73.1 101688 1436804
63 09/16/08 14:24:30 82.9 102196 1436516
64 09/16/08 14:24:40 89.1 102428 1436372
65 09/16/08 14:24:50 94.2 104404 1438420
66
67
68 Both windows and unix operating systems support the numProcessors argument in the variable argument list in order
69 to normalise the CPU statistics gathered by the number of available CPUs.
70
71 """
72
73 - def __init__(self, pid, interval, file=None, **kwargs):
74 """Construct an instance of the process monitor.
75
76 @param pid: The process id to monitor
77 @param interval: The interval in seconds to record the process statistics
78 @param file: The full path to the file to log the process statistics
79 @param kwargs: Keyword arguments to allow platform specific configurations
80
81 """
82 self.pid = pid
83 self.interval = interval
84 if file:
85 self.file = open(file, 'w', 0)
86 else:
87 self.file = sys.stdout
88
89
90 self.numProcessors=1
91 if kwargs.has_key("numProcessors"):
92 self.numProcessors = int(kwargs["numProcessors"])
93
94
96 if bRefresh: win32pdh.EnumObjects(None, None, 0, 1)
97
98
99 items, instances = win32pdh.EnumObjectItems(None, None, "Process", -1)
100
101
102 instanceDict = {}
103 for i in instances:
104 try: instanceDict[i] = instanceDict[i] + 1
105 except KeyError: instanceDict[i] = 0
106
107
108 instance = None
109 inum = -1
110 for instance, numInstances in instanceDict.items():
111 for inum in xrange(numInstances+1):
112 try:
113 value = self.__win32getProfileAttribute("Process", instance, inum, "ID Process")
114 if value == pid:
115 return instance, inum
116 except:
117 pass
118
119 return instance, inum
120
121
123 if bRefresh: win32pdh.EnumObjects(None, None, 0, 1)
124
125
126 items, instances = win32pdh.EnumObjectItems(None, None, "Thread", -1)
127
128
129 instanceNum = []
130 instanceDict = {}
131 for i in instances:
132 try: instanceDict[i] = instanceDict[i] + 1
133 except KeyError: instanceDict[i] = 0
134 instanceNum.append(instanceDict[i])
135
136
137 threads=[]
138 for i in range(0, len(instances)):
139 try:
140 value = self.__win32getProfileAttribute("Thread", instances[i], instanceNum[i], "ID Process")
141 if value == pid: threads.append((instances[i], instanceNum[i]))
142 except:
143 pass
144 return threads
145
146
148
149 path = win32pdh.MakeCounterPath((None, object, instance, None, inum, counter))
150 query = win32pdh.OpenQuery()
151 hcounter = win32pdh.AddCounter(query, path)
152 win32pdh.CollectQueryData(query)
153
154
155 value = None
156 try:
157 value = win32pdh.GetFormattedCounterValue(hcounter, win32pdh.PDH_FMT_LONG)[1]
158 except:
159 pass
160
161
162 win32pdh.RemoveCounter(hcounter)
163 win32pdh.CloseQuery(query)
164 return value
165
166
168
169
170 process_counters=[]
171 process_query=win32pdh.OpenQuery()
172 for counter in "Working Set", "Virtual Bytes", "Private Bytes", "Thread Count", "Handle Count":
173 path = win32pdh.MakeCounterPath( (None, "Process", instance, None, inum, counter) )
174 process_counters.append(win32pdh.AddCounter(process_query, path))
175 win32pdh.CollectQueryData(process_query)
176
177
178 thread_counters=[]
179 thread_query=win32pdh.OpenQuery()
180 for (instance, inum) in threads:
181 path=win32pdh.MakeCounterPath( (None, "Thread", instance, None, inum, "% Processor Time") )
182 thread_counters.append(win32pdh.AddCounter(thread_query, path))
183 win32pdh.CollectQueryData(thread_query)
184
185
186 data = [0]*(len(process_counters)+1)
187 while self.active:
188 win32pdh.CollectQueryData(process_query)
189 win32pdh.CollectQueryData(thread_query)
190
191 for i in range(len(process_counters)):
192 try:
193 data[i+1] = win32pdh.GetFormattedCounterValue(process_counters[i], win32pdh.PDH_FMT_LONG)[1]
194 except win32api.error:
195 data[i+1] = -1
196
197 data[0]=0
198 for i in range(0, len(thread_counters)):
199 try:
200 data[0]=data[0]+win32pdh.GetFormattedCounterValue(thread_counters[i], win32pdh.PDH_FMT_LONG)[1]
201 except:
202 pass
203
204 currentTime = time.strftime("%d/%m/%y %H:%M:%S", time.gmtime(time.time()))
205 file.write( "%s\t%s\t%d\t%d\t%d\t%d\t%d\n" % (currentTime, data[0]/self.numProcessors, float(data[1])/1024,
206 float(data[2])/1024, float(data[3])/1024, float(data[4]), float(data[5])))
207 time.sleep(interval)
208
209
210 for c in process_counters:
211 win32pdh.RemoveCounter(c)
212 win32pdh.CloseQuery(process_query)
213 for c in thread_counters:
214 win32pdh.RemoveCounter(c)
215 win32pdh.CloseQuery(thread_query)
216 if file != sys.stdout: file.close()
217
219 """Return the running status of the process monitor.
220
221 @return: The running status (True | False)
222 @rtype: integer
223 """
224 return self.active
225
226
228 """Start the process monitor.
229
230 """
231 self.active = 1
232
233
234 instance, inum = self.__win32GetInstance(pid=self.pid, bRefresh=1)
235
236
237 threads = self.__win32GetThreads(pid=self.pid, bRefresh=1)
238
239
240 thread.start_new_thread(self.__win32LogProfile, (instance, inum, threads, self.interval, self.file))
241
242
244 """Stop the process monitor.
245
246 """
247 self.active = 0
248
249
250 if __name__ == "__main__":
251 items, instances = win32pdh.EnumObjectItems(None, None, "System", -1)
252 print "System - available counters are; "
253 for item in items: print item
254 print
255
256 items, instances = win32pdh.EnumObjectItems(None, None, "Processor", -1)
257 print "Processor - available counters are; "
258 for item in items: print item
259 print
260
261 items, instances = win32pdh.EnumObjectItems(None, None, "Process", -1)
262 print "Process - available counters are; "
263 for item in items: print item
264 print
265
266 items, instances = win32pdh.EnumObjectItems(None, None, "Thread", -1)
267 print "Thread - available counters are; "
268 for item in items: print item
269 print
270