[pypy-commit] pypy vmprof-native: remove allocated libunwind info for dynamic code regions as soon as the loop token goes out of scope

plan_rich pypy.commits at gmail.com
Sat Feb 11 10:58:40 EST 2017


Author: Richard Plangger <planrichi at gmail.com>
Branch: vmprof-native
Changeset: r90050:fd79187556df
Date: 2017-02-11 16:58 +0100
http://bitbucket.org/pypy/pypy/changeset/fd79187556df/

Log:	remove allocated libunwind info for dynamic code regions as soon as
	the loop token goes out of scope

diff --git a/rpython/jit/backend/x86/assembler.py b/rpython/jit/backend/x86/assembler.py
--- a/rpython/jit/backend/x86/assembler.py
+++ b/rpython/jit/backend/x86/assembler.py
@@ -491,7 +491,6 @@
         if not we_are_translated():
             # Arguments should be unique
             assert len(set(inputargs)) == len(inputargs)
-
         self.setup(looptoken)
         if self.cpu.HAS_CODEMAP:
             self.codemap_builder.enter_portal_frame(jd_id, unique_id,
diff --git a/rpython/jit/metainterp/compile.py b/rpython/jit/metainterp/compile.py
--- a/rpython/jit/metainterp/compile.py
+++ b/rpython/jit/metainterp/compile.py
@@ -160,9 +160,10 @@
     jitcell_token.outermost_jitdriver_sd = jitdriver_sd
     return jitcell_token
 
-def record_loop_or_bridge(metainterp_sd, loop):
+def record_loop_or_bridge(metainterp_sd, loop, asminfo):
     """Do post-backend recordings and cleanups on 'loop'.
     """
+    from rpython.rlib.rvmprof import rvmprof
     # get the original jitcell token corresponding to jitcell form which
     # this trace starts
     original_jitcell_token = loop.original_jitcell_token
@@ -172,6 +173,11 @@
     wref = weakref.ref(original_jitcell_token)
     clt = original_jitcell_token.compiled_loop_token
     clt.loop_token_wref = wref
+
+    rvmprof.dyn_register_jit_page(original_jitcell_token, asminfo.asmaddr,
+                                  asminfo.asmaddr+asminfo.asmlen)
+
+
     for op in loop.operations:
         descr = op.getdescr()
         # not sure what descr.index is about
@@ -242,9 +248,9 @@
     if not we_are_translated():
         loop.check_consistency()
     jitcell_token.target_tokens = [target_token]
-    send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop, "loop",
-                         runtime_args, metainterp.box_names_memo)
-    record_loop_or_bridge(metainterp_sd, loop)
+    asminfo = send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd,
+                    loop, "loop", runtime_args, metainterp.box_names_memo)
+    record_loop_or_bridge(metainterp_sd, loop, asminfo)
     return target_token
 
 def compile_loop(metainterp, greenkey, start, inputargs, jumpargs,
@@ -337,9 +343,9 @@
                        loop_info.extra_before_label + [loop_info.label_op] + loop_ops)
     if not we_are_translated():
         loop.check_consistency()
-    send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd, loop, "loop",
-                         inputargs, metainterp.box_names_memo)
-    record_loop_or_bridge(metainterp_sd, loop)
+    asminfo = send_loop_to_backend(greenkey, jitdriver_sd, metainterp_sd,
+                    loop, "loop", inputargs, metainterp.box_names_memo)
+    record_loop_or_bridge(metainterp_sd, loop, asminfo)
     loop_info.post_loop_compilation(loop, jitdriver_sd, metainterp, jitcell_token)
     return start_descr
 
@@ -414,9 +420,9 @@
         loop.quasi_immutable_deps = quasi_immutable_deps
 
     target_token = loop.operations[-1].getdescr()
-    resumekey.compile_and_attach(metainterp, loop, inputargs)
+    asminfo = resumekey.compile_and_attach(metainterp, loop, inputargs)
 
-    record_loop_or_bridge(metainterp_sd, loop)
+    record_loop_or_bridge(metainterp_sd, loop, asminfo)
     return target_token
 
 def get_box_replacement(op, allow_none=False):
@@ -506,11 +512,6 @@
                                           jd_id=jd_id, unique_id=unique_id,
                                           log=log, name=name, logger=metainterp_sd.jitlog)
 
