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