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