Bug 1559461 - gdb unwinder: Lookup JIT memory using ::execMemory. r=jandem
authorNicolas B. Pierron <nicolas.b.pierron@nbp.name>
Tue, 25 Jun 2019 16:39:31 +0000
changeset 542888 db91fecd39888a822876fd6cc52989729e790d10
parent 542887 74bb6796032db5cc24fde251753cddcc9726558b
child 542889 16b817a562f3e31d51e3a9e4838fb4331de9651f
push id2131
push userffxbld-merge
push dateMon, 26 Aug 2019 18:30:20 +0000
treeherdermozilla-release@b19ffb3ca153 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjandem
bugs1559461
milestone69.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1559461 - gdb unwinder: Lookup JIT memory using ::execMemory. r=jandem Differential Revision: https://phabricator.services.mozilla.com/D35062
js/src/gdb/mozilla/unwind.py
--- a/js/src/gdb/mozilla/unwind.py
+++ b/js/src/gdb/mozilla/unwind.py
@@ -1,16 +1,14 @@
 # mozilla/unwind.py --- unwinder and frame filter for SpiderMonkey
 
 import gdb
 import gdb.types
 from gdb.FrameDecorator import FrameDecorator
-import re
 import platform
-from mozilla.ExecutableAllocator import jsjitExecutableAllocatorCache, jsjitExecutableAllocator
 
 # For ease of use in Python 2, we use "long" instead of "int"
 # everywhere.
 try:
     long
 except NameError:
     long = int
 
@@ -97,17 +95,16 @@ class UnwinderTypeCache(object):
 
         self.compute_frame_info()
         commonFrameLayout = gdb.lookup_type('js::jit::CommonFrameLayout')
         self.d['typeCommonFrameLayout'] = commonFrameLayout
         self.d['typeCommonFrameLayoutPointer'] = commonFrameLayout.pointer()
         self.d['per_tls_context'] = gdb.lookup_global_symbol('js::TlsContext')
 
         self.d['void_starstar'] = gdb.lookup_type('void').pointer().pointer()
-        self.d['mod_ExecutableAllocator'] = jsjitExecutableAllocatorCache()
 
         jitframe = gdb.lookup_type("js::jit::JitFrameLayout")
         self.d['jitFrameLayoutPointer'] = jitframe.pointer()
 
         self.d['CalleeToken_Function'] = self.jit_value("CalleeToken_Function")
         self.d['CalleeToken_FunctionConstructing'] = self.jit_value(
             "CalleeToken_FunctionConstructing")
         self.d['CalleeToken_Script'] = self.jit_value("CalleeToken_Script")
@@ -115,56 +112,34 @@ class UnwinderTypeCache(object):
         self.d['JSScript'] = gdb.lookup_type("JSScript").pointer()
         self.d['Value'] = gdb.lookup_type("JS::Value")
 
         self.d['SOURCE_SLOT'] = self.value('js::ScriptSourceObject::SOURCE_SLOT')
         self.d['NativeObject'] = gdb.lookup_type("js::NativeObject").pointer()
         self.d['HeapSlot'] = gdb.lookup_type("js::HeapSlot").pointer()
         self.d['ScriptSource'] = gdb.lookup_type("js::ScriptSource").pointer()
 
+        # ProcessExecutableMemory, used to identify if a pc is in the section
+        # pre-allocated by the JIT.
+        self.d['MaxCodeBytesPerProcess'] = self.jit_value('MaxCodeBytesPerProcess')
+        self.d['execMemory'] = gdb.lookup_symbol('::execMemory')[0].value()
+
     # Compute maps related to jit frames.
     def compute_frame_info(self):
         t = gdb.lookup_type('enum js::jit::FrameType')
         for field in t.fields():
             # Strip off "js::jit::", remains: "FrameType::*".
             name = field.name[9:]
             enumval = long(field.enumval)
             self.d[name] = enumval
             self.frame_enum_names[enumval] = name
             class_type = gdb.lookup_type('js::jit::' + SizeOfFramePrefix[name])
             self.frame_class_types[enumval] = class_type.pointer()
 
 
