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 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
72
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 """run(func, *args, **params). Dumps 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
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 s = pstats.Stats(os.path.join(self.path, filename))
98 s.strip_dirs()
99 s.sort_stats(sortby)
100 oldout = sys.stdout
101 try:
102 sys.stdout = sio = StringIO.StringIO()
103 s.print_stats()
104 finally:
105 sys.stdout = oldout
106 response = sio.getvalue()
107 sio.close()
108 return response
109
111 return """<html>
112 <head><title>CherryPy profile data</title></head>
113 <frameset cols='200, 1*'>
114 <frame src='menu' />
115 <frame name='main' src='' />
116 </frameset>
117 </html>
118 """
119 index.exposed = True
120
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()
125 runs.sort()
126 for i in runs:
127 yield "<a href='report?filename=%s' target='main'>%s</a><br />" % (i, i)
128 menu.exposed = True
129
134 report.exposed = True
135
136
138
144
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)
149 return result
150
151
153 - def __init__(self, nextapp, path=None, aggregate=False):
154 """Make a WSGI middleware app which wraps 'nextapp' with profiling."""
155 self.nextapp = nextapp
156 self.aggregate = aggregate
157 if aggregate:
158 self.profiler = ProfileAggregator(path)
159 else:
160 self.profiler = Profiler(path)
161
162 - def __call__(self, environ, start_response):
163 def gather():
164 result = []
165 for line in self.nextapp(environ, start_response):
166 result.append(line)
167 return result
168 return self.profiler.run(gather)
169
170
171 -def serve(path=None, port=8080):
178
179
180 if __name__ == "__main__":
181 serve(*tuple(sys.argv[1:]))
182