Author: Armin Rigo <ar...@tunes.org>
Branch: reverse-debugger
Changeset: r86162:9b771a2cb860
Date: 2016-08-11 22:15 +0200
http://bitbucket.org/pypy/pypy/changeset/9b771a2cb860/

Log:    Add commands 'nthread' and 'bthread' to navigate thread switches.

diff --git a/pypy/interpreter/reverse_debugging.py 
b/pypy/interpreter/reverse_debugging.py
--- a/pypy/interpreter/reverse_debugging.py
+++ b/pypy/interpreter/reverse_debugging.py
@@ -479,6 +479,7 @@
 def command_breakpoints(cmd, extra):
     space = dbstate.space
     dbstate.breakpoint_stack_id = cmd.c_arg1
+    revdb.set_thread_breakpoint(cmd.c_arg2)
     funcnames = None
     watch_progs = []
     with non_standard_code:
diff --git a/rpython/rlib/revdb.py b/rpython/rlib/revdb.py
--- a/rpython/rlib/revdb.py
+++ b/rpython/rlib/revdb.py
@@ -92,6 +92,9 @@
 def breakpoint(num):
     llop.revdb_breakpoint(lltype.Void, num)
 
+def set_thread_breakpoint(tnum):
+    llop.revdb_set_thread_breakpoint(lltype.Void, tnum)
+
 @specialize.argtype(0)
 def get_unique_id(x):
     """Returns the creation number of the object 'x'.  For objects created
diff --git a/rpython/rtyper/lltypesystem/lloperation.py 
b/rpython/rtyper/lltypesystem/lloperation.py
--- a/rpython/rtyper/lltypesystem/lloperation.py
+++ b/rpython/rtyper/lltypesystem/lloperation.py
@@ -583,6 +583,7 @@
     'revdb_weakref_create': LLOp(),
     'revdb_weakref_deref':  LLOp(),
     'revdb_call_destructor': LLOp(),
+    'revdb_set_thread_breakpoint': LLOp(),
 }
 # ***** Run test_lloperation after changes. *****
 
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
@@ -45,7 +45,7 @@
                 if self.pgroup.get_current_thread() != previous_thread:
                     previous_thread = self.pgroup.get_current_thread()
                     if previous_thread == 0:
-                        print ('-------------------- in the main thread '
+                        print ('-------------------- in main thread #0 '
                                '--------------------')
                     else:
                         print ('-------------------- in non-main thread '
@@ -147,6 +147,9 @@
         elif num == -3:
             kind = 'stoppoint'
             name = 'explicit stop'
+        elif num == -4:
+            kind = 'switchpoint'
+            name = 'thread switch'
         else:
             kind = '?????point'
             name = repr(break_at)
@@ -245,6 +248,17 @@
         finally:
             b.stack_id = 0
 
+    @contextmanager
+    def _thread_num_break(self, thread_num):
+        # add temporarily a breakpoint that hits when we enter/leave
+        # the given thread
+        b = self.pgroup.edit_breakpoints()
+        b.thread_num = thread_num
+        try:
+            yield
+        finally:
+            b.thread_num = -1
+
     def command_next(self, argument):
         """Run forward for one step, skipping calls"""
         while True:
@@ -308,7 +322,7 @@
         """Run forward until the current function finishes"""
         stack_id = self.pgroup.get_stack_id(is_parent=True)
         if stack_id == 0:
-            print 'No stack.'
+            print 'No caller.'
         else:
             with self._stack_id_break(stack_id):
                 self.command_continue('')
@@ -317,7 +331,7 @@
         """Run backward until the current function is called"""
         stack_id = self.pgroup.get_stack_id(is_parent=True)
         if stack_id == 0:
-            print 'No stack.'
+            print 'No caller.'
         else:
             with self._stack_id_break(stack_id):
                 self.command_bcontinue('')
@@ -333,6 +347,31 @@
         self.move_backward(self.pgroup.get_current_time() - 1)
     command_bc = command_bcontinue
 
+    def _cmd_thread(self, argument, cmd_continue):
+        argument = argument.lstrip('#')
+        if argument:
+            arg = int(argument)
+            if arg == self.pgroup.get_current_thread():
+                print 'Thread #%d is already the current one.' % (arg,)
+                return
+        else:
+            # use the current thread number to detect switches to any
+            # other thread (this works because revdb.c issues a
+            # breakpoint whenever there is a switch FROM or TO the
+            # thread '#arg').
+            arg = self.pgroup.get_current_thread()
+        #
+        with self._thread_num_break(arg):
+            cmd_continue('')
+
+    def command_nthread(self, argument):
+        """Run forward until thread switch (optionally to #ARG)"""
+        self._cmd_thread(argument, self.command_continue)
+
+    def command_bthread(self, argument):
+        """Run backward until thread switch (optionally to #ARG)"""
+        self._cmd_thread(argument, self.command_bcontinue)
+
     def command_print(self, argument):
         """Print an expression or execute a line of code"""
         # locate which $NUM appear used in the expression
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
@@ -31,15 +31,17 @@
         self.watchuids = {}    # {small number: [uid...]}
         self.stack_id = 0      # breaks when leaving/entering a frame from/to
                                # the frame identified by 'stack_id'
+        self.thread_num = -1   # breaks when leaving/entering the thread_num
 
     def __repr__(self):
-        return 'AllBreakpoints(%r, %r, %r, %r)' % (
+        return 'AllBreakpoints(%r, %r, %r, %r, %r)' % (
             self.num2break, self.watchvalues, self.watchuids,
-            self.stack_id)
+            self.stack_id, self.thread_num)
 
     def compare(self, other):
         if (self.num2break == other.num2break and
-            self.stack_id == other.stack_id):
+            self.stack_id == other.stack_id and
+            self.thread_num == other.thread_num):
             if self.watchvalues == other.watchvalues:
                 return 2     # completely equal
             else:
