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
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
54
55
56
57
58
59
60
61
62
63 import os, os.path
64 import sys
65
66 try:
67 import cStringIO as StringIO
68 except ImportError:
69 import StringIO
70
71
72 _count = 0
73
75
77 if not path:
78 path = os.path.join(os.path.dirname(__file__), "profile")
79 self.path = path
80 if not os.path.exists(path):
81 os.makedirs(path)
82
83 - def run(self, func, *args, **params):
84 """Dump profile data into self.path."""
85 global _count
86 c = _count = _count + 1
87 path = os.path.join(self.path, "cp_%04d.prof" % c)
88 prof = profile.Profile()
89 result = prof.runcall(func, *args, **params)
90 prof.dump_stats(path)
91 return result
92
94 """statfiles() -> list of available profiles."""
95 return [f for f in os.listdir(self.path)
96 if f.startswith("cp_") and f.endswith(".prof")]
97
98 - def stats(self, filename, sortby='cumulative'):
99 """stats(index) -> output of print_stats() for the given profile."""
100 sio = StringIO.StringIO()
101 if sys.version_info >= (2, 5):
102 s = pstats.Stats(os.path.join(self.path, filename), stream=sio)
103 s.strip_dirs()
104 s.sort_stats(sortby)
105 s.print_stats()
106 else:
107
108
109 s = pstats.Stats(os.path.join(self.path, filename))
110 s.strip_dirs()
111 s.sort_stats(sortby)
112 oldout = sys.stdout
113 try:
114 sys.stdout = sio
115 s.print_stats()
116 finally:
117 sys.stdout = oldout
118 response = sio.getvalue()
119 sio.close()
120 return response
121
123 return """<html>
124 <head><title>CherryPy profile data</title></head>
125 <frameset cols='200, 1*'>
126 <frame src='menu' />
127 <frame name='main' src='' />
128 </frameset>
129 </html>
130 """
131 index.exposed = True
132
134 yield "<h2>Profiling runs</h2>"
135 yield "<p>Click on one of the runs below to see profiling data.</p>"
136 runs = self.statfiles()
137 runs.sort()
138 for i in runs:
139 yield "<a href='report?filename=%s' target='main'>%s</a><br />" % (i, i)
140 menu.exposed = True
141
146 report.exposed = True
147
148
150
156
157 - def run(self, func, *args):
158 path = os.path.join(self.path, "cp_%04d.prof" % self.count)
159 result = self.profiler.runcall(func, *args)
160 self.profiler.dump_stats(path)
161 return result
162
163
165 - def __init__(self, nextapp, path=None, aggregate=False):
166 """Make a WSGI middleware app which wraps 'nextapp' with profiling.
167
168 nextapp: the WSGI application to wrap, usually an instance of
169 cherrypy.Application.
170 path: where to dump the profiling output.
171 aggregate: if True, profile data for all HTTP requests will go in
172 a single file. If False (the default), each HTTP request will
173 dump its profile data into a separate file.
174 """
175 self.nextapp = nextapp
176 self.aggregate = aggregate
177 if aggregate:
178 self.profiler = ProfileAggregator(path)
179 else:
180 self.profiler = Profiler(path)
181
182 - def __call__(self, environ, start_response):
183 def gather():
184 result = []
185 for line in self.nextapp(environ, start_response):
186 result.append(line)
187 return result
188 return self.profiler.run(gather)
189
190
191 -def serve(path=None, port=8080):
198
199
200 if __name__ == "__main__":
201 serve(*tuple(sys.argv[1:]))
202