##// END OF EJS Templates
dispatch: have dispatch.dispatch and dispatch._runcatch emit trace events...
Augie Fackler -
r39291:4019b454 default
parent child Browse files
Show More
@@ -21,6 +21,8 b' import traceback'
21
21
22 from .i18n import _
22 from .i18n import _
23
23
24 from hgdemandimport import tracing
25
24 from . import (
26 from . import (
25 cmdutil,
27 cmdutil,
26 color,
28 color,
@@ -84,7 +86,8 b' class request(object):'
84 def run():
86 def run():
85 "run the command in sys.argv"
87 "run the command in sys.argv"
86 initstdio()
88 initstdio()
87 req = request(pycompat.sysargv[1:])
89 with tracing.log('parse args into request'):
90 req = request(pycompat.sysargv[1:])
88 err = None
91 err = None
89 try:
92 try:
90 status = dispatch(req)
93 status = dispatch(req)
@@ -176,182 +179,184 b' def _formatargs(args):'
176
179
177 def dispatch(req):
180 def dispatch(req):
178 """run the command specified in req.args; returns an integer status code"""
181 """run the command specified in req.args; returns an integer status code"""
179 if req.ferr:
182 with tracing.log('dispatch.dispatch'):
180 ferr = req.ferr
183 if req.ferr:
181 elif req.ui:
184 ferr = req.ferr
182 ferr = req.ui.ferr
185 elif req.ui:
183 else:
186 ferr = req.ui.ferr
184 ferr = procutil.stderr
187 else:
188 ferr = procutil.stderr
185
189
186 try:
190 try:
187 if not req.ui:
191 if not req.ui:
188 req.ui = uimod.ui.load()
192 req.ui = uimod.ui.load()
189 req.earlyoptions.update(_earlyparseopts(req.ui, req.args))
193 req.earlyoptions.update(_earlyparseopts(req.ui, req.args))
190 if req.earlyoptions['traceback']:
194 if req.earlyoptions['traceback']:
191 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
195 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
192
196
193 # set ui streams from the request
197 # set ui streams from the request
194 if req.fin:
198 if req.fin:
195 req.ui.fin = req.fin
199 req.ui.fin = req.fin
196 if req.fout:
200 if req.fout:
197 req.ui.fout = req.fout
201 req.ui.fout = req.fout
198 if req.ferr:
202 if req.ferr:
199 req.ui.ferr = req.ferr
203 req.ui.ferr = req.ferr
200 except error.Abort as inst:
204 except error.Abort as inst:
201 ferr.write(_("abort: %s\n") % inst)
205 ferr.write(_("abort: %s\n") % inst)
202 if inst.hint:
206 if inst.hint:
203 ferr.write(_("(%s)\n") % inst.hint)
207 ferr.write(_("(%s)\n") % inst.hint)
204 return -1
208 return -1
205 except error.ParseError as inst:
209 except error.ParseError as inst:
206 _formatparse(ferr.write, inst)
210 _formatparse(ferr.write, inst)
207 return -1
211 return -1
208
212
209 msg = _formatargs(req.args)
213 msg = _formatargs(req.args)
210 starttime = util.timer()
214 starttime = util.timer()
211 ret = 1 # default of Python exit code on unhandled exception
215 ret = 1 # default of Python exit code on unhandled exception
212 try:
213 ret = _runcatch(req) or 0
214 except error.ProgrammingError as inst:
215 req.ui.error(_('** ProgrammingError: %s\n') % inst)
216 if inst.hint:
217 req.ui.error(_('** (%s)\n') % inst.hint)
218 raise
219 except KeyboardInterrupt as inst:
220 try:
216 try:
221 if isinstance(inst, error.SignalInterrupt):
217 ret = _runcatch(req) or 0
222 msg = _("killed!\n")
218 except error.ProgrammingError as inst:
223 else:
219 req.ui.error(_('** ProgrammingError: %s\n') % inst)
224 msg = _("interrupted!\n")
220 if inst.hint:
225 req.ui.error(msg)
221 req.ui.error(_('** (%s)\n') % inst.hint)
226 except error.SignalInterrupt:
222 raise
227 # maybe pager would quit without consuming all the output, and
223 except KeyboardInterrupt as inst:
228 # SIGPIPE was raised. we cannot print anything in this case.
224 try:
229 pass
225 if isinstance(inst, error.SignalInterrupt):
230 except IOError as inst:
226 msg = _("killed!\n")
231 if inst.errno != errno.EPIPE:
227 else:
232 raise
228 msg = _("interrupted!\n")
233 ret = -1
229 req.ui.error(msg)
234 finally:
230 except error.SignalInterrupt:
235 duration = util.timer() - starttime
231 # maybe pager would quit without consuming all the output, and
236 req.ui.flush()
232 # SIGPIPE was raised. we cannot print anything in this case.
237 if req.ui.logblockedtimes:
233 pass
238 req.ui._blockedtimes['command_duration'] = duration * 1000
234 except IOError as inst:
239 req.ui.log('uiblocked', 'ui blocked ms',
235 if inst.errno != errno.EPIPE:
240 **pycompat.strkwargs(req.ui._blockedtimes))
236 raise
241 req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n",
237 ret = -1
242 msg, ret & 255, duration)
238 finally:
243 try:
239 duration = util.timer() - starttime
244 req._runexithandlers()
240 req.ui.flush()
245 except: # exiting, so no re-raises
241 if req.ui.logblockedtimes:
246 ret = ret or -1
242 req.ui._blockedtimes['command_duration'] = duration * 1000
247 return ret
243 req.ui.log('uiblocked', 'ui blocked ms',
244 **pycompat.strkwargs(req.ui._blockedtimes))
245 req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n",
246 msg, ret & 255, duration)
247 try:
248 req._runexithandlers()
249 except: # exiting, so no re-raises
250 ret = ret or -1
251 return ret
248
252
249 def _runcatch(req):
253 def _runcatch(req):
250 def catchterm(*args):
254 with tracing.log('dispatch._runcatch'):
251 raise error.SignalInterrupt
255 def catchterm(*args):
256 raise error.SignalInterrupt
252
257
253 ui = req.ui
258 ui = req.ui
254 try:
255 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
256 num = getattr(signal, name, None)
257 if num:
258 signal.signal(num, catchterm)
259 except ValueError:
260 pass # happens if called in a thread
261
262 def _runcatchfunc():
263 realcmd = None
264 try:
259 try:
265 cmdargs = fancyopts.fancyopts(req.args[:], commands.globalopts, {})
260 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
266 cmd = cmdargs[0]
261 num = getattr(signal, name, None)
267 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
262 if num:
268 realcmd = aliases[0]
263 signal.signal(num, catchterm)
269 except (error.UnknownCommand, error.AmbiguousCommand,
264 except ValueError:
270 IndexError, getopt.GetoptError):
265 pass # happens if called in a thread
271 # Don't handle this here. We know the command is
272 # invalid, but all we're worried about for now is that
273 # it's not a command that server operators expect to
274 # be safe to offer to users in a sandbox.
275 pass
276 if realcmd == 'serve' and '--stdio' in cmdargs:
277 # We want to constrain 'hg serve --stdio' instances pretty
278 # closely, as many shared-ssh access tools want to grant
279 # access to run *only* 'hg -R $repo serve --stdio'. We
280 # restrict to exactly that set of arguments, and prohibit
281 # any repo name that starts with '--' to prevent
282 # shenanigans wherein a user does something like pass
283 # --debugger or --config=ui.debugger=1 as a repo
284 # name. This used to actually run the debugger.
285 if (len(req.args) != 4 or
286 req.args[0] != '-R' or
287 req.args[1].startswith('--') or
288 req.args[2] != 'serve' or
289 req.args[3] != '--stdio'):
290 raise error.Abort(
291 _('potentially unsafe serve --stdio invocation: %s') %
292 (stringutil.pprint(req.args),))
293
266
294 try:
267 def _runcatchfunc():
295 debugger = 'pdb'
268 realcmd = None
296 debugtrace = {
269 try:
297 'pdb': pdb.set_trace
270 cmdargs = fancyopts.fancyopts(
298 }
271 req.args[:], commands.globalopts, {})
299 debugmortem = {
272 cmd = cmdargs[0]
300 'pdb': pdb.post_mortem
273 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
301 }
274 realcmd = aliases[0]
275 except (error.UnknownCommand, error.AmbiguousCommand,
276 IndexError, getopt.GetoptError):
277 # Don't handle this here. We know the command is
278 # invalid, but all we're worried about for now is that
279 # it's not a command that server operators expect to
280 # be safe to offer to users in a sandbox.
281 pass
282 if realcmd == 'serve' and '--stdio' in cmdargs:
283 # We want to constrain 'hg serve --stdio' instances pretty
284 # closely, as many shared-ssh access tools want to grant
285 # access to run *only* 'hg -R $repo serve --stdio'. We
286 # restrict to exactly that set of arguments, and prohibit
287 # any repo name that starts with '--' to prevent
288 # shenanigans wherein a user does something like pass
289 # --debugger or --config=ui.debugger=1 as a repo
290 # name. This used to actually run the debugger.
291 if (len(req.args) != 4 or
292 req.args[0] != '-R' or
293 req.args[1].startswith('--') or
294 req.args[2] != 'serve' or
295 req.args[3] != '--stdio'):
296 raise error.Abort(
297 _('potentially unsafe serve --stdio invocation: %s') %
298 (stringutil.pprint(req.args),))
302
299
303 # read --config before doing anything else
300 try:
304 # (e.g. to change trust settings for reading .hg/hgrc)
301 debugger = 'pdb'
305 cfgs = _parseconfig(req.ui, req.earlyoptions['config'])
302 debugtrace = {
306
303 'pdb': pdb.set_trace
307 if req.repo:
304 }
308 # copy configs that were passed on the cmdline (--config) to
305 debugmortem = {
309 # the repo ui
306 'pdb': pdb.post_mortem
310 for sec, name, val in cfgs:
307 }
311 req.repo.ui.setconfig(sec, name, val, source='--config')
312
308
313 # developer config: ui.debugger
309 # read --config before doing anything else
314 debugger = ui.config("ui", "debugger")
310 # (e.g. to change trust settings for reading .hg/hgrc)
315 debugmod = pdb
311 cfgs = _parseconfig(req.ui, req.earlyoptions['config'])
316 if not debugger or ui.plain():
312
317 # if we are in HGPLAIN mode, then disable custom debugging
313 if req.repo:
318 debugger = 'pdb'
314 # copy configs that were passed on the cmdline (--config) to
319 elif req.earlyoptions['debugger']:
315 # the repo ui
320 # This import can be slow for fancy debuggers, so only
316 for sec, name, val in cfgs:
321 # do it when absolutely necessary, i.e. when actual
317 req.repo.ui.setconfig(sec, name, val, source='--config')
322 # debugging has been requested
323 with demandimport.deactivated():
324 try:
325 debugmod = __import__(debugger)
326 except ImportError:
327 pass # Leave debugmod = pdb
328
318
329 debugtrace[debugger] = debugmod.set_trace
319 # developer config: ui.debugger
330 debugmortem[debugger] = debugmod.post_mortem
320 debugger = ui.config("ui", "debugger")
321 debugmod = pdb
322 if not debugger or ui.plain():
323 # if we are in HGPLAIN mode, then disable custom debugging
324 debugger = 'pdb'
325 elif req.earlyoptions['debugger']:
326 # This import can be slow for fancy debuggers, so only
327 # do it when absolutely necessary, i.e. when actual
328 # debugging has been requested
329 with demandimport.deactivated():
330 try:
331 debugmod = __import__(debugger)
332 except ImportError:
333 pass # Leave debugmod = pdb
331
334
332 # enter the debugger before command execution
335 debugtrace[debugger] = debugmod.set_trace
333 if req.earlyoptions['debugger']:
336 debugmortem[debugger] = debugmod.post_mortem
334 ui.warn(_("entering debugger - "
335 "type c to continue starting hg or h for help\n"))
336
337
337 if (debugger != 'pdb' and
338 # enter the debugger before command execution
338 debugtrace[debugger] == debugtrace['pdb']):
339 if req.earlyoptions['debugger']:
339 ui.warn(_("%s debugger specified "
340 ui.warn(_("entering debugger - "
340 "but its module was not found\n") % debugger)
341 "type c to continue starting hg or h for help\n"))
341 with demandimport.deactivated():
342 debugtrace[debugger]()
343 try:
344 return _dispatch(req)
345 finally:
346 ui.flush()
347 except: # re-raises
348 # enter the debugger when we hit an exception
349 if req.earlyoptions['debugger']:
350 traceback.print_exc()
351 debugmortem[debugger](sys.exc_info()[2])
352 raise
353
342
354 return _callcatch(ui, _runcatchfunc)
343 if (debugger != 'pdb' and
344 debugtrace[debugger] == debugtrace['pdb']):
345 ui.warn(_("%s debugger specified "
346 "but its module was not found\n") % debugger)
347 with demandimport.deactivated():
348 debugtrace[debugger]()
349 try:
350 return _dispatch(req)
351 finally:
352 ui.flush()
353 except: # re-raises
354 # enter the debugger when we hit an exception
355 if req.earlyoptions['debugger']:
356 traceback.print_exc()
357 debugmortem[debugger](sys.exc_info()[2])
358 raise
359 return _callcatch(ui, _runcatchfunc)
355
360
356 def _callcatch(ui, func):
361 def _callcatch(ui, func):
357 """like scmutil.callcatch but handles more high-level exceptions about
362 """like scmutil.callcatch but handles more high-level exceptions about
General Comments 0
You need to be logged in to leave comments. Login now