Author: Armin Rigo <[email protected]>
Branch: reverse-debugger
Changeset: r86802:799bea7b4905
Date: 2016-09-01 00:31 +0100
http://bitbucket.org/pypy/pypy/changeset/799bea7b4905/

Log:    fixes fixes fixes

        now after "b FILENAME:LINENO", will break whenever we reach the
        LINENO in a code object with co_filename == (optional
        prefix/)FILENAME. Should all be done efficiently enough with
        multiple levels of caching.

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
@@ -1,5 +1,5 @@
 import sys
-from rpython.rlib import revdb, rpath, rstring
+from rpython.rlib import revdb
 from rpython.rlib.debug import make_sure_not_resized
 from rpython.rlib.objectmodel import specialize, we_are_translated
 from rpython.rtyper.annlowlevel import cast_gcref_to_instance
@@ -15,6 +15,7 @@
     breakpoint_stack_id = 0
     breakpoint_funcnames = None
     breakpoint_filelines = None
+    breakpoint_by_file = None
     breakpoint_version = 0
     printed_objects = {}
     metavars = []
@@ -131,7 +132,7 @@
             call_stop_point_at_line = False
     #
     if call_stop_point_at_line:
-        if dbstate.breakpoint_filelines is not None:
+        if dbstate.breakpoint_by_file is not None:
             check_and_trigger_bkpt(frame.pycode, cur)
         stop_point_activate()
         cur += 1
@@ -214,6 +215,7 @@
                 lastlineno = lineno
             addr += byte_incr
         lineno += line_incr
+        p += 2
     if lineno != lastlineno:
         result.append((addr, lineno))
     return result
@@ -545,6 +547,8 @@
     # mapping {opindex: bkpt_num}.  This cache is updated when the
     # version in 'pycode.co_revdb_bkpt_version' does not match
     # 'dbstate.breakpoint_version' any more.
+    #
+    # IMPORTANT: no object allocation here, outside update_bkpt_cache!
     if pycode.co_revdb_bkpt_version != dbstate.breakpoint_version:
         update_bkpt_cache(pycode)
     cache = pycode.co_revdb_bkpt_cache
@@ -552,33 +556,46 @@
         revdb.breakpoint(cache[opindex])
 
 def update_bkpt_cache(pycode):
-    # dbstate.breakpoint_filelines == {'normfilename': {lineno: bkpt_num}}
+    # initialized by command_breakpoints():
+    #     dbstate.breakpoint_filelines == [('FILENAME', lineno, bkpt_num)]
+    # computed lazily (here, first half of the logic):
+    #     dbstate.breakpoint_by_file == {'co_filename': {lineno: bkpt_num}}
+    # the goal is to set:
+    #     pycode.co_revdb_bkpt_cache == {opindex: bkpt_num}
+    #
+    prev_state = revdb.watch_save_state(force=True)
+    # ^^^ the object allocations done in this function should not count!
+
     co_filename = pycode.co_filename
     try:
-        linenos = dbstate.breakpoint_filelines[co_filename]
+        linenos = dbstate.breakpoint_by_file[co_filename]
     except KeyError:
-        # normalize co_filename, and assigns the {lineno: bkpt_num} dict
-        # back over the original key, to avoid calling rabspath/rnormpath
-        # again the next time
-        co_filename = rstring.assert_str0(co_filename)
-        normfilename = rpath.rabspath(co_filename)
-        normfilename = rpath.rnormpath(normfilename)
-        linenos = dbstate.breakpoint_filelines.get(normfilename, None)
-        dbstate.breakpoint_filelines[co_filename] = linenos
-    #
+        linenos = None
+        match = co_filename.upper()    # ignore cAsE in filename matching
+        for filename, lineno, bkpt_num in dbstate.breakpoint_filelines:
+            if match.endswith(filename) and (
+                    len(match) == len(filename) or
+                    match[-len(filename)-1] in '/\\'):    # a valid prefix
+                if linenos is None:
+                    linenos = {}
+                linenos[lineno] = bkpt_num
+        dbstate.breakpoint_by_file[co_filename] = linenos
+
     newcache = None
     if linenos is not None:
         # parse co_lnotab to figure out the opindexes that correspond
-        # to the marked line numbers.
+        # to the marked line numbers.  here, linenos == {lineno: bkpt_num}
         for addr, lineno in find_line_starts(pycode):
             if lineno in linenos:
                 if newcache is None:
                     newcache = {}
                 newcache[addr] = linenos[lineno]
-    #
+
     pycode.co_revdb_bkpt_cache = newcache
     pycode.co_revdb_bkpt_version = dbstate.breakpoint_version
 
+    revdb.watch_restore_state(prev_state)
+
 
 def valid_identifier(s):
     if not s:
@@ -596,10 +613,14 @@
     dbstate.breakpoint_funcnames[name] = i
 
 def add_breakpoint_fileline(filename, lineno, i):
+    # dbstate.breakpoint_filelines is just a list of (FILENAME, lineno, i).
+    # dbstate.breakpoint_by_file is {co_filename: {lineno: i}}, but
+    # computed lazily when we encounter a code object with the given
+    # co_filename.  Any suffix 'filename' matches 'co_filename'.
     if dbstate.breakpoint_filelines is None:
-        dbstate.breakpoint_filelines = {}
-    linenos = dbstate.breakpoint_filelines.setdefault(filename, {})
-    linenos[lineno] = i
+        dbstate.breakpoint_filelines = []
+        dbstate.breakpoint_by_file = {}
+    dbstate.breakpoint_filelines.append((filename.upper(), lineno, i))
 
 def add_breakpoint(name, i):
     # if it is empty, complain
@@ -654,10 +675,6 @@
         revdb.send_output(
             'Note: "%s" doesn''t look like a Python filename. '
             'Setting breakpoint anyway\n' % (filename,))
-    elif '\x00' not in filename:
-        filename = rstring.assert_str0(filename)
-        filename = rpath.rabspath(filename)
-        filename = rpath.rnormpath(filename)
 
     add_breakpoint_fileline(filename, lineno, i)
     name = '%s:%d' % (filename, lineno)
@@ -670,6 +687,7 @@
     revdb.set_thread_breakpoint(cmd.c_arg2)
     dbstate.breakpoint_funcnames = None
     dbstate.breakpoint_filelines = None
+    dbstate.breakpoint_by_file = None
     dbstate.breakpoint_version += 1
     watch_progs = []
     with non_standard_code:
diff --git a/pypy/interpreter/test/test_reverse_debugging.py 
b/pypy/interpreter/test/test_reverse_debugging.py
--- a/pypy/interpreter/test/test_reverse_debugging.py
+++ b/pypy/interpreter/test/test_reverse_debugging.py
@@ -1,7 +1,7 @@
 import dis
 from pypy.interpreter.reverse_debugging import *
 from pypy.interpreter import reverse_debugging
-from rpython.rlib import revdb, rpath
+from rpython.rlib import revdb
 from hypothesis import given, strategies, example
 
 
@@ -73,7 +73,7 @@
         assert dbstate.breakpoint_filelines is None
     else:
         filename, lineno = expected_fileline
-        assert dbstate.breakpoint_filelines == {filename: {lineno: 5}}
+        assert dbstate.breakpoint_filelines == [(filename.upper(), lineno, 5)]
 
     got_output = None
     got_chbkpt = None
@@ -89,9 +89,6 @@
     assert got_output == expected_output
     assert got_chbkpt == expected_chbkpt
 
-def fullpath(path):
-    return rpath.rnormpath(rpath.rabspath(path))
-
 def test_add_breakpoint():
     check_add_breakpoint('', expected_output="Empty breakpoint name\n",
                          expected_chbkpt='')
@@ -109,12 +106,8 @@
     check_add_breakpoint('abcd:42', expected_fileline=('abcd', 42),
         expected_output='Note: "abcd" doesnt look like a Python filename.'
                         ' Setting breakpoint anyway\n')
-    full = fullpath('abcd.py')
     check_add_breakpoint('abcd.py:42',
-                         expected_fileline=(full, 42),
-                         expected_chbkpt='%s:42' % full)
-    check_add_breakpoint('%s:42' % full,
-                         expected_fileline=(full, 42))
+                         expected_fileline=('abcd.py', 42))
     check_add_breakpoint('42:abc',
         expected_output='"42:abc": expected a line number after colon\n',
         expected_chbkpt='')
diff --git a/rpython/rlib/revdb.py b/rpython/rlib/revdb.py
--- a/rpython/rlib/revdb.py
+++ b/rpython/rlib/revdb.py
@@ -132,8 +132,8 @@
     ll_callback = llhelper(_CALLBACK_GCREF_FNPTR, callback)
     llop.revdb_track_object(lltype.Void, unique_id, ll_callback)
 
-def watch_save_state():
-    return llop.revdb_watch_save_state(lltype.Bool)
+def watch_save_state(force=False):
+    return llop.revdb_watch_save_state(lltype.Bool, force)
 
 def watch_restore_state(any_watch_point):
     llop.revdb_watch_restore_state(lltype.Void, any_watch_point)
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
@@ -51,6 +51,7 @@
                 print 'KeyboardInterrupt: restoring state at time %d...' % (
                     rtime,)
                 self.pgroup.recreate_subprocess(rtime)
+                print "(type 'q' or Ctrl-D to quit)"
                 self.last_command = ''
                 self.previous_thread = '?'
                 self.previous_time = '?'
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
@@ -205,9 +205,9 @@
 #define OP_REVDB_TRACK_OBJECT(uid, callback, r)                         \
     rpy_reverse_db_track_object(uid, callback)
 
-#define OP_REVDB_WATCH_SAVE_STATE(r)   do {                             \
+#define OP_REVDB_WATCH_SAVE_STATE(force, r)   do {                      \
         r = rpy_revdb.watch_enabled;                                    \
-        if (r) rpy_reverse_db_watch_save_state();                       \
+        if ((force) || r) rpy_reverse_db_watch_save_state();            \
     } while (0)
 
 #define OP_REVDB_WATCH_RESTORE_STATE(any_watch_point, r)                \
_______________________________________________
pypy-commit mailing list
[email protected]
https://mail.python.org/mailman/listinfo/pypy-commit

Reply via email to