Bug 1559461 - gdb unwinder: Lookup JIT memory using ::execMemory. r=jandem
☠☠ backed out by c99ee72409e8 ☠ ☠
authorNicolas B. Pierron <nicolas.b.pierron@nbp.name>
Tue, 25 Jun 2019 13:56:57 +0000
changeset 480030 00b675dfb3dedafee6346876a311d252912a4d3a
parent 480029 d3c9483c4d79657ec4d0ba54ee102d15cb74f497
child 480031 e1415c18f96d3315f6aee121f686fb6312e8ed2c
push id36200
push useraiakab@mozilla.com
push dateTue, 25 Jun 2019 21:58:14 +0000
treeherdermozilla-central@b23bd78e4d94 [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
@@ -103,56 +103,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::".
             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):
@@ -312,21 +290,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))
@@ -335,46 +308,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']