-    vmprof = metainterp_sd.vmprof
-    if vmprof:
-        vmprof.dyn_register_jit_page(asminfo.asmaddr,
-                                     asminfo.asmaddr+asminfo.asmlen, 1)
-
     return asminfo
 
 def do_compile_bridge(metainterp_sd, faildescr, inputargs, operations,
@@ -524,10 +525,6 @@
     asminfo = metainterp_sd.cpu.compile_bridge(faildescr, inputargs, operations,
                                             original_loop_token, log=log,
                                             logger=metainterp_sd.jitlog)
-    vmprof = metainterp_sd.vmprof
-    if vmprof:
-        vmprof.dyn_register_jit_page(asminfo.asmaddr,
-                                     asminfo.asmaddr+asminfo.asmlen, 0)
     return asminfo
 
 def forget_optimization_info(lst, reset_values=False):
@@ -598,6 +595,8 @@
     if metainterp_sd.warmrunnerdesc is not None:    # for tests
         metainterp_sd.warmrunnerdesc.memory_manager.keep_loop_alive(original_jitcell_token)
 
+    return asminfo
+
 def send_bridge_to_backend(jitdriver_sd, metainterp_sd, faildescr, inputargs,
                            operations, original_loop_token, memo):
     forget_optimization_info(operations)
@@ -843,10 +842,10 @@
             self._debug_subinputargs = new_loop.inputargs
             self._debug_suboperations = new_loop.operations
         propagate_original_jitcell_token(new_loop)
-        send_bridge_to_backend(metainterp.jitdriver_sd, metainterp.staticdata,
-                               self, inputargs, new_loop.operations,
-                               new_loop.original_jitcell_token,
-                               metainterp.box_names_memo)
+        return send_bridge_to_backend(metainterp.jitdriver_sd,
+                    metainterp.staticdata, self, inputargs,
+                    new_loop.operations, new_loop.original_jitcell_token,
+                    metainterp.box_names_memo)
 
     def make_a_counter_per_value(self, guard_value_op, index):
         assert guard_value_op.getopnum() == rop.GUARD_VALUE
@@ -1047,13 +1046,14 @@
         jitdriver_sd = metainterp.jitdriver_sd
         new_loop.original_jitcell_token = jitcell_token = make_jitcell_token(jitdriver_sd)
         propagate_original_jitcell_token(new_loop)
-        send_loop_to_backend(self.original_greenkey, metainterp.jitdriver_sd,
-                             metainterp_sd, new_loop, "entry bridge",
-                             orig_inputargs, metainterp.box_names_memo)
+        asminfo = send_loop_to_backend(self.original_greenkey,
+                     metainterp.jitdriver_sd, metainterp_sd, new_loop,
+                     "entry bridge", orig_inputargs, metainterp.box_names_memo)
         # send the new_loop to warmspot.py, to be called directly the next time
         jitdriver_sd.warmstate.attach_procedure_to_interp(
             self.original_greenkey, jitcell_token)
         metainterp_sd.stats.add_jitcell_token(jitcell_token)
+        return asminfo
 
 
 def compile_trace(metainterp, resumekey, runtime_boxes):
@@ -1115,8 +1115,8 @@
     if info.final():
         new_trace.inputargs = info.inputargs
         target_token = new_trace.operations[-1].getdescr()
-        resumekey.compile_and_attach(metainterp, new_trace, inputargs)
-        record_loop_or_bridge(metainterp_sd, new_trace)
+        asminfo = resumekey.compile_and_attach(metainterp, new_trace, inputargs)
+        record_loop_or_bridge(metainterp_sd, new_trace, asminfo)
         return target_token
     new_trace.inputargs = info.renamed_inputargs
     metainterp.retrace_needed(new_trace, info)
diff --git a/rpython/jit/metainterp/history.py b/rpython/jit/metainterp/history.py
--- a/rpython/jit/metainterp/history.py
+++ b/rpython/jit/metainterp/history.py
@@ -419,6 +419,14 @@
     def __init__(self):
         # For memory management of assembled loops
         self._keepalive_jitcell_tokens = {}      # set of other JitCellToken
+        self._rvmprof_references = []
+
+    def rvmprof_register(self, ref):
+        """ Call this method for every loop or bridge that hangs on this
+            token. Otherwise the information tracked by libunwind will
+            not be freed.
+        """
+        self._rvmprof_references.append(ref)
 
     def record_jump_to(self, jitcell_token):
         assert isinstance(jitcell_token, JitCellToken)
diff --git a/rpython/jit/metainterp/memmgr.py b/rpython/jit/metainterp/memmgr.py
--- a/rpython/jit/metainterp/memmgr.py
+++ b/rpython/jit/metainterp/memmgr.py
@@ -2,6 +2,7 @@
 from rpython.rlib.rarithmetic import r_int64
 from rpython.rlib.debug import debug_start, debug_print, debug_stop
 from rpython.rlib.objectmodel import we_are_translated
+from rpython.rlib.rvmprof import rvmprof
 
 #
 # Logic to decide which loops are old and not used any more.
@@ -70,6 +71,7 @@
         for looptoken in self.alive_loops.keys():
             if (0 <= looptoken.generation < max_generation or
                 looptoken.invalidated):
+                rvmprof.vmp_dyn_cancel(looptoken)
                 del self.alive_loops[looptoken]
         newtotal = len(self.alive_loops)
         debug_print("Loop tokens freed: ", oldtotal - newtotal)
diff --git a/rpython/rlib/rvmprof/cintf.py b/rpython/rlib/rvmprof/cintf.py
--- a/rpython/rlib/rvmprof/cintf.py
+++ b/rpython/rlib/rvmprof/cintf.py
@@ -54,6 +54,7 @@
         SHARED.join('machine.c'),
         SHARED.join('symboltable.c'),
         SHARED.join('vmp_stack.c'),
+        SHARED.join('vmp_dynamic.c'),
         # udis86
         SHARED.join('libudis86/decode.c'),
         SHARED.join('libudis86/itab.c'),
@@ -96,6 +97,19 @@
                                   lltype.Signed, compilation_info=eci,
                                   _nowrapper=True)
 
