[pypy-commit] pypy reverse-debugger: Trying to support pressing Ctrl-C to interrupt the current operation
arigo
pypy.commits at gmail.com
Fri Aug 12 16:54:09 EDT 2016
Author: Armin Rigo <arigo at tunes.org>
Branch: reverse-debugger
Changeset: r86180:2311c3b8a675
Date: 2016-08-12 22:44 +0200
http://bitbucket.org/pypy/pypy/changeset/2311c3b8a675/
Log: Trying to support pressing Ctrl-C to interrupt the current operation
diff --git a/rpython/translator/revdb/interact.py b/rpython/translator/revdb/interact.py
--- a/rpython/translator/revdb/interact.py
+++ b/rpython/translator/revdb/interact.py
@@ -35,59 +35,79 @@
self.print_extra_pending_info = None
def interact(self):
- last_command = 'help'
- previous_time = None
- previous_thread = 0
+ self.last_command = 'help'
+ self.previous_time = None
+ self.previous_thread = 0
while True:
+ prompt = self.print_lines_before_prompt()
+ try:
+ while True:
+ cmdline = self.display_prompt(prompt)
+ self.run_command(cmdline)
+ prompt = self.print_lines_before_prompt()
+ except KeyboardInterrupt:
+ self.pgroup.recreate_subprocess(self.previous_time or 1)
+ self.last_command = ''
+ self.previous_thread = '?'
+ self.previous_time = '?'
+
+ def print_lines_before_prompt(self):
+ last_time = self.pgroup.get_current_time()
+ if last_time != self.previous_time:
+ print
+ if self.pgroup.get_current_thread() != self.previous_thread:
+ self.previous_thread = self.pgroup.get_current_thread()
+ if self.previous_thread == 0:
+ print ('-------------------- in main thread #0 '
+ '--------------------')
+ else:
+ print ('-------------------- in non-main thread '
+ '#%d --------------------' % (self.previous_thread,))
+ self.pgroup.update_watch_values()
last_time = self.pgroup.get_current_time()
- if last_time != previous_time:
- print
- if self.pgroup.get_current_thread() != previous_thread:
- previous_thread = self.pgroup.get_current_thread()
- if previous_thread == 0:
- print ('-------------------- in main thread #0 '
- '--------------------')
- else:
- print ('-------------------- in non-main thread '
- '#%d --------------------' % (previous_thread,))
- self.pgroup.update_watch_values()
- last_time = self.pgroup.get_current_time()
- if self.print_extra_pending_info:
- print self.print_extra_pending_info
- self.print_extra_pending_info = None
- if last_time != previous_time:
- self.pgroup.show_backtrace(complete=0)
- previous_time = last_time
+ if self.print_extra_pending_info:
+ print self.print_extra_pending_info
+ self.print_extra_pending_info = None
+ if last_time != self.previous_time:
+ self.pgroup.show_backtrace(complete=0)
+ self.previous_time = last_time
+ prompt = '(%d)$ ' % last_time
+ return prompt
- prompt = '(%d)$ ' % last_time
+ def display_prompt(self, prompt):
+ try:
+ cmdline = raw_input(prompt).strip()
+ except EOFError:
+ print
+ cmdline = 'quit'
+ if not cmdline:
+ cmdline = self.last_command
+ return cmdline
+
+ def run_command(self, cmdline):
+ match = r_cmdline.match(cmdline)
+ if not match:
+ return
+ self.last_command = cmdline
+ command, argument = match.groups()
+ try:
+ runner = getattr(self, 'command_' + command)
+ except AttributeError:
+ print >> sys.stderr, "no command '%s', try 'help'" % (command,)
+ else:
try:
- cmdline = raw_input(prompt).strip()
- except EOFError:
- print
- cmdline = 'quit'
- if not cmdline:
- cmdline = last_command
- match = r_cmdline.match(cmdline)
- if not match:
- continue
- last_command = cmdline
- command, argument = match.groups()
- try:
- runner = getattr(self, 'command_' + command)
- except AttributeError:
- print >> sys.stderr, "no command '%s', try 'help'" % (command,)
- else:
- try:
- runner(argument)
- except Exception as e:
- traceback.print_exc()
- print >> sys.stderr
- print >> sys.stderr, 'Something went wrong. You are now',
- print >> sys.stderr, 'in a pdb; press Ctrl-D to continue.'
- import pdb; pdb.post_mortem(sys.exc_info()[2])
- print >> sys.stderr
- print >> sys.stderr, 'You are back running %s.' % (
- sys.argv[0],)
+ runner(argument)
+ except KeyboardInterrupt:
+ raise
+ except Exception as e:
+ traceback.print_exc()
+ print >> sys.stderr
+ print >> sys.stderr, 'Something went wrong. You are now',
+ print >> sys.stderr, 'in a pdb; press Ctrl-D to continue.'
+ import pdb; pdb.post_mortem(sys.exc_info()[2])
+ print >> sys.stderr
+ print >> sys.stderr, 'You are back running %s.' % (
+ sys.argv[0],)
def command_help(self, argument):
"""Display commands summary"""
diff --git a/rpython/translator/revdb/process.py b/rpython/translator/revdb/process.py
--- a/rpython/translator/revdb/process.py
+++ b/rpython/translator/revdb/process.py
@@ -132,11 +132,11 @@
self.currently_created_objects = msg.arg2
self.current_thread = msg.arg3
- def clone(self):
+ def clone(self, activate=False):
"""Fork this subprocess. Returns a new ReplayProcess() that is
an identical copy.
"""
- self.send(Message(CMD_FORK))
+ self.send(Message(CMD_FORK, int(activate)))
s1, s2 = socket.socketpair()
ancillary.send_fds(self.control_socket.fileno(), [s2.fileno()])
s2.close()
@@ -459,7 +459,7 @@
clone_me = self.paused[from_time]
if self.active is not None:
self.active.close()
- self.active = clone_me.clone()
+ self.active = clone_me.clone(activate=True)
def jump_in_time(self, target_time):
"""Jump in time at the given 'target_time'.
@@ -561,11 +561,13 @@
self.active.send(Message(CMD_ATTACHID, nid, uid, int(watch_env)))
self.active.expect_ready()
- def recreate_subprocess(self):
- # recreate a subprocess at the current time
- time = self.get_current_time()
+ def recreate_subprocess(self, target_time=None):
+ # recreate a subprocess at the given time, or by default the
+ # current time
+ if target_time is None:
+ target_time = self.get_current_time()
self.active = None
- self.jump_in_time(time)
+ self.jump_in_time(target_time)
def print_cmd(self, expression, nids=[]):
"""Print an expression.
diff --git a/rpython/translator/revdb/src-revdb/revdb.c b/rpython/translator/revdb/src-revdb/revdb.c
--- a/rpython/translator/revdb/src-revdb/revdb.c
+++ b/rpython/translator/revdb/src-revdb/revdb.c
@@ -1249,7 +1249,7 @@
exit(0);
}
-static void command_fork(void)
+static void command_fork(int activate)
{
int child_sockfd;
int child_pid;
@@ -1272,6 +1272,11 @@
}
rpy_rev_sockfd = child_sockfd;
+ /* The 'activate' flag of CMD_FORK tells if the child process
+ must die or not when receiving SIGINT. Active children
+ die; inactive children (stored in 'pgroup.paused') don't. */
+ signal(SIGINT, activate ? SIG_DFL : SIG_IGN);
+
/* Close and re-open the revdb log file in the child process.
This is the simplest way I found to give 'rpy_rev_fileno'
its own offset, independent from the parent. It assumes
@@ -1422,7 +1427,7 @@
switch (cmd.cmd) {
case CMD_FORK:
- command_fork();
+ command_fork(cmd.arg1);
break;
case CMD_QUIT:
diff --git a/rpython/translator/revdb/test/ctrl_c.py b/rpython/translator/revdb/test/ctrl_c.py
new file mode 100644
--- /dev/null
+++ b/rpython/translator/revdb/test/ctrl_c.py
@@ -0,0 +1,43 @@
+import sys, os, thread, time, signal
+
+os.setpgid(0, 0)
+assert os.getpgrp() == os.getpid()
+
+
+sys.path[:] = sys.argv[1].split('\x7f')
+from rpython.translator.revdb.process import ReplayProcessGroup
+
+exename, rdbname = sys.argv[2:]
+group = ReplayProcessGroup(exename, rdbname)
+
+
+class MyInterrupt(Exception):
+ pass
+def my_signal(*args):
+ raise MyInterrupt
+prev_signal = signal.signal(signal.SIGINT, my_signal)
+
+def enable_timer():
+ def my_kill():
+ time.sleep(0.8)
+ print >> sys.stderr, "--<<< Sending CTRL-C >>>--"
+ os.killpg(os.getpid(), signal.SIGINT)
+ thread.start_new_thread(my_kill, ())
+
+all_ok = False
+try:
+ # this runs for ~9 seconds if uninterrupted
+ enable_timer()
+ group.print_cmd('very-long-loop')
+except MyInterrupt:
+ print >> sys.stderr, "very-long-loop interrupted, trying again"
+ group.recreate_subprocess(1)
+ try:
+ enable_timer()
+ group.print_cmd('very-long-loop')
+ except MyInterrupt:
+ print >> sys.stderr, "second interruption ok"
+ all_ok = True
+
+assert all_ok, "expected very-long-loop to be killed by SIGINT"
+print "all ok"
diff --git a/rpython/translator/revdb/test/test_process.py b/rpython/translator/revdb/test/test_process.py
--- a/rpython/translator/revdb/test/test_process.py
+++ b/rpython/translator/revdb/test/test_process.py
@@ -1,4 +1,4 @@
-import py, sys, math
+import py, sys, math, os, subprocess, time
from cStringIO import StringIO
from rpython.rlib import revdb, rdtoa
from rpython.rlib.debug import debug_print, ll_assert
@@ -56,6 +56,14 @@
revdb.send_output(rdtoa.dtoa(xx) + '\n')
revdb.send_output('%d\n' % yy)
return
+ elif extra == 'very-long-loop':
+ i = 0
+ total = 0
+ while i < 2000000000:
+ total += revdb.flag_io_disabled()
+ i += 1
+ revdb.send_output(str(total))
+ return
else:
assert False
uid = revdb.get_unique_id(stuff)
@@ -214,3 +222,16 @@
with stdout_capture() as buf:
group.print_cmd('2.35')
assert buf.getvalue() == "0.35\n2.0\n0.5875\n2\n"
+
+ def test_ctrl_c(self):
+ localdir = os.path.dirname(__file__)
+ args = [sys.executable, os.path.join(localdir, 'ctrl_c.py'),
+ '\x7f'.join(sys.path),
+ str(self.exename), self.rdbname]
+ t1 = time.time()
+ result = subprocess.check_output(args)
+ t2 = time.time()
+ print 'subprocess returned with captured stdout:\n%r' % (result,)
+ assert result == 'all ok\n'
+ # should take two times ~0.8 seconds if correctly interrupted
+ assert t2 - t1 < 3.0
More information about the pypy-commit
mailing list