Package cherrypy :: Package lib :: Module profiler
[hide private]
[frames] | no frames]

Source Code for Module cherrypy.lib.profiler

  1  """Profiler tools for CherryPy. 
  2   
  3  CherryPy users 
  4  ============== 
  5   
  6  You can profile any of your pages as follows: 
  7   
  8      from cherrypy.lib import profiler 
  9       
 10      class Root: 
 11          p = profile.Profiler("/path/to/profile/dir") 
 12           
 13          def index(self): 
 14              self.p.run(self._index) 
 15          index.exposed = True 
 16           
 17          def _index(self): 
 18              return "Hello, world!" 
 19       
 20      cherrypy.tree.mount(Root()) 
 21   
 22   
 23  You can also turn on profiling for all requests 
 24  using the make_app function as WSGI middleware. 
 25   
 26   
 27  CherryPy developers 
 28  =================== 
 29   
 30  This module can be used whenever you make changes to CherryPy, 
 31  to get a quick sanity-check on overall CP performance. Use the 
 32  "--profile" flag when running the test suite. Then, use the serve() 
 33  function to browse the results in a web browser. If you run this 
 34  module from the command line, it will call serve() for you. 
 35   
 36  """ 
 37   
 38   
 39  # Make profiler output more readable by adding __init__ modules' parents. 
40 -def new_func_strip_path(func_name):
41 filename, line, name = func_name 42 if filename.endswith("__init__.py"): 43 return os.path.basename(filename[:-12]) + filename[-12:], line, name 44 return os.path.basename(filename), line, name
45 46 try: 47 import profile 48 import pstats 49 pstats.func_strip_path = new_func_strip_path 50 except ImportError: 51 profile = None 52 pstats = None 53 # do not advertise the non-free profiler everytime; its existence is 54 # already documented at the README.Debian file 55 # 56 #import warnings 57 #msg = ("Your installation of Python does not have a profile module. " 58 # "If you're on Debian, you can apt-get python2.4-profiler from " 59 # "non-free in a separate step. See http://www.cherrypy.org/wiki/" 60 # "ProfilingOnDebian for details.") 61 #warnings.warn(msg) 62 63 import os, os.path 64 import sys 65 66 try: 67 import cStringIO as StringIO 68 except ImportError: 69 import StringIO 70 71 72 _count = 0 73
74 -class Profiler(object):
75
76 - def __init__(self, path=None):
77 if not path: 78 path = os.path.join(os.path.dirname(__file__), "profile") 79 self.path = path 80 if not os.path.exists(path): 81 os.makedirs(path)
82
83 - def run(self, func, *args, **params):
84 """Dump profile data into self.path.""" 85 global _count 86 c = _count = _count + 1 87 path = os.path.join(self.path, "cp_%04d.prof" % c) 88 prof = profile.Profile() 89 result = prof.runcall(func, *args, **params) 90 prof.dump_stats(path) 91 return result
92
93 - def statfiles(self):
94 """statfiles() -> list of available profiles.""" 95 return [f for f in os.listdir(self.path) 96 if f.startswith("cp_") and f.endswith(".prof")]
97
98 - def stats(self, filename, sortby='cumulative'):
99 """stats(index) -> output of print_stats() for the given profile.""" 100 sio = StringIO.StringIO() 101 if sys.version_info >= (2, 5): 102 s = pstats.Stats(os.path.join(self.path, filename), stream=sio) 103 s.strip_dirs() 104 s.sort_stats(sortby) 105 s.print_stats() 106 else: 107 # pstats.Stats before Python 2.5 didn't take a 'stream' arg, 108 # but just printed to stdout. So re-route stdout. 109 s = pstats.Stats(os.path.join(self.path, filename)) 110 s.strip_dirs() 111 s.sort_stats(sortby) 112 oldout = sys.stdout 113 try: 114 sys.stdout = sio 115 s.print_stats() 116 finally: 117 sys.stdout = oldout 118 response = sio.getvalue() 119 sio.close() 120 return response
121
122 - def index(self):
123 return """<html> 124 <head><title>CherryPy profile data</title></head> 125 <frameset cols='200, 1*'> 126 <frame src='menu' /> 127 <frame name='main' src='' /> 128 </frameset> 129 </html> 130 """
131 index.exposed = True 132
133 - def menu(self):
134 yield "<h2>Profiling runs</h2>" 135 yield "<p>Click on one of the runs below to see profiling data.</p>" 136 runs = self.statfiles() 137 runs.sort() 138 for i in runs: 139 yield "<a href='report?filename=%s' target='main'>%s</a><br />" % (i, i)
140 menu.exposed = True 141
142 - def report(self, filename):
143 import cherrypy 144 cherrypy.response.headers['Content-Type'] = 'text/plain' 145 return self.stats(filename)
146 report.exposed = True
147 148
149 -class ProfileAggregator(Profiler):
150
151 - def __init__(self, path=None):
152 Profiler.__init__(self, path) 153 global _count 154 self.count = _count = _count + 1 155 self.profiler = profile.Profile()
156
157 - def run(self, func, *args):
158 path = os.path.join(self.path, "cp_%04d.prof" % self.count) 159 result = self.profiler.runcall(func, *args) 160 self.profiler.dump_stats(path) 161 return result
162 163
164 -class make_app:
165 - def __init__(self, nextapp, path=None, aggregate=False):
166 """Make a WSGI middleware app which wraps 'nextapp' with profiling. 167 168 nextapp: the WSGI application to wrap, usually an instance of 169 cherrypy.Application. 170 path: where to dump the profiling output. 171 aggregate: if True, profile data for all HTTP requests will go in 172 a single file. If False (the default), each HTTP request will 173 dump its profile data into a separate file. 174 """ 175 self.nextapp = nextapp 176 self.aggregate = aggregate 177 if aggregate: 178 self.profiler = ProfileAggregator(path) 179 else: 180 self.profiler = Profiler(path)
181
182 - def __call__(self, environ, start_response):
183 def gather(): 184 result = [] 185 for line in self.nextapp(environ, start_response): 186 result.append(line) 187 return result
188 return self.profiler.run(gather)
189 190
191 -def serve(path=None, port=8080):
192 import cherrypy 193 cherrypy.config.update({'server.socket_port': int(port), 194 'server.thread_pool': 10, 195 'environment': "production", 196 }) 197 cherrypy.quickstart(Profiler(path))
198 199 200 if __name__ == "__main__": 201 serve(*tuple(sys.argv[1:])) 202