3 # ***** BEGIN LICENSE BLOCK *****
4 # Version: MPL 1.1/GPL 2.0/LGPL 2.1
6 # The contents of this file are subject to the Mozilla Public License Version
7 # 1.1 (the "License"); you may not use this file except in compliance with
8 # the License. You may obtain a copy of the License at
9 # http://www.mozilla.org/MPL/
11 # Software distributed under the License is distributed on an "AS IS" basis,
12 # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
13 # for the specific language governing rights and limitations under the
16 # The Original Code is standalone Firefox Windows performance test.
18 # The Initial Developer of the Original Code is Google Inc.
19 # Portions created by the Initial Developer are Copyright (C) 2006
20 # the Initial Developer. All Rights Reserved.
23 # Annie Sullivan <annie.sullivan@gmail.com> (original author)
24 # Ben Hearsum <bhearsum@wittydomain.com> (ported to linux)
26 # Alternatively, the contents of this file may be used under the terms of
27 # either the GNU General Public License Version 2 or later (the "GPL"), or
28 # the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
29 # in which case the provisions of the GPL or the LGPL are applicable instead
30 # of those above. If you wish to allow use of your version of this file only
31 # under the terms of either the GPL or the LGPL, and not to allow others to
32 # use your version of this file under the terms of the MPL, indicate your
33 # decision by deleting the provisions above and replace them with the notice
34 # and other provisions required by the GPL or the LGPL. If you do not delete
35 # the provisions above, a recipient may use your version of this file under
36 # the terms of any one of the MPL, the GPL or the LGPL.
38 # ***** END LICENSE BLOCK *****
40 """A set of functions to run the Tp test.
42 The Tp test measures page load times in Firefox. It does this with a
43 JavaScript script that opens a new window and cycles through page loads
44 from the local disk, timing each one. The script dumps the sum of the
45 mean times to open each page, and the standard deviation, to standard out.
46 We can also measure performance attributes during the test. See below for
50 __author__
= 'annie.sullivan@gmail.com (Annie Sullivan)'
60 def GetPrivateBytes(pid
):
61 """Calculate the amount of private, writeable memory allocated to a process.
62 This code was adapted from 'pmap.c', part of the procps project.
64 mapfile
= '/proc/%s/maps' % pid
71 (range,line
) = line
.split(" ", 1)
73 (start
,end
) = range.split("-")
74 flags
= line
.split(" ", 1)[0]
76 size
= int(end
, 16) - int(start
, 16)
78 if flags
.find("p") >= 0:
79 if flags
.find("w") >= 0:
85 def GetResidentSize(pid
):
86 """Retrieve the current resident memory for a given process"""
87 # for some reason /proc/PID/stat doesn't give accurate information
88 # so we use status instead
89 file = '/proc/%s/status' % pid
94 if line
.find("VmRSS") >= 0:
95 return int(line
.split()[1]) * 1024
98 def GetCpuTime(pid
, sampleTime
=1):
99 # return all zeros on this platform as per the 7/18/07 perf meeting
103 """Returns the total bytes used by X"""
105 cmdline
= "xrestop -m 1 -b | grep -A 15 " + str(pid
) + " | tr -d \"\n\" | sed \"s/.*total bytes.*: ~//g\""
106 pipe
= subprocess
.Popen(cmdline
, shell
=True, stdout
=-1).stdout
111 print "Unexpected error:", sys
.exc_info()
115 counterDict
["Private Bytes"] = GetPrivateBytes
116 counterDict
["RSS"] = GetResidentSize
117 counterDict
["% Processor Time"] = GetCpuTime
118 counterDict
["XRes"] = GetXRes
120 class CounterManager(threading
.Thread
):
121 """This class manages the monitoring of a process with any number of
124 A counter can be any function that takes an argument of one pid and
125 returns a piece of data about that process.
126 Some examples are: CalcCPUTime, GetResidentSize, and GetPrivateBytes
131 def __init__(self
, process
, counters
=None):
133 counters: A list of counters to monitor. Any counters whose name does
134 not match a key in 'counterDict' will be ignored.
136 self
.allCounters
= {}
137 self
.registeredCounters
= {}
138 self
.process
= process
139 self
.runThread
= False
143 self
.registerCounters(counters
)
145 threading
.Thread
.__init
__(self
)
147 def _loadCounters(self
):
148 """Loads all of the counters defined in the counterDict"""
149 for counter
in counterDict
.keys():
150 self
.allCounters
[counter
] = counterDict
[counter
]
152 def registerCounters(self
, counters
):
153 """Registers a list of counters that will be monitoring.
154 Only counters whose names are found in allCounters will be added
156 for counter
in counters
:
157 if counter
in self
.allCounters
:
158 self
.registeredCounters
[counter
] = \
159 [self
.allCounters
[counter
], []]
161 def unregisterCounters(self
, counters
):
162 """Unregister a list of counters.
163 Only counters whose names are found in registeredCounters will be
166 for counter
in counters
:
167 if counter
in self
.registeredCounters
:
168 del self
.registeredCounters
[counter
]
170 def getRegisteredCounters(self
):
171 """Returns a list of the registered counters."""
172 return keys(self
.registeredCounters
)
174 def getCounterValue(self
, counterName
):
175 """Returns the last value of the counter 'counterName'"""
177 if counterName
is "% Processor Time":
178 return self
._getCounterAverage
(counterName
)
180 return self
.registeredCounters
[counterName
][1][-1]
184 def _getCounterAverage(self
, counterName
):
185 """Returns the average value of the counter 'counterName'"""
188 for v
in self
.registeredCounters
[counterName
][1]:
190 return total
/ len(self
.registeredCounters
[counterName
][1])
194 def getProcess(self
):
195 """Returns the process currently associated with this CounterManager"""
198 def startMonitor(self
):
199 """Starts the monitoring process.
200 Throws an exception if any error occurs
202 # TODO: make this function less ugly
204 # the last process is the useful one
205 self
.pid
= ffprocess
.GetPidsByName(self
.process
)[-1]
206 os
.stat('/proc/%s' % self
.pid
)
207 self
.runThread
= True
210 print 'WARNING: problem starting counter monitor'
212 def stopMonitor(self
):
213 """Stops the monitor"""
214 # TODO: should probably wait until we know run() is completely stopped
215 # before setting self.pid to None. Use a lock?
216 self
.runThread
= False
219 """Performs the actual monitoring of the process. Will keep running
220 until stopMonitor() is called
222 while self
.runThread
:
223 for counter
in self
.registeredCounters
.keys():
224 # counter[0] is a function that gets the current value for
226 # counter[1] is a list of recorded values
228 self
.registeredCounters
[counter
][1].append(
229 self
.registeredCounters
[counter
][0](self
.pid
))
231 # if a counter throws an exception, remove it
232 self
.unregisterCounters([counter
])
234 time
.sleep(self
.pollInterval
)