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