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 """run(func, *args, **params). Dumps 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 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 = StringIO.StringIO()
106 s.print_stats()
107 finally:
108 sys.stdout = oldout
109 response = sio.getvalue()
110 sio.close()
111 return response
112
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
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 />" % (i, i)
131 menu.exposed = True
132
137 report.exposed = True
138
139
141
147
148 - def run(self, func, *args):
149 path = os.path.join(self.path, "cp_%04d.prof" % self.count)
150 result = self.profiler.runcall(func, *args)
151 self.profiler.dump_stats(path)
152 return result
153
154
156 - def __init__(self, nextapp, path=None, aggregate=False):
157 """Make a WSGI middleware app which wraps 'nextapp' with profiling."""
158 self.nextapp = nextapp
159 self.aggregate = aggregate
160 if aggregate:
161 self.profiler = ProfileAggregator(path)
162 else:
163 self.profiler = Profiler(path)
164
165 - def __call__(self, environ, start_response):
166 def gather():
167 result = []
168 for line in self.nextapp(environ, start_response):
169 result.append(line)
170 return result
171 return self.profiler.run(gather)
172
173
174 -def serve(path=None, port=8080):
181
182
183 if __name__ == "__main__":
184 serve(*tuple(sys.argv[1:]))
185