summaryrefslogtreecommitdiffstats
path: root/js/src/gdb/mozilla/jitsrc.py
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:22:09 +0000
commit43a97878ce14b72f0981164f87f2e35e14151312 (patch)
tree620249daf56c0258faa40cbdcf9cfba06de2a846 /js/src/gdb/mozilla/jitsrc.py
parentInitial commit. (diff)
downloadfirefox-upstream.tar.xz
firefox-upstream.zip
Adding upstream version 110.0.1.upstream/110.0.1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'js/src/gdb/mozilla/jitsrc.py')
-rw-r--r--js/src/gdb/mozilla/jitsrc.py161
1 files changed, 161 insertions, 0 deletions
diff --git a/js/src/gdb/mozilla/jitsrc.py b/js/src/gdb/mozilla/jitsrc.py
new file mode 100644
index 0000000000..9a9ffff28b
--- /dev/null
+++ b/js/src/gdb/mozilla/jitsrc.py
@@ -0,0 +1,161 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this file,
+# You can obtain one at http://mozilla.org/MPL/2.0/.
+
+# This is a gdb extension to automate the process of tracing backwards in rr
+# from a jit instruction to the code that generated that instruction.
+#
+# Usage:
+# (rr) x/i $pc
+# => 0x240e954ac13a: pushq (%rbx)
+# (rr) jitsrc 0x240e954ac13a
+
+import re
+
+import gdb
+
+# (base_name, hops, func_name, source_var, dest_var) tuples, such that :
+# - `base_name`: a regex matching the name of the function that implements
+# the actual write
+# - `hops`: the number of stack frames between `base_name` and `func_name`
+# - `func_name`: a regex matching the name of the function that calls memcpy
+# - `source_var`: an expression that can be evaluated in the frame
+# corresponding to `func_name` to get the source of the memcpy
+# - `dest_var`: an expression that can be evaluated in the frame
+# corresponding to `func_name` to get the destination of the memcpy
+#
+# If an invocation of `jitsrc` stops in the middle of a memcpy, the solution
+# is normally to add a new pattern here.
+patterns = [
+ (
+ "__memmove_(avx|evex)_unaligned_erms",
+ 1,
+ "js::jit::X86Encoding::BaseAssembler::executableCopy",
+ "src",
+ "dst",
+ ),
+ (
+ "__memcpy_(avx|evex)_unaligned",
+ 1,
+ "js::jit::X86Encoding::BaseAssembler::executableCopy",
+ "src",
+ "dst",
+ ),
+ (
+ "__memmove_(avx|evex)_unaligned_erms",
+ 1,
+ "arena_t::RallocSmallOrLarge",
+ "aPtr",
+ "ret",
+ ),
+ ("__memcpy_(avx|evex)_unaligned", 1, "arena_t::RallocSmallOrLarge", "aPtr", "ret"),
+ (
+ "mozilla::detail::VectorImpl<.*>::new_<.*>",
+ 3,
+ "mozilla::Vector<.*>::convertToHeapStorage",
+ "beginNoCheck()",
+ "newBuf",
+ ),
+ (
+ "__memmove_(avx|evex)_unaligned_erms",
+ 1,
+ "js::jit::AssemblerBufferWithConstantPools",
+ "&cur->instructions[0]",
+ "dest",
+ ),
+ (
+ "__memcpy_sse2_unaligned",
+ 1,
+ "js::jit::AssemblerBufferWithConstantPools",
+ "&cur->instructions[0]",
+ "dest",
+ ),
+ (
+ "__memcpy_sse2_unaligned",
+ 2,
+ "js::jit::AssemblerX86Shared::executableCopy",
+ "masm.m_formatter.m_buffer.m_buffer.mBegin",
+ "buffer",
+ ),
+ ("__memcpy_sse2_unaligned", 1, "arena_t::RallocSmallOrLarge", "aPtr", "ret"),
+ ("js::jit::X86Encoding::SetInt32", 0, "js::jit::X86Encoding::SetInt32", "0", "0"),
+ (
+ "js::jit::X86Encoding::SetPointer",
+ 0,
+ "js::jit::X86Encoding::SetPointer",
+ "0",
+ "0",
+ ),
+ (
+ "<unnamed>",
+ 1,
+ "js::jit::AssemblerBufferWithConstantPools<.*>::executableCopy",
+ "&cur->instructions[0]",
+ "dest",
+ ),
+ ("std::__copy_move", 4, "CopySpan", "source.data()", "target.data()"),
+ (
+ "__memmove_(avx|evex)_unaligned_erms",
+ 1,
+ "mozilla::detail::EndianUtils::copyAndSwapTo<.*0,.*0",
+ "aSrc",
+ "(size_t) aDest",
+ ),
+]
+
+
+class JitSource(gdb.Command):
+ def __init__(self):
+ super(JitSource, self).__init__("jitsrc", gdb.COMMAND_RUNNING)
+ self.dont_repeat()
+
+ def disable_breakpoints(self):
+ self.disabled_breakpoints = [b for b in gdb.breakpoints() if b.enabled]
+ for b in self.disabled_breakpoints:
+ b.enabled = False
+
+ def enable_breakpoints(self):
+ for b in self.disabled_breakpoints:
+ b.enabled = True
+
+ def search_stack(self, base_name, hops, name, src, dst, address):
+ current_frame_name = gdb.newest_frame().name() or "<unnamed>"
+ if not re.match(base_name, current_frame_name):
+ return None
+ f = gdb.newest_frame()
+ for _ in range(hops):
+ f = f.older()
+ if not re.match(name, f.name()):
+ return None
+ f.select()
+ src_val = gdb.parse_and_eval(src)
+ dst_val = gdb.parse_and_eval(dst)
+ return hex(src_val + int(address, 16) - dst_val)
+
+ def next_address(self, old):
+ for pattern in patterns:
+ found = self.search_stack(*pattern, old)
+ if found:
+ return found
+ return None
+
+ def runback(self, address):
+ b = gdb.Breakpoint(
+ "*" + address, type=gdb.BP_WATCHPOINT, wp_class=gdb.WP_WRITE, internal=True
+ )
+ while b.hit_count == 0:
+ gdb.execute("rc", to_string=True)
+ b.delete()
+
+ def invoke(self, arg, from_tty):
+ args = gdb.string_to_argv(arg)
+ address = args[0]
+ self.disable_breakpoints()
+ while address:
+ self.runback(address)
+ address = self.next_address(address)
+ self.enable_breakpoints()
+
+
+# Register to the gdb runtime
+JitSource()