Download Install Tutorial Docs FAQ Tools WikiLicense Team IRC Planet Involvement Shop Book

root/trunk/cherrypy/lib/profiler.py

Revision 2012 (checked in by fumanchu, 2 months ago)

Doc tweaks.

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

Hosted by WebFaction

Log in as guest/cpguest to create tickets