1
0
mirror of https://github.com/Tygs/0bin.git synced 2023-08-10 21:13:00 +03:00
0bin/libs/cherrypy/lib/profiler.py

217 lines
6.4 KiB
Python
Raw Normal View History

2012-04-26 23:19:12 +04:00
"""Profiler tools for CherryPy.
CherryPy users
==============
You can profile any of your pages as follows::
from cherrypy.lib import profiler
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
class Root:
p = profile.Profiler("/path/to/profile/dir")
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def index(self):
self.p.run(self._index)
index.exposed = True
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def _index(self):
return "Hello, world!"
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
cherrypy.tree.mount(Root())
You can also turn on profiling for all requests
using the ``make_app`` function as WSGI middleware.
CherryPy developers
===================
This module can be used whenever you make changes to CherryPy,
to get a quick sanity-check on overall CP performance. Use the
``--profile`` flag when running the test suite. Then, use the ``serve()``
function to browse the results in a web browser. If you run this
module from the command line, it will call ``serve()`` for you.
"""
def new_func_strip_path(func_name):
2015-05-10 20:19:02 +03:00
"""Make profiler output more readable by adding `__init__` modules' parents
"""
2012-04-26 23:19:12 +04:00
filename, line, name = func_name
if filename.endswith("__init__.py"):
return os.path.basename(filename[:-12]) + filename[-12:], line, name
return os.path.basename(filename), line, name
try:
import profile
import pstats
pstats.func_strip_path = new_func_strip_path
except ImportError:
profile = None
pstats = None
2015-05-10 20:19:02 +03:00
import os
import os.path
2012-04-26 23:19:12 +04:00
import sys
import warnings
2015-05-10 20:19:02 +03:00
from cherrypy._cpcompat import StringIO
2012-04-26 23:19:12 +04:00
_count = 0
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
class Profiler(object):
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def __init__(self, path=None):
if not path:
path = os.path.join(os.path.dirname(__file__), "profile")
self.path = path
if not os.path.exists(path):
os.makedirs(path)
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def run(self, func, *args, **params):
"""Dump profile data into self.path."""
global _count
c = _count = _count + 1
path = os.path.join(self.path, "cp_%04d.prof" % c)
prof = profile.Profile()
result = prof.runcall(func, *args, **params)
prof.dump_stats(path)
return result
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def statfiles(self):
""":rtype: list of available profiles.
"""
return [f for f in os.listdir(self.path)
if f.startswith("cp_") and f.endswith(".prof")]
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def stats(self, filename, sortby='cumulative'):
""":rtype stats(index): output of print_stats() for the given profile.
"""
2015-05-10 20:19:02 +03:00
sio = StringIO()
2012-04-26 23:19:12 +04:00
if sys.version_info >= (2, 5):
s = pstats.Stats(os.path.join(self.path, filename), stream=sio)
s.strip_dirs()
s.sort_stats(sortby)
s.print_stats()
else:
# pstats.Stats before Python 2.5 didn't take a 'stream' arg,
# but just printed to stdout. So re-route stdout.
s = pstats.Stats(os.path.join(self.path, filename))
s.strip_dirs()
s.sort_stats(sortby)
oldout = sys.stdout
try:
sys.stdout = sio
s.print_stats()
finally:
sys.stdout = oldout
response = sio.getvalue()
sio.close()
return response
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def index(self):
return """<html>
<head><title>CherryPy profile data</title></head>
<frameset cols='200, 1*'>
<frame src='menu' />
<frame name='main' src='' />
</frameset>
</html>
"""
index.exposed = True
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def menu(self):
yield "<h2>Profiling runs</h2>"
yield "<p>Click on one of the runs below to see profiling data.</p>"
runs = self.statfiles()
runs.sort()
for i in runs:
2015-05-10 20:19:02 +03:00
yield "<a href='report?filename=%s' target='main'>%s</a><br />" % (
i, i)
2012-04-26 23:19:12 +04:00
menu.exposed = True
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def report(self, filename):
import cherrypy
cherrypy.response.headers['Content-Type'] = 'text/plain'
return self.stats(filename)
report.exposed = True
class ProfileAggregator(Profiler):
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def __init__(self, path=None):
Profiler.__init__(self, path)
global _count
self.count = _count = _count + 1
self.profiler = profile.Profile()
2015-05-10 20:19:02 +03:00
def run(self, func, *args, **params):
2012-04-26 23:19:12 +04:00
path = os.path.join(self.path, "cp_%04d.prof" % self.count)
2015-05-10 20:19:02 +03:00
result = self.profiler.runcall(func, *args, **params)
2012-04-26 23:19:12 +04:00
self.profiler.dump_stats(path)
return result
class make_app:
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def __init__(self, nextapp, path=None, aggregate=False):
"""Make a WSGI middleware app which wraps 'nextapp' with profiling.
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
nextapp
the WSGI application to wrap, usually an instance of
cherrypy.Application.
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
path
where to dump the profiling output.
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
aggregate
if True, profile data for all HTTP requests will go in
a single file. If False (the default), each HTTP request will
dump its profile data into a separate file.
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
"""
if profile is None or pstats is None:
2015-05-10 20:19:02 +03:00
msg = ("Your installation of Python does not have a profile "
"module. If you're on Debian, try "
"`sudo apt-get install python-profiler`. "
"See http://www.cherrypy.org/wiki/ProfilingOnDebian "
"for details.")
2012-04-26 23:19:12 +04:00
warnings.warn(msg)
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
self.nextapp = nextapp
self.aggregate = aggregate
if aggregate:
self.profiler = ProfileAggregator(path)
else:
self.profiler = Profiler(path)
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
def __call__(self, environ, start_response):
def gather():
result = []
for line in self.nextapp(environ, start_response):
result.append(line)
return result
return self.profiler.run(gather)
def serve(path=None, port=8080):
if profile is None or pstats is None:
msg = ("Your installation of Python does not have a profile module. "
2015-05-10 20:19:02 +03:00
"If you're on Debian, try "
"`sudo apt-get install python-profiler`. "
"See http://www.cherrypy.org/wiki/ProfilingOnDebian "
"for details.")
2012-04-26 23:19:12 +04:00
warnings.warn(msg)
2015-05-10 20:19:02 +03:00
2012-04-26 23:19:12 +04:00
import cherrypy
cherrypy.config.update({'server.socket_port': int(port),
'server.thread_pool': 10,
'environment': "production",
})
cherrypy.quickstart(Profiler(path))
if __name__ == "__main__":
serve(*tuple(sys.argv[1:]))