1 """Profiler tools for CherryPy.
6 You can profile any of your pages as follows::
8 from cherrypy.lib import profiler
11 p = profile.Profiler("/path/to/profile/dir")
14 self.p.run(self._index)
18 return "Hello, world!"
20 cherrypy.tree.mount(Root())
22 You can also turn on profiling for all requests
23 using the ``make_app`` function as WSGI middleware.
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.
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
47 pstats
.func_strip_path
= new_func_strip_path
56 from cherrypy
._cpcompat
import BytesIO
60 class Profiler(object):
62 def __init__(self
, path
=None):
64 path
= os
.path
.join(os
.path
.dirname(__file__
), "profile")
66 if not os
.path
.exists(path
):
69 def run(self
, func
, *args
, **params
):
70 """Dump profile data into self.path."""
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
)
80 """:rtype: list of available profiles.
82 return [f
for f
in os
.listdir(self
.path
)
83 if f
.startswith("cp_") and f
.endswith(".prof")]
85 def stats(self
, filename
, sortby
='cumulative'):
86 """:rtype stats(index): output of print_stats() for the given profile.
89 if sys
.version_info
>= (2, 5):
90 s
= pstats
.Stats(os
.path
.join(self
.path
, filename
), stream
=sio
)
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
))
106 response
= sio
.getvalue()
112 <head><title>CherryPy profile data</title></head>
113 <frameset cols='200, 1*'>
115 <frame name='main' src='' />
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()
127 yield "<a href='report?filename=%s' target='main'>%s</a><br />" % (i
, i
)
130 def report(self
, filename
):
132 cherrypy
.response
.headers
['Content-Type'] = 'text/plain'
133 return self
.stats(filename
)
134 report
.exposed
= True
137 class ProfileAggregator(Profiler
):
139 def __init__(self
, path
=None):
140 Profiler
.__init
__(self
, path
)
142 self
.count
= _count
= _count
+ 1
143 self
.profiler
= profile
.Profile()
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
)
153 def __init__(self
, nextapp
, path
=None, aggregate
=False):
154 """Make a WSGI middleware app which wraps 'nextapp' with profiling.
157 the WSGI application to wrap, usually an instance of
158 cherrypy.Application.
161 where to dump the profiling output.
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.
169 if profile
is None or pstats
is None:
170 msg
= ("Your installation of Python does not have a profile module. "
171 "If you're on Debian, try `sudo apt-get install python-profiler`. "
172 "See http://www.cherrypy.org/wiki/ProfilingOnDebian for details.")
175 self
.nextapp
= nextapp
176 self
.aggregate
= aggregate
178 self
.profiler
= ProfileAggregator(path
)
180 self
.profiler
= Profiler(path
)
182 def __call__(self
, environ
, start_response
):
185 for line
in self
.nextapp(environ
, start_response
):
188 return self
.profiler
.run(gather
)
191 def serve(path
=None, port
=8080):
192 if profile
is None or pstats
is None:
193 msg
= ("Your installation of Python does not have a profile module. "
194 "If you're on Debian, try `sudo apt-get install python-profiler`. "
195 "See http://www.cherrypy.org/wiki/ProfilingOnDebian for details.")
199 cherrypy
.config
.update({'server.socket_port': int(port
),
200 'server.thread_pool': 10,
201 'environment': "production",
203 cherrypy
.quickstart(Profiler(path
))
206 if __name__
== "__main__":
207 serve(*tuple(sys
.argv
[1:]))