@@ -48,12 +50,14 @@
             return 0     # different
 
     def is_empty(self):
-        return len(self.num2break) == 0 and self.stack_id == 0
+        return (len(self.num2break) == 0 and self.stack_id == 0
+                                         and self.thread_num == -1)
 
     def duplicate(self):
         a = AllBreakpoints()
         a.num2break.update(self.num2break)
         a.stack_id = self.stack_id
+        a.thread_num = self.thread_num
         return a
 
 
@@ -392,8 +396,9 @@
         if cmp == 0:
             flat = [num2break.get(n, '\x00') for n in range(N)]
             arg1 = self.all_breakpoints.stack_id
+            arg2 = self.all_breakpoints.thread_num
             extra = ''.join(flat)
-            self.active.send(Message(CMD_BREAKPOINTS, arg1, extra=extra))
+            self.active.send(Message(CMD_BREAKPOINTS, arg1, arg2, extra=extra))
             self.active.expect_ready()
         else:
             assert cmp == 1
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
@@ -663,7 +663,7 @@
 static stacklet_thread_handle st_thread;
 static stacklet_handle st_outer_controller_h;
 static uint64_t current_thread_id, target_thread_id;
-static uint64_t current_thread_num, next_thread_num;
+static uint64_t current_thread_num, next_thread_num, break_thread_num;
 static void *thread_tree_root;
 
 
@@ -726,6 +726,13 @@
         return 1;
 }
 
+static void set_current_thread_num(uint64_t tnum)
+{
+    if (break_thread_num == current_thread_num || break_thread_num == tnum)
+        rpy_reverse_db_breakpoint(-4);
+    current_thread_num = tnum;
+}
+
 RPY_EXTERN
 int rpy_reverse_db_main(Signed entry_point(Signed, char**),
                         int argc, char **argv)
@@ -797,7 +804,7 @@
             item = tfind(&dummy, &thread_tree_root, compare_replay_thread);
             if (item == NULL) {
                 /* it's a new thread, start it now */
-                current_thread_num = next_thread_num++;
+                set_current_thread_num(next_thread_num++);
                 if (real_tloc != NULL)
                     memset(((char *)real_tloc) + RPY_TLOFSFIRST, 0,
                            sizeof(struct pypy_threadlocal_s) - RPY_TLOFSFIRST);
@@ -806,7 +813,7 @@
             else {
                 node = *item;
                 assert(node->tid == target_thread_id);
-                current_thread_num = node->tnum;
+                set_current_thread_num(node->tnum);
                 h = node->h;
                 tdelete(node, &thread_tree_root, compare_replay_thread);
                 if (real_tloc != NULL)
@@ -965,6 +972,7 @@
     current_thread_id = h.main_thread_id;
     current_thread_num = 0;
     next_thread_num = 1;
+    break_thread_num = (uint64_t)-1;
     if (h.ptr1 != &rpy_reverse_db_stop_point ||
         h.ptr2 != &rpy_revdb) {
         fprintf(stderr,
@@ -1713,6 +1721,12 @@
     exit(1);
 }
 
+RPY_EXTERN
+void rpy_reverse_db_set_thread_breakpoint(int64_t tnum)
+{
+    break_thread_num = (uint64_t)tnum;
+}
+
 
 /* ------------------------------------------------------------ */
 
diff --git a/rpython/translator/revdb/src-revdb/revdb_include.h 
b/rpython/translator/revdb/src-revdb/revdb_include.h
--- a/rpython/translator/revdb/src-revdb/revdb_include.h
+++ b/rpython/translator/revdb/src-revdb/revdb_include.h
@@ -227,6 +227,9 @@
    we'll just return the UID. */
 #define RPY_REVDB_CAST_PTR_TO_INT(obj)   (((struct pypy_header0 *)obj)->h_uid)
 
+#define OP_REVDB_SET_THREAD_BREAKPOINT(tnum, r)                         \
+    rpy_reverse_db_set_thread_breakpoint(tnum)
+
 
 RPY_EXTERN void rpy_reverse_db_flush(void);  /* must be called with the lock */
 RPY_EXTERN void rpy_reverse_db_fetch(const char *file, int line);
@@ -249,5 +252,6 @@
 RPY_EXTERN void rpy_reverse_db_callback_loc(int);
 RPY_EXTERN void rpy_reverse_db_lock_acquire(bool_t lock_contention);
 RPY_EXTERN void rpy_reverse_db_bad_acquire_gil(void);
+RPY_EXTERN void rpy_reverse_db_set_thread_breakpoint(int64_t tnum);
 
 /* ------------------------------------------------------------ */
_______________________________________________
pypy-commit mailing list
pypy-commit@python.org
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to