#! /usr/bin/env python # $Id: pydb.py,v 1.2 1998/11/12 23:11:33 zeller Exp $ # pydb.py -- a Python debugger! # Modified original pdb.py # modified by N Matloff per the advice of the original author, R Wolff # (see lines with "RW" in the comments) # (See pdb.doc for original documentation.) import string import sys import linecache import cmd import bdb import repr import os import posix import pydbcmd from pydbsupt import Breakpoint, effective, Display, DisplayNode # Interaction prompt line will separate file and call info from code # text using value of line_prefix string. A newline and arrow may # be to your liking. You can set it once pydb is imported using the # command "pydb.line_prefix = '\n% '". # line_prefix = ': ' # Use this to get the old situation back line_prefix = '\n-> ' # Probably a better default class Pdb(bdb.Bdb, pydbcmd.Cmd): def __init__(self): bdb.Bdb.__init__(self) pydbcmd.Cmd.__init__(self) self.prompt = '(Pdb) ' self.running = 0 self.filetodebug = "" self.moduletodebug = "" self.nextbpnumber = 1 self.latestbp = None # for stack print routine self.cmdqueue = [] def reset(self): bdb.Bdb.reset(self) self.forget() def forget(self): self.lineno = None self.stack = [] self.curindex = 0 self.curframe = None def setup(self, f, t): self.forget() self.stack, self.curindex = self.get_stack(f, t) self.curframe = self.stack[self.curindex][0] # Override Bdb methods (except user_call, for now) def user_line(self, frame): # This function is called when we stop or break at this line self.interaction(frame, None) def user_return(self, frame, return_value): # This function is called when a return trap is set here frame.f_locals['__return__'] = return_value print '--Return--' self.interaction(frame, None) def user_exception(self, frame, (exc_type, exc_value, exc_traceback)): # This function is called if an exception occurs, # but only if we are to stop at or just below this level frame.f_locals['__exception__'] = exc_type, exc_value if type(exc_type) == type(''): exc_type_name = exc_type else: exc_type_name = exc_type.__name__ print exc_type_name + ':', repr.repr(exc_value) self.interaction(frame, exc_traceback) # General interaction function def interaction(self, frame, traceback): if self.running != 0: self.setup(frame, traceback) self.print_stack_entry(self.stack[self.curindex]) self.display.displayAny(self.curframe) try: self.cmdloop() except KeyboardInterrupt: self.do_kill(None) self.latestbp = None # Can forget it now that we're moving on self.forget() def default(self, line): if line[:1] == '!': line = line[1:] # Try both the local and "outer" environments. both = 0 try: locals = self.curframe.f_locals globals = self.curframe.f_globals both = 1 except: locals = self.outerlocals globals = self.outerglobals globals['__privileged__'] = 1 try: code = compile(line + '\n', '', 'single') exec code in globals, locals except: if both: # Failed with current frame, so try 'outer' world. locals = self.outerlocals globals = self.outerglobals globals['__privileged__'] = 1 try: code = compile(line + '\n', '', 'single') exec code in globals, locals except: t, v = sys.exc_info()[:2] if type(t) == type(''): exc_type_name = t else: exc_type_name = t.__name__ print '***', exc_type_name + ':', v else: t, v = sys.exc_info()[:2] if type(t) == type(''): exc_type_name = t else: exc_type_name = t.__name__ print '***', exc_type_name + ':', v # Override Bdb (note number of args is different) def set_break(self, filename, lineno, temporary=0): filename = self.canonic(filename) # changed per RW import linecache # Import as late as possible line = linecache.getline(filename, lineno) if not line: return 'That line does not exist!' if not self.breaks.has_key(filename): self.breaks[filename] = [] list = self.breaks[filename] if not lineno in list: list.append(lineno) bp = Breakpoint(filename, lineno, temporary) print 'Breakpoint %d, at %s:%d.' %(bp.number, filename, lineno) # Override Bdb def break_here(self, frame): filename=frame.f_code.co_filename filename = self.canonic(filename) # changed per RW if not self.breaks.has_key(filename): return 0 lineno=frame.f_lineno if not lineno in self.breaks[filename]: return 0 # flag says ok to delete temp. bp (bp, flag) = effective(filename, lineno, frame) if bp: self.latestbp = bp.number if (flag and bp.temporary): self.do_delete(str(bp.number)) return 1 else: return 0 # Command definitions, called by cmdloop() # The argument is the remaining string on the command line # Return true to exit from the command loop do_h = pydbcmd.Cmd.do_help def do_break(self, arg, temporary = 0): if not arg: print self.get_all_breaks() return f_l = string.split(arg, ':') # Setting a breakpoint at func:line if len(f_l) == 2: filename = f_l[0] lineno = int(f_l[1]) line = self.checkline(filename, lineno) if line: # now set the break point err = self.set_break(filename, lineno, temporary) if err: print '***', err return try: lineno = int(arg) try: filename = self.curframe.f_code.co_filename except: filename = self.filetodebug # not tracing yet except: # Try function name as the argument try: func = arg if hasattr(func, 'im_func'): func = func.im_func code = func.func_code lineno = code.co_firstlineno filename = code.co_filename except: # One more thing to try: answer = self.lineinfo("'" + arg + "'") if not answer[0]: print '*** The specified object "', arg, print '" is not a function' return (junk, filename, l) = answer lineno = int(l) line = self.checkline(filename, lineno) if line: # now set the break point err = self.set_break(filename, line, temporary) if err: print '***', err return do_b = do_break def do_tbreak(self, arg): self.do_break(arg, 1) def checkline(self, filename, lineno): """Return line number of first line at or after input argument such that if the input points to a 'def', the returned line number is the first non-blank/non-comment line to follow. If the input points to a blank or comment line, return 0. At end of file, also return 0.""" line = linecache.getline(filename, lineno) if not line: print 'End of file' return 0 line = string.strip(line) # Don't allow setting breakpoint at a blank line if ( not line or (line[0] == '#') or (line[:3] == '"""') or line[:3] == "'''" ): print '*** Blank or comment' return 0 # When a file is read in and a breakpoint is at # the 'def' statement, the system stops there at # code parse time. We don't want that, so all breakpoints # set at 'def' statements are moved one line onward if line[:3] == 'def': incomment = '' while 1: lineno = lineno+1 line = linecache.getline(filename, lineno) if not line: print 'end of file' return 0 line = string.strip(line) if incomment: if len(line) < 3: continue if (line[-3:] == incomment): incomment = '' continue if not line: continue # Blank line if len(line) >= 3: if ( line[:3] == '"""' or line[:3] == "'''" ): incomment = line[:3] continue if line[0] != '#': break return lineno def do_enable(self, arg): args = string.split(arg) if args[0] == 'display': self.display_enable(args[1:], 1) return for i in args: bp = Breakpoint.bpbynumber[int(i)] if bp: bp.enable() def do_disable(self, arg): args = string.split(arg) if args[0] == 'display': self.display_enable(args[1:], 0) return for i in args: bp = Breakpoint.bpbynumber[int(i)] if bp: bp.disable() def display_enable(self, arg, flag): # arg is list of display points for i in arg: self.display.enable(int(i), flag) def do_condition(self, arg): # arg is breakpoint number and condition args = string.split(arg, ' ', 1) bpnum = int(string.strip(args[0])) try: cond = args[1] except: cond = None bp = Breakpoint.bpbynumber[bpnum] if bp: bp.cond = cond if not cond: print 'Breakpoint %d is now unconditional.' % (bpnum,) def do_ignore(self,arg): # arg is bp number followed by ignore count args = string.split(arg) bpnum = int(string.strip(args[0])) try: count = int(string.strip(args[1])) except: count = 0 bp = Breakpoint.bpbynumber[bpnum] if bp: bp.ignore = count if (count > 0): reply = 'Will ignore next ' if (count > 1): reply = reply + '%d crossings' % (count,) else: reply = reply + '1 crossing' print reply + ' of breakpoint %d.' % (bpnum,) else: print 'Will stop next time breakpoint %d is reached.' % (bpnum,) def do_delete(self, arg): # Delete from a list, or delete all if no arg numberlist = string.split(arg) if numberlist: for i in numberlist: number = int(i) bp = Breakpoint.bpbynumber[number] # filename = bp.file # deleted per RW filename = self.canonic(bp.file) # added per RW lineno = bp.line if not self.breaks.has_key(filename): return 'There are no breakpoints in that file!' if lineno not in self.breaks[filename]: return 'There is no breakpoint there!' # If there's only one bp in the list for that file,line # pair, then remove the breaks entry if len(Breakpoint.bplist[filename, lineno]) == 1: self.breaks[filename].remove(lineno) if not self.breaks[filename]: del self.breaks[filename] bp.deleteMe() print 'Deleted breakpoint %d ' % (number,) else: # delete them all for bp in Breakpoint.bpbynumber: if bp: bp.deleteMe() self.breaks = {} def do_where(self, arg): if arg: count = int(string.strip(arg)) # neg count possible ?? XXX else: count = 10000 # 'huge' number # Last two frames are internal to debugger and don't # get printed for j in range(len(self.stack)-1, 1, -1): self.frame_print(j) count = count -1 if count == 0: break do_w = do_where def frame_print(self, j): prefix = '#%d ' % (len(self.stack) -1 - j,) res = self.format_stack_entry(self.stack[j], prefix) print string.split(res, '\n', 1)[0] def do_frame(self, arg): if not arg: self.frame_print(self.curindex) else: goto = int(string.strip(arg)) # Should never happen, but if you want to be sure: # Replace -1 with -3 if limited to frames of debugged program if (goto < 0 or goto > len(self.stack)-1): return self.curindex = len(self.stack) - 1 - goto self.curframe = self.stack[self.curindex][0] self.frame_print(self.curindex) self.lineno = None def do_up(self, arg): # Replace 0 with 2 if up is limited to frames of debugged program if self.curindex == 2: print 'Initial frame selected; you cannot go up.' else: self.curindex = self.curindex - 1 self.curframe = self.stack[self.curindex][0] self.frame_print(self.curindex) self.lineno = None do_u = do_up def do_down(self, arg): if self.curindex + 1 == len(self.stack): print 'Bottom (i.e., innermost) frame selected; you cannot go down.' else: self.curindex = self.curindex + 1 self.curframe = self.stack[self.curindex][0] self.frame_print(self.curindex) self.lineno = None do_d = do_down def do_step(self, arg): self.set_step() return 1 do_s = do_step def do_next(self, arg): self.set_next(self.curframe) return 1 do_n = do_next def do_return(self, arg): self.set_return(self.curframe) return 1 do_r = do_return def do_continue(self, arg): self.set_continue() return 1 do_c = do_cont = do_continue def do_quit(self, arg): if self.running: self.set_quit() self.running = 0 self.alldie = 1 return 1 do_q = do_quit def do_kill(self, arg): self.set_quit() self.running = 0 return 1 do_k = do_kill def do_args(self, arg): f = self.curframe co = f.f_code dict = f.f_locals n = co.co_argcount if co.co_flags & 4: n = n+1 if co.co_flags & 8: n = n+1 for i in range(n): name = co.co_varnames[i] print name, '=', if dict.has_key(name): print dict[name] else: print "*** undefined ***" do_a = do_args def do_retval(self, arg): if self.curframe.f_locals.has_key('__return__'): print self.curframe.f_locals['__return__'] else: print '*** Not yet returned!' do_rv = do_retval def do_p(self, arg): try: l = self.curframe.f_locals g = self.curframe.f_globals except: l = self.outerlocals g = self.outerglobals g['__privileged__'] = 1 try: value = eval(arg, g, l) except: return print `value` def do_list(self, arg): if self.filetodebug == "": print 'No symbol table is loaded. Use the "file" command.' return self.lastcmd = 'list' last = None if arg: try: x = eval(arg, {}, {}) if type(x) == type(()): first, last = x first = int(first) last = int(last) if last < first: # Assume it's a count last = first + last else: first = max(1, int(x) - 5) except: print '*** Error in argument:', `arg` return elif self.lineno is None: if self.running == 0: first = 1 else: first = max(1, self.curframe.f_lineno - 5) else: first = self.lineno + 1 if last == None: last = first + 10 if self.running == 0: filename = self.filetodebug else: filename = self.curframe.f_code.co_filename try: for lineno in range(first, last+1): line = linecache.getline(filename, lineno) if not line: print '[EOF]' break else: s = string.rjust(`lineno`, 3) if len(s) < 4: s = s + ' ' print s + '\t' + line, self.lineno = lineno except KeyboardInterrupt: pass do_l = do_list def do_whatis(self, arg): try: value = eval(arg, self.curframe.f_globals, \ self.curframe.f_locals) except: print 'No symbol "%s" in current context.' % (arg,) return code = None # Is it a function? try: code = value.func_code except: pass if code: print 'type = Function', code.co_name return # Is it an instance method? try: code = value.im_func.func_code except: pass if code: print 'type = Method', code.co_name return # None of the above... print 'type = ' + type(value) do_ptype = do_whatis # Print a traceback starting at the top stack frame. # The most recently entered frame is printed last; # this is different from dbx and gdb, but consistent with # the Python interpreter's stack trace. # It is also consistent with the up/down commands (which are # compatible with dbx and gdb: up moves towards 'main()' # and down moves towards the most recent stack frame). def print_stack_trace(self): try: for frame_lineno in self.stack: self.print_stack_entry(frame_lineno) except KeyboardInterrupt: pass def print_stack_entry(self, frame_lineno, prompt_prefix=line_prefix): frame, lineno = frame_lineno prefix = "" if not self.stop_here(frame) and self.latestbp: prefix = "Breakpoint %d, "% (self.latestbp,) print self.format_stack_entry(frame_lineno, prefix) def format_stack_entry(self, frame_lineno, prefix=""): import linecache, repr, string frame, lineno = frame_lineno filename = frame.f_code.co_filename s = ' at ' + filename + ':' + `lineno` if frame.f_code.co_name: f = frame.f_code.co_name else: f = "" if frame.f_locals.has_key('__args__'): args = frame.f_locals['__args__'] else: args = None if args: a = '(' + repr.repr(args) + ')' else: a = '()' first_line = prefix + f + a + s + '\n' # Don't want the ?() at : line printed out; confuses ddd if first_line[:15] == '?() at ': return 'Issue "continue" command' second_line = `lineno` + ' ' line = linecache.getline(filename, lineno) if line: second_line = second_line + string.strip(line) result = first_line + second_line if frame.f_locals.has_key('__return__'): rv = frame.f_locals['__return__'] third_line = 'Value returned is $1 = ' + repr.repr(rv) result = result + '\n' + third_line return result def do_info(self,args): arglist = string.split(args) arg = arglist[0] if arg == 'program': if not self.running: print 'The program being debugged is not being run.' else: print 'Program stopped.\n' if self.latestbp: print 'It stopped at breakpoint %d.' % (self.latestbp,) return if arg == 'line': #info line identifier if len(arglist) == 2: # lineinfo returns (item, file, lineno) or (None,) answer = self.lineinfo(arglist[1]) if answer[0]: print 'Lineinfo <%s> at %s:%s' % answer return #info line if self.lineno == None: try: file = self.curframe.f_code.co_filename line = self.curframe.f_lineno print 'Line %s of "%s"' % (line, file) except: print 'No line number information available.' else: print 'Line %s of "%s"' %\ (self.lineno, self.curframe.f_code.co_filename) return if arg == 'files': if self.filetodebug == "": pass else: print 'Symbols from "%s".' % (self.filetodebug,) return if arg == 'breakpoints': any = 0 for b in Breakpoint.bpbynumber: if b: if not any: print "Num Type Disp Enb Where" any = 1 b.bpprint() if not any: print 'No breakpoints or watchpoints.' return if arg == 'display': if not self.display.displayAll(): print 'There are no auto-display expressions now.' return print 'Undefined info command: "'+arg+'". Try "help info".' def lineinfo(self, identifier): # Input is identifier in single quotes idstring = string.split(identifier, "'") if len(idstring) == 1: # not in single quotes id = string.strip(idstring[0]) elif len(idstring) == 3: # quoted id = string.strip(idstring[1]) else: return (None,) if id == '': return (None,) parts = string.split(id, '.') if parts[0] == 'self': # What could we do here? return (None,) # Best first guess at file to look at try: fname = self.curframe.f_code.co_filename except: fname = self.filetodebug # not tracing yet if len(parts) == 1: item = parts[0] else: # more than one part. First is module, second is method/class module = parts[0] + '.py' item = parts[1] for dirname in sys.path: while os.path.islink(dirname): dirname = os.readlink(dirname) fname = os.path.join(dirname, module) if os.path.exists(fname): break # take the first grepstring = 'egrep -n "(def|class) *%s *[(:]" %s /dev/null' % (item, fname) answer = os.popen(grepstring, 'r').readline() if answer: f, line, junk = string.split(answer, ':', 2) return(item, f,line) else: return (None,) def do_run(self, arg): # if running, clean up by killing up to outer loop and then run if self.running: self.cmdqueue = ['k', 'k', 'run '+arg] return print 'Starting program:' if self.filetodebug == "": print 'No executable file specified.' return oldp = self.prompt self.prompt = '(pydb) ' # Mark running, reset command count self.running = 1 # Clear hit counts on breakpoints for b in Breakpoint.bpbynumber: if b: b.hits = 0 oldarg = sys.argv argv = [self.filetodebug] if arg: args = string.split(arg) argv = argv + args sys.argv = argv self.run('execfile(' + `self.filetodebug` + ')', {'__name__': '__main__'}) sys.argv = oldarg self.prompt = oldp def do_file(self, fname): if fname == "": if self.filetodebug == "": print 'No exec file now.\nNo symbol file now.' return else: # should confirm this per #Discard symbol table from `/work/.../ddd/cxxtest'? (y or n) n # XXX # del module sys.path = self.originalpath[:] self.filetodebug == "" else: path, module = os.path.split(fname) module = string.split(module, '.')[0] sys.path = self.originalpath[:] sys.path.insert(0, path) # XXX how to clean up name space ?? if self.moduletodebug: try: # del self.moduletodebug self.moduletodebug = '' except: pass line = "import " + module + '\n' code = compile(line, '', 'single') exec code in self.outerglobals, self.outerlocals # Could print 'Reading symbols from fname...done.' for gdb conformance self.filetodebug = fname self.moduletodebug = module # Ultimately for interrogating settable attributes of the debugger def do_show(self, arg): if arg == 'language': print 'The current source language is "auto; currently python".' def do_pwd(self, arg): print 'Working directory ' + os.getcwd() + '.' def do_cd(self, arg): if arg == '': do_pwd(arg) else: os.chdir(arg) return def do_display(self, arg): if not arg: # Display anything active self.display.displayAny(self.curframe) else: # Set up a display arglist = string.split(arg) if len(arglist) == 2: (format, variable) = tuple(arglist) else: format = "" variable = arglist[0] dp = DisplayNode(self.curframe, variable, format) res = dp.checkValid(self.curframe) print res return def do_undisplay(self, arg): args = string.split(arg) for i in args: self.display.deleteOne(int(i)) # Help methods (originally derived from pdb.doc) def help_help(self): self.help_h() def help_h(self): print """h(elp) Without argument, print the list of available commands. With a command name as argument, print help about that command. "help exec" gives help on the ! command""" def help_where(self): self.help_w() def help_w(self): print """w(here) Print a stack trace, with the most recent frame at the bottom.""" def help_down(self): self.help_d() def help_d(self): print """d(own) Move the current frame one level down in the stack trace (to an older frame).""" def help_up(self): self.help_u() def help_u(self): print """u(p) Move the current frame one level up in the stack trace (to a newer frame).""" def help_break(self): self.help_b() def help_b(self): print """b(reak) [file:lineno] [lineno | method] With a line number argument, set a break there in the current or specified file. With a method name, set a break at the first executable line after the 'def'. Without argument, list all breaks.""" def help_tbreak(self): print """tbreak same arguments as break, but breakpoint is removed when first hit.""" def help_delete(self): print """delete [bpnumbers] With a space separated list of breakpoint numbers, deletes those breakpoints. Without the argument, deletes all breakpoints.""" def help_enable(self): print """enable [display] bpnumber Enables the breakpoints given as a space separated list of bp numbers. If the word 'display' is present, the indicated display(s) is/are enabled.""" def help_disable(self): print """disable [display] bpnumber Disables the breakpoints given as a space separated list of bp numbers. If the word 'display' is present, the indicated display(s) is/are disabled.""" def help_ignore(self): print """ignore bpnumber count Sets the ignore count for the given breakpoint number. A breakpoint becomes active when the ignore count is zero. When non-zero, the count is decremented each time the breakpoint is reached and the breakpoint is not disabled and any associated condition evaluates to true.""" def help_condition(self): print """condition bpnumber str_condition str_condition is a string specifying an expression which must evaluate to true before the breakpoint is honored. If str_condition is absent, any existing condition is removed; i.e., the breakpoint is made unconditional.""" def help_step(self): self.help_s() def help_s(self): print """s(tep) Execute the current line, stop at the first possible occasion (either in a function that is called or in the current function).""" def help_next(self): self.help_n() def help_n(self): print """n(ext) Continue execution until the next line in the current function is reached or it returns.""" def help_return(self): self.help_r() def help_r(self): print """r(eturn) Continue execution until the current function returns.""" def help_continue(self): self.help_c() def help_cont(self): self.help_c() def help_c(self): print """c(ont(inue)) Continue execution, only stop when a breakpoint is encountered.""" def help_list(self): self.help_l() def help_l(self): print """l(ist) [first [,last]] List source code for the current file. Without arguments, list 11 lines around the current line or continue the previous listing. With one argument, list 11 lines starting at that line. With two arguments, list the given range; if the second argument is less than the first, it is a count.""" def help_args(self): self.help_a() def help_a(self): print """a(rgs) Print the arguments of the current function.""" def help_p(self): print """p expression Print the value of the expression.""" def help_exec(self): print """(!) statement Execute the (one-line) statement in the context of the current stack frame. The exclamation point can be omitted unless the first word of the statement resembles a debugger command. To assign to a global variable you must always prefix the command with a 'global' command, e.g.: (Pdb) global list_options; list_options = ['-l'] (Pdb)""" def help_quit(self): self.help_q() def help_q(self): print """Quit from the debugger.""" def help_kill(self): self.help_k() def help_k(self): print """Kill the program being debugged The program being executed is aborted.""" def help_info(self): print """Generic command for showing things about the program being debugged. List of info subcommands: info program -- Execution status of the program info line -- If "info line arg", line number and full path to file containing 'def' for arg; if arg omitted, display where currently stopped. info display -- Expressions to display when program stops info breakpoints -- Status of user-settable breakpoints info files -- Names of targets and files being debugged""" def help_show(self): print """Generic command for showing things about the debugger. List of show subcommands: show language -- Show the current source language """ def help_cd(self): print 'Change to directory specified by argument.' def help_display(self): print """display [format] expression Displays the value of the expression whenever the program stops at a breakpoint and the expression is valid in the current namespace which must be the one in which the display command was issued. Format is /a where a is one of[xotfcs] for hex, octal, binary (twos), float, chr and string.""" def help_undisplay(self): print """undisplay dnumber Delete the displays listed in the space separated list dnumber.""" def help_file(self): print """Use 'file' as the program to be debugged. It is the program executed when you use the 'run' command.""" def help_frame(self): print """frame [arg] Prints the current frame location if no argument is given; Changes to the frame 'arg' if 'arg' is present. Frame 0 is the newest one.""" def help_pwd(self): print 'Print working directory.' def help_run(self): print """run [arg1 [arg2 [...]]] Run the program to be debugged. Arguments are space separated and are inserted into sys.argv following the file name from the 'file' command, all for the delectation of the program under study.""" def help_whatis(self): print """whatis arg Prints the type of the argument.""" def help_ptype(self): print """ptype arg Prints the type of the argument.""" help_pydb = help_help # Simplified interface def run(statement, globals=None, locals=None): Pdb().run(statement, globals, locals) def runeval(expression, globals=None, locals=None): return Pdb().runeval(expression, globals, locals) def runctx(statement, globals, locals): # B/W compatibility run(statement, globals, locals) def runcall(*args): return apply(Pdb().runcall, args) def set_trace(): Pdb().set_trace() # Post-Mortem interface def post_mortem(t): p = Pdb() p.reset() while t.tb_next <> None: t = t.tb_next p.interaction(t.tb_frame, t) def pm(): import sys post_mortem(sys.last_traceback) # When invoked as main program, invoke the debugger on a script if __name__=='__main__': import sys import os outer = Pdb() outer.prompt = "(Pdb) " outer.alldie = 0 outer.display = Display() outer.reset() outer.originalpath = sys.path[:] while not outer.alldie: try: 1 + '' except: frame = sys.exc_info()[2].tb_frame outer.outerlocals = frame.f_locals outer.outerglobals = frame.f_locals try: outer.cmdloop() except AttributeError, msg: print "Invalid command in outer loop: %s\n" % (msg,) except KeyboardInterrupt: pass