##// END OF EJS Templates
profiling: add a missing argument to the ProgrammingError constructor...
Matt Harbison -
r44131:882e633a default
parent child Browse files
Show More
@@ -1,282 +1,282 b''
1 1 # profiling.py - profiling functions
2 2 #
3 3 # Copyright 2016 Gregory Szorc <gregory.szorc@gmail.com>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7
8 8 from __future__ import absolute_import, print_function
9 9
10 10 import contextlib
11 11
12 12 from .i18n import _
13 13 from .pycompat import (
14 14 getattr,
15 15 open,
16 16 )
17 17 from . import (
18 18 encoding,
19 19 error,
20 20 extensions,
21 21 pycompat,
22 22 util,
23 23 )
24 24
25 25
26 26 def _loadprofiler(ui, profiler):
27 27 """load profiler extension. return profile method, or None on failure"""
28 28 extname = profiler
29 29 extensions.loadall(ui, whitelist=[extname])
30 30 try:
31 31 mod = extensions.find(extname)
32 32 except KeyError:
33 33 return None
34 34 else:
35 35 return getattr(mod, 'profile', None)
36 36
37 37
38 38 @contextlib.contextmanager
39 39 def lsprofile(ui, fp):
40 40 format = ui.config(b'profiling', b'format')
41 41 field = ui.config(b'profiling', b'sort')
42 42 limit = ui.configint(b'profiling', b'limit')
43 43 climit = ui.configint(b'profiling', b'nested')
44 44
45 45 if format not in [b'text', b'kcachegrind']:
46 46 ui.warn(_(b"unrecognized profiling format '%s' - Ignored\n") % format)
47 47 format = b'text'
48 48
49 49 try:
50 50 from . import lsprof
51 51 except ImportError:
52 52 raise error.Abort(
53 53 _(
54 54 b'lsprof not available - install from '
55 55 b'http://codespeak.net/svn/user/arigo/hack/misc/lsprof/'
56 56 )
57 57 )
58 58 p = lsprof.Profiler()
59 59 p.enable(subcalls=True)
60 60 try:
61 61 yield
62 62 finally:
63 63 p.disable()
64 64
65 65 if format == b'kcachegrind':
66 66 from . import lsprofcalltree
67 67
68 68 calltree = lsprofcalltree.KCacheGrind(p)
69 69 calltree.output(fp)
70 70 else:
71 71 # format == 'text'
72 72 stats = lsprof.Stats(p.getstats())
73 73 stats.sort(pycompat.sysstr(field))
74 74 stats.pprint(limit=limit, file=fp, climit=climit)
75 75
76 76
77 77 @contextlib.contextmanager
78 78 def flameprofile(ui, fp):
79 79 try:
80 80 from flamegraph import flamegraph # pytype: disable=import-error
81 81 except ImportError:
82 82 raise error.Abort(
83 83 _(
84 84 b'flamegraph not available - install from '
85 85 b'https://github.com/evanhempel/python-flamegraph'
86 86 )
87 87 )
88 88 # developer config: profiling.freq
89 89 freq = ui.configint(b'profiling', b'freq')
90 90 filter_ = None
91 91 collapse_recursion = True
92 92 thread = flamegraph.ProfileThread(
93 93 fp, 1.0 / freq, filter_, collapse_recursion
94 94 )
95 95 start_time = util.timer()
96 96 try:
97 97 thread.start()
98 98 yield
99 99 finally:
100 100 thread.stop()
101 101 thread.join()
102 102 print(
103 103 b'Collected %d stack frames (%d unique) in %2.2f seconds.'
104 104 % (
105 105 util.timer() - start_time,
106 106 thread.num_frames(),
107 107 thread.num_frames(unique=True),
108 108 )
109 109 )
110 110
111 111
112 112 @contextlib.contextmanager
113 113 def statprofile(ui, fp):
114 114 from . import statprof
115 115
116 116 freq = ui.configint(b'profiling', b'freq')
117 117 if freq > 0:
118 118 # Cannot reset when profiler is already active. So silently no-op.
119 119 if statprof.state.profile_level == 0:
120 120 statprof.reset(freq)
121 121 else:
122 122 ui.warn(_(b"invalid sampling frequency '%s' - ignoring\n") % freq)
123 123
124 124 track = ui.config(
125 125 b'profiling', b'time-track', pycompat.iswindows and b'cpu' or b'real'
126 126 )
127 127 statprof.start(mechanism=b'thread', track=track)
128 128
129 129 try:
130 130 yield
131 131 finally:
132 132 data = statprof.stop()
133 133
134 134 profformat = ui.config(b'profiling', b'statformat')
135 135
136 136 formats = {
137 137 b'byline': statprof.DisplayFormats.ByLine,
138 138 b'bymethod': statprof.DisplayFormats.ByMethod,
139 139 b'hotpath': statprof.DisplayFormats.Hotpath,
140 140 b'json': statprof.DisplayFormats.Json,
141 141 b'chrome': statprof.DisplayFormats.Chrome,
142 142 }
143 143
144 144 if profformat in formats:
145 145 displayformat = formats[profformat]
146 146 else:
147 147 ui.warn(_(b'unknown profiler output format: %s\n') % profformat)
148 148 displayformat = statprof.DisplayFormats.Hotpath
149 149
150 150 kwargs = {}
151 151
152 152 def fraction(s):
153 153 if isinstance(s, (float, int)):
154 154 return float(s)
155 155 if s.endswith(b'%'):
156 156 v = float(s[:-1]) / 100
157 157 else:
158 158 v = float(s)
159 159 if 0 <= v <= 1:
160 160 return v
161 161 raise ValueError(s)
162 162
163 163 if profformat == b'chrome':
164 164 showmin = ui.configwith(fraction, b'profiling', b'showmin', 0.005)
165 165 showmax = ui.configwith(fraction, b'profiling', b'showmax')
166 166 kwargs.update(minthreshold=showmin, maxthreshold=showmax)
167 167 elif profformat == b'hotpath':
168 168 # inconsistent config: profiling.showmin
169 169 limit = ui.configwith(fraction, b'profiling', b'showmin', 0.05)
170 170 kwargs['limit'] = limit
171 171 showtime = ui.configbool(b'profiling', b'showtime')
172 172 kwargs['showtime'] = showtime
173 173
174 174 statprof.display(fp, data=data, format=displayformat, **kwargs)
175 175
176 176
177 177 class profile(object):
178 178 """Start profiling.
179 179
180 180 Profiling is active when the context manager is active. When the context
181 181 manager exits, profiling results will be written to the configured output.
182 182 """
183 183
184 184 def __init__(self, ui, enabled=True):
185 185 self._ui = ui
186 186 self._output = None
187 187 self._fp = None
188 188 self._fpdoclose = True
189 189 self._profiler = None
190 190 self._enabled = enabled
191 191 self._entered = False
192 192 self._started = False
193 193
194 194 def __enter__(self):
195 195 self._entered = True
196 196 if self._enabled:
197 197 self.start()
198 198 return self
199 199
200 200 def start(self):
201 201 """Start profiling.
202 202
203 203 The profiling will stop at the context exit.
204 204
205 205 If the profiler was already started, this has no effect."""
206 206 if not self._entered:
207 raise error.ProgrammingError()
207 raise error.ProgrammingError(b'use a context manager to start')
208 208 if self._started:
209 209 return
210 210 self._started = True
211 211 profiler = encoding.environ.get(b'HGPROF')
212 212 proffn = None
213 213 if profiler is None:
214 214 profiler = self._ui.config(b'profiling', b'type')
215 215 if profiler not in (b'ls', b'stat', b'flame'):
216 216 # try load profiler from extension with the same name
217 217 proffn = _loadprofiler(self._ui, profiler)
218 218 if proffn is None:
219 219 self._ui.warn(
220 220 _(b"unrecognized profiler '%s' - ignored\n") % profiler
221 221 )
222 222 profiler = b'stat'
223 223
224 224 self._output = self._ui.config(b'profiling', b'output')
225 225
226 226 try:
227 227 if self._output == b'blackbox':
228 228 self._fp = util.stringio()
229 229 elif self._output:
230 230 path = self._ui.expandpath(self._output)
231 231 self._fp = open(path, b'wb')
232 232 elif pycompat.iswindows:
233 233 # parse escape sequence by win32print()
234 234 class uifp(object):
235 235 def __init__(self, ui):
236 236 self._ui = ui
237 237
238 238 def write(self, data):
239 239 self._ui.write_err(data)
240 240
241 241 def flush(self):
242 242 self._ui.flush()
243 243
244 244 self._fpdoclose = False
245 245 self._fp = uifp(self._ui)
246 246 else:
247 247 self._fpdoclose = False
248 248 self._fp = self._ui.ferr
249 249
250 250 if proffn is not None:
251 251 pass
252 252 elif profiler == b'ls':
253 253 proffn = lsprofile
254 254 elif profiler == b'flame':
255 255 proffn = flameprofile
256 256 else:
257 257 proffn = statprofile
258 258
259 259 self._profiler = proffn(self._ui, self._fp)
260 260 self._profiler.__enter__()
261 261 except: # re-raises
262 262 self._closefp()
263 263 raise
264 264
265 265 def __exit__(self, exception_type, exception_value, traceback):
266 266 propagate = None
267 267 if self._profiler is not None:
268 268 propagate = self._profiler.__exit__(
269 269 exception_type, exception_value, traceback
270 270 )
271 271 if self._output == b'blackbox':
272 272 val = b'Profile:\n%s' % self._fp.getvalue()
273 273 # ui.log treats the input as a format string,
274 274 # so we need to escape any % signs.
275 275 val = val.replace(b'%', b'%%')
276 276 self._ui.log(b'profile', val)
277 277 self._closefp()
278 278 return propagate
279 279
280 280 def _closefp(self):
281 281 if self._fpdoclose and self._fp is not None:
282 282 self._fp.close()
General Comments 0
You need to be logged in to leave comments. Login now