##// 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 22 from .i18n import _
23 23
24 from hgdemandimport import tracing
25
24 26 from . import (
25 27 cmdutil,
26 28 color,
@@ -84,7 +86,8 b' class request(object):'
84 86 def run():
85 87 "run the command in sys.argv"
86 88 initstdio()
87 req = request(pycompat.sysargv[1:])
89 with tracing.log('parse args into request'):
90 req = request(pycompat.sysargv[1:])
88 91 err = None
89 92 try:
90 93 status = dispatch(req)
@@ -176,182 +179,184 b' def _formatargs(args):'
176 179
177 180 def dispatch(req):
178 181 """run the command specified in req.args; returns an integer status code"""
179 if req.ferr:
180 ferr = req.ferr
181 elif req.ui:
182 ferr = req.ui.ferr
183 else:
184 ferr = procutil.stderr
182 with tracing.log('dispatch.dispatch'):
183 if req.ferr:
184 ferr = req.ferr
185 elif req.ui:
186 ferr = req.ui.ferr
187 else:
188 ferr = procutil.stderr
185 189
186 try:
187 if not req.ui:
188 req.ui = uimod.ui.load()
189 req.earlyoptions.update(_earlyparseopts(req.ui, req.args))
190 if req.earlyoptions['traceback']:
191 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
190 try:
191 if not req.ui:
192 req.ui = uimod.ui.load()
193 req.earlyoptions.update(_earlyparseopts(req.ui, req.args))
194 if req.earlyoptions['traceback']:
195 req.ui.setconfig('ui', 'traceback', 'on', '--traceback')
192 196
193 # set ui streams from the request
194 if req.fin:
195 req.ui.fin = req.fin
196 if req.fout:
197 req.ui.fout = req.fout
198 if req.ferr:
199 req.ui.ferr = req.ferr
200 except error.Abort as inst:
201 ferr.write(_("abort: %s\n") % inst)
202 if inst.hint:
203 ferr.write(_("(%s)\n") % inst.hint)
204 return -1
205 except error.ParseError as inst:
206 _formatparse(ferr.write, inst)
207 return -1
197 # set ui streams from the request
198 if req.fin:
199 req.ui.fin = req.fin
200 if req.fout:
201 req.ui.fout = req.fout
202 if req.ferr:
203 req.ui.ferr = req.ferr
204 except error.Abort as inst:
205 ferr.write(_("abort: %s\n") % inst)
206 if inst.hint:
207 ferr.write(_("(%s)\n") % inst.hint)
208 return -1
209 except error.ParseError as inst:
210 _formatparse(ferr.write, inst)
211 return -1
208 212
209 msg = _formatargs(req.args)
210 starttime = util.timer()
211 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:
213 msg = _formatargs(req.args)
214 starttime = util.timer()
215 ret = 1 # default of Python exit code on unhandled exception
220 216 try:
221 if isinstance(inst, error.SignalInterrupt):
222 msg = _("killed!\n")
223 else:
224 msg = _("interrupted!\n")
225 req.ui.error(msg)
226 except error.SignalInterrupt:
227 # maybe pager would quit without consuming all the output, and
228 # SIGPIPE was raised. we cannot print anything in this case.
229 pass
230 except IOError as inst:
231 if inst.errno != errno.EPIPE:
232 raise
233 ret = -1
234 finally:
235 duration = util.timer() - starttime
236 req.ui.flush()
237 if req.ui.logblockedtimes:
238 req.ui._blockedtimes['command_duration'] = duration * 1000
239 req.ui.log('uiblocked', 'ui blocked ms',
240 **pycompat.strkwargs(req.ui._blockedtimes))
241 req.ui.log("commandfinish", "%s exited %d after %0.2f seconds\n",
242 msg, ret & 255, duration)
243 try:
244 req._runexithandlers()
245 except: # exiting, so no re-raises
246 ret = ret or -1
247 return ret
217 ret = _runcatch(req) or 0
218 except error.ProgrammingError as inst:
219 req.ui.error(_('** ProgrammingError: %s\n') % inst)
220 if inst.hint:
221 req.ui.error(_('** (%s)\n') % inst.hint)
222 raise
223 except KeyboardInterrupt as inst:
224 try:
225 if isinstance(inst, error.SignalInterrupt):
226 msg = _("killed!\n")
227 else:
228 msg = _("interrupted!\n")
229 req.ui.error(msg)
230 except error.SignalInterrupt:
231 # maybe pager would quit without consuming all the output, and
232 # SIGPIPE was raised. we cannot print anything in this case.
233 pass
234 except IOError as inst:
235 if inst.errno != errno.EPIPE:
236 raise
237 ret = -1
238 finally:
239 duration = util.timer() - starttime
240 req.ui.flush()
241 if req.ui.logblockedtimes:
242 req.ui._blockedtimes['command_duration'] = duration * 1000
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 253 def _runcatch(req):
250 def catchterm(*args):
251 raise error.SignalInterrupt
254 with tracing.log('dispatch._runcatch'):
255 def catchterm(*args):
256 raise error.SignalInterrupt
252 257
253 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
258 ui = req.ui
264 259 try:
265 cmdargs = fancyopts.fancyopts(req.args[:], commands.globalopts, {})
266 cmd = cmdargs[0]
267 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
268 realcmd = aliases[0]
269 except (error.UnknownCommand, error.AmbiguousCommand,
270 IndexError, getopt.GetoptError):
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),))
260 for name in 'SIGBREAK', 'SIGHUP', 'SIGTERM':
261 num = getattr(signal, name, None)
262 if num:
263 signal.signal(num, catchterm)
264 except ValueError:
265 pass # happens if called in a thread
293 266
294 try:
295 debugger = 'pdb'
296 debugtrace = {
297 'pdb': pdb.set_trace
298 }
299 debugmortem = {
300 'pdb': pdb.post_mortem
301 }
267 def _runcatchfunc():
268 realcmd = None
269 try:
270 cmdargs = fancyopts.fancyopts(
271 req.args[:], commands.globalopts, {})
272 cmd = cmdargs[0]
273 aliases, entry = cmdutil.findcmd(cmd, commands.table, False)
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
304 # (e.g. to change trust settings for reading .hg/hgrc)
305 cfgs = _parseconfig(req.ui, req.earlyoptions['config'])
306
307 if req.repo:
308 # copy configs that were passed on the cmdline (--config) to
309 # the repo ui
310 for sec, name, val in cfgs:
311 req.repo.ui.setconfig(sec, name, val, source='--config')
300 try:
301 debugger = 'pdb'
302 debugtrace = {
303 'pdb': pdb.set_trace
304 }
305 debugmortem = {
306 'pdb': pdb.post_mortem
307 }
312 308
313 # developer config: ui.debugger
314 debugger = ui.config("ui", "debugger")
315 debugmod = pdb
316 if not debugger or ui.plain():
317 # if we are in HGPLAIN mode, then disable custom debugging
318 debugger = 'pdb'
319 elif req.earlyoptions['debugger']:
320 # This import can be slow for fancy debuggers, so only
321 # do it when absolutely necessary, i.e. when actual
322 # debugging has been requested
323 with demandimport.deactivated():
324 try:
325 debugmod = __import__(debugger)
326 except ImportError:
327 pass # Leave debugmod = pdb
309 # read --config before doing anything else
310 # (e.g. to change trust settings for reading .hg/hgrc)
311 cfgs = _parseconfig(req.ui, req.earlyoptions['config'])
312
313 if req.repo:
314 # copy configs that were passed on the cmdline (--config) to
315 # the repo ui
316 for sec, name, val in cfgs:
317 req.repo.ui.setconfig(sec, name, val, source='--config')
328 318
329 debugtrace[debugger] = debugmod.set_trace
330 debugmortem[debugger] = debugmod.post_mortem
319 # developer config: ui.debugger
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
333 if req.earlyoptions['debugger']:
334 ui.warn(_("entering debugger - "
335 "type c to continue starting hg or h for help\n"))
335 debugtrace[debugger] = debugmod.set_trace
336 debugmortem[debugger] = debugmod.post_mortem
336 337
337 if (debugger != 'pdb' and
338 debugtrace[debugger] == debugtrace['pdb']):
339 ui.warn(_("%s debugger specified "
340 "but its module was not found\n") % debugger)
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
338 # enter the debugger before command execution
339 if req.earlyoptions['debugger']:
340 ui.warn(_("entering debugger - "
341 "type c to continue starting hg or h for help\n"))
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 361 def _callcatch(ui, func):
357 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