+    vmp_dyn_register_jit_page = rffi.llexternal("vmp_dyn_register_jit_page",
+                                  [lltype.Signed, lltype.Signed, rffi.CCHARP],
+                                  rffi.INT, compilation_info=eci,
+                                  _nowrapper=True)
+
+    vmp_dyn_cancel = rffi.llexternal("vmp_dyn_cancel", [rffi.INT],
+                                  lltype.Void, compilation_info=eci,
+                                  _nowrapper=True)
+
+    vmp_dyn_cancel = rffi.llexternal("vmp_dyn_teardown", [lltype.Void],
+                                  rffi.INT, compilation_info=eci,
+                                  _nowrapper=True)
+
     return CInterface(locals())
 
 
diff --git a/rpython/rlib/rvmprof/rvmprof.py b/rpython/rlib/rvmprof/rvmprof.py
--- a/rpython/rlib/rvmprof/rvmprof.py
+++ b/rpython/rlib/rvmprof/rvmprof.py
@@ -161,16 +161,6 @@
             raise VMProfError(os.strerror(rposix.get_saved_errno()))
 
 
-    def dyn_register_jit_page(self, addr, end_addr, loop, name=None):
-        if name is None:
-            cname = rffi.cast(rffi.CHARP, 0)
-        else:
-            cname = rffi.str2charp(name)
-        return self.cintf.vmp_dyn_register_jit_page(addr, end_addr, cname)
-
-    def dyn_cancel(self, ref):
-        self.cintf.vmp_dyn_cancel(ref)
-
     def _write_code_registration(self, uid, name):
         assert name.count(':') == 3 and len(name) <= MAX_FUNC_NAME, (
             "the name must be 'class:func_name:func_line:filename' "
@@ -257,6 +247,26 @@
 
     return decorate
 
+def dyn_register_jit_page(token, addr, end_addr):
+    try:
+        c = _get_vmprof().cintf
+        cname = lltype.nullptr(rffi.CCHARP.TO)
+        ref = c.vmp_dyn_register_jit_page(addr, end_addr, cname)
+        token.rvmprof_register(ref)
+        return True
+    except cintf.VMProfPlatformUnsupported:
+        return False
+
+def dyn_cancel(token):
+    try:
+        c = _get_vmprof().cintf
+        for ref in token._rvmprof_references:
+            c.vmp_dyn_cancel(ref)
+        token._rvmprof_references = []
+        return True
+    except cintf.VMProfPlatformUnsupported:
+        return False
+
 @specialize.memo()
 def _was_registered(CodeClass):
     return hasattr(CodeClass, '_vmprof_unique_id')
diff --git a/rpython/rlib/rvmprof/src/shared/vmp_dynamic.c b/rpython/rlib/rvmprof/src/shared/vmp_dynamic.c
--- a/rpython/rlib/rvmprof/src/shared/vmp_dynamic.c
+++ b/rpython/rlib/rvmprof/src/shared/vmp_dynamic.c
@@ -9,8 +9,17 @@
 static int g_dyn_entry_count_max = 0;
 static unw_dyn_info_t ** g_dyn_entries = 0;
 
+RPY_EXTERN
 int vmp_dyn_teardown(void)
 {
+    int i;
+    for (i = 0; i < g_dyn_entry_count; i++) {
+        unw_dyn_info_t * u = g_dyn_entries[i];
+        if (u != NULL) {
+            free(u);
+            g_dyn_entries[i] = NULL;
+        }
+    }
     if (g_dyn_entries != NULL) {
         free(g_dyn_entries);
     }
@@ -30,7 +39,7 @@
         g_dyn_entry_count_max *= 2;
         g_dyn_entries = (unw_dyn_info_t**)realloc(g_dyn_entries, sizeof(unw_dyn_info_t*) * g_dyn_entry_count_max);
         memset(g_dyn_entries + g_dyn_entry_count, 0,
-               sizeof(unw_dyn_info_t*)*g_dyn_entry_count_max - g_dyn_entry_count);
+               sizeof(unw_dyn_info_t*)*(g_dyn_entry_count_max - g_dyn_entry_count));
     }
 }
 
@@ -72,6 +81,7 @@
     free(u);
 }
 
+RPY_EXTERN
 int vmp_dyn_register_jit_page(intptr_t addr, intptr_t end_addr,
                               const char * name)
 {
@@ -98,6 +108,7 @@
     return ref;
 }
 
+RPY_EXTERN
 int vmp_dyn_cancel(int ref) {
     unw_dyn_info_t * u;
 
diff --git a/rpython/rlib/rvmprof/src/shared/vmp_dynamic.h b/rpython/rlib/rvmprof/src/shared/vmp_dynamic.h
--- a/rpython/rlib/rvmprof/src/shared/vmp_dynamic.h
+++ b/rpython/rlib/rvmprof/src/shared/vmp_dynamic.h
@@ -3,8 +3,10 @@
 #include <stdint.h>
 #include <libunwind.h>
 
-int vmp_dyn_register_jit_page(intptr_t addr, intptr_t end_addr,
+#include "rvmprof.h"
+
+RPY_EXTERN int vmp_dyn_register_jit_page(intptr_t addr, intptr_t end_addr,
                               const char * name);
-int vmp_dyn_cancel(int ref);
-int vmp_dyn_teardown(void);
+RPY_EXTERN int vmp_dyn_cancel(int ref);
+RPY_EXTERN int vmp_dyn_teardown(void);
 
diff --git a/rpython/rlib/rvmprof/test/test_dynamic.py b/rpython/rlib/rvmprof/test/test_dynamic.py
--- a/rpython/rlib/rvmprof/test/test_dynamic.py
+++ b/rpython/rlib/rvmprof/test/test_dynamic.py
@@ -16,11 +16,12 @@
         ffi.cdef("""
         int vmp_dyn_register_jit_page(intptr_t addr, intptr_t end_addr, const char * name);
         int vmp_dyn_cancel(int ref);
+        int vmp_dyn_teardown(void);
         """)
 
         with open(str(srcdir.join("shared/vmp_dynamic.c"))) as fd:
             ffi.set_source("rpython.rlib.rvmprof.test._test_dynamic", fd.read(),
-                    include_dirs=[str(srcdir.join('shared'))],
+                    include_dirs=[str(srcdir), str(srcdir.join('shared'))],
                     libraries=['unwind'])
 
         ffi.compile(verbose=True)
@@ -44,4 +45,28 @@
         lib.vmp_dyn_cancel(0)
         lib.vmp_dyn_cancel(1)
 
+    def test_register_dynamic_code_many(self):
+        lib = self.lib
+        ffi = self.ffi
 
+        refs = []
+        for i in range(5000):
+            ref = lib.vmp_dyn_register_jit_page(0x100*i, 0x200*i, ffi.NULL)
+            refs.append(ref)
+
+        for i in range(1000, 4000):
+            ref = refs[i]
+            lib.vmp_dyn_cancel(ref)
+
+        refs = refs[:1000] + refs[4000:]
+
+        for i in range(5000):
+            ref = lib.vmp_dyn_register_jit_page(0x100*i, 0x200*i, ffi.NULL)
+            refs.append(ref)
+
+        while refs:
+            ref = refs.pop()
+            lib.vmp_dyn_cancel(ref)
+
+        lib.vmp_dyn_teardown()
+


More information about the pypy-commit mailing list