-def parse_proc_maps():
-    # gdb doesn't have a direct way to tell us if a given address is
-    # claimed by some shared library or the executable.  See
-    # https://sourceware.org/bugzilla/show_bug.cgi?id=19288
-    # In the interest of not requiring a patched gdb, instead we read
-    # /proc/.../maps.  This only works locally, but maybe could work
-    # remotely using "remote get".  FIXME.
-    mapfile = '/proc/' + str(gdb.selected_inferior().pid) + '/maps'
-    # Note we only examine executable mappings here.
-    matcher = re.compile("^([a-fA-F0-9]+)-([a-fA-F0-9]+)\s+..x.\s+\S+\s+\S+\s+\S*(.*)$")
-    mappings = []
-    with open(mapfile, "r") as inp:
-        for line in inp:
-            match = matcher.match(line)
-            if not match:
-                # Header lines and such.
-                continue
-            start = match.group(1)
-            end = match.group(2)
-            name = match.group(3).strip()
-            if name is '' or (name.startswith('[') and name is not '[vdso]'):
-                # Skip entries not corresponding to a file.
-                continue
-            mappings.append((long(start, 16), long(end, 16)))
-    return mappings
-
-
 class FrameSymbol(object):
     "A symbol/value pair as expected from gdb frame decorators."
 
     def __init__(self, sym, val):
         self.sym = sym
         self.val = val
 
     def symbol(self):
@@ -324,21 +299,16 @@ class UnwinderState(object):
     def __init__(self, typecache):
         self.next_sp = None
         self.next_type = None
         self.activation = None
         # An unwinder instance is specific to a thread.  Record the
         # selected thread for later verification.
         self.thread = gdb.selected_thread()
         self.frame_map = {}
-        self.proc_mappings = None
-        try:
-            self.proc_mappings = parse_proc_maps()
-        except IOError:
-            pass
         self.typecache = typecache
 
     # If the given gdb.Frame was created by this unwinder, return the
     # corresponding informational dictionary for the frame.
     # Otherwise, return None.  This is used by the frame filter to
     # display extra information about the frame.
     def get_frame(self, frame):
         sp = long(frame.read_register(self.SP_REGISTER))
@@ -347,46 +317,28 @@ class UnwinderState(object):
         return None
 
     # Add information about a frame to the frame map.  This map is
     # queried by |self.get_frame|.  |sp| is the frame's stack pointer,
     # and |name| the frame's type as a string, e.g. "FrameType::Exit".
     def add_frame(self, sp, name=None, this_frame=None):
         self.frame_map[long(sp)] = {"name": name, "this_frame": this_frame}
 
-    # See whether |pc| is claimed by some text mapping.  See
-    # |parse_proc_maps| for details on how the decision is made.
-    def text_address_claimed(self, pc):
-        for (start, end) in self.proc_mappings:
-            if (pc >= start and pc <= end):
-                return True
-        return False
-
     # See whether |pc| is claimed by the Jit.
     def is_jit_address(self, pc):
-        if self.proc_mappings is not None:
-            return not self.text_address_claimed(pc)
+        execMem = self.typecache.execMemory
+        base = long(execMem['base_'])
+        length = self.typecache.MaxCodeBytesPerProcess
 
-        cx = self.get_tls_context()
-        runtime = cx['runtime_']['value']
-        if long(runtime.address) == 0:
+        # If the base pointer is null, then no memory got allocated yet.
+        if long(base) == 0:
             return False
 
-        jitRuntime = runtime['jitRuntime_']
-        if long(jitRuntime.address) == 0:
-            return False
-
-        execAllocators = [jitRuntime['execAlloc_'], jitRuntime['backedgeExecAlloc_']]
-        for execAlloc in execAllocators:
-            for pool in jsjitExecutableAllocator(execAlloc, self.typecache):
-                pages = pool['m_allocation']['pages']
-                size = pool['m_allocation']['size']
-                if pages <= pc and pc < pages + size:
-                    return True
-        return False
+        # If allocated, then we allocated MaxCodeBytesPerProcess.
+        return base <= pc and pc < base + length
 
     # Check whether |self| is valid for the selected thread.
     def check(self):
         return gdb.selected_thread() is self.thread
 
     # Essentially js::TlsContext.get().
     def get_tls_context(self):
         return self.typecache.per_tls_context.value()['mValue']