jitsrc.py (4809B)
1 # This Source Code Form is subject to the terms of the Mozilla Public 2 # License, v. 2.0. If a copy of the MPL was not distributed with this file, 3 # You can obtain one at http://mozilla.org/MPL/2.0/. 4 5 # This is a gdb extension to automate the process of tracing backwards in rr 6 # from a jit instruction to the code that generated that instruction. 7 # 8 # Usage: 9 # (rr) x/i $pc 10 # => 0x240e954ac13a: pushq (%rbx) 11 # (rr) jitsrc 0x240e954ac13a 12 13 import re 14 15 import gdb 16 17 # (base_name, hops, func_name, source_var, dest_var) tuples, such that : 18 # - `base_name`: a regex matching the name of the function that implements 19 # the actual write 20 # - `hops`: the number of stack frames between `base_name` and `func_name` 21 # - `func_name`: a regex matching the name of the function that calls memcpy 22 # - `source_var`: an expression that can be evaluated in the frame 23 # corresponding to `func_name` to get the source of the memcpy 24 # - `dest_var`: an expression that can be evaluated in the frame 25 # corresponding to `func_name` to get the destination of the memcpy 26 # 27 # If an invocation of `jitsrc` stops in the middle of a memcpy, the solution 28 # is normally to add a new pattern here. 29 patterns = [ 30 ( 31 "__memmove", 32 1, 33 "js::jit::X86Encoding::BaseAssembler::executableCopy", 34 "src", 35 "dst", 36 ), 37 ( 38 "__memcpy", 39 1, 40 "js::jit::X86Encoding::BaseAssembler::executableCopy", 41 "src", 42 "dst", 43 ), 44 ( 45 "__memmove", 46 1, 47 "arena_t::RallocSmallOrLarge", 48 "aPtr", 49 "ret", 50 ), 51 ("__memcpy", 1, "arena_t::RallocSmallOrLarge", "aPtr", "ret"), 52 ( 53 "mozilla::detail::VectorImpl<.*>::new_<.*>", 54 3, 55 "mozilla::Vector<.*>::convertToHeapStorage", 56 "beginNoCheck()", 57 "newBuf", 58 ), 59 ( 60 "__memmove", 61 1, 62 "js::jit::AssemblerBufferWithConstantPools", 63 "&cur->instructions[0]", 64 "dest", 65 ), 66 ( 67 "__memcpy", 68 1, 69 "js::jit::AssemblerBufferWithConstantPools", 70 "&cur->instructions[0]", 71 "dest", 72 ), 73 ( 74 "__memcpy", 75 2, 76 "js::jit::AssemblerX86Shared::executableCopy", 77 "masm.m_formatter.m_buffer.m_buffer.mBegin", 78 "buffer", 79 ), 80 ("__memcpy", 1, "arena_t::RallocSmallOrLarge", "aPtr", "ret"), 81 ("js::jit::X86Encoding::SetInt32", 0, "js::jit::X86Encoding::SetInt32", "0", "0"), 82 ( 83 "js::jit::X86Encoding::SetPointer", 84 0, 85 "js::jit::X86Encoding::SetPointer", 86 "0", 87 "0", 88 ), 89 ( 90 "<unnamed>", 91 1, 92 "js::jit::AssemblerBufferWithConstantPools<.*>::executableCopy", 93 "&cur->instructions[0]", 94 "dest", 95 ), 96 ("std::__copy_move", 4, "CopySpan", "source.data()", "target.data()"), 97 ( 98 "__memmove_(avx|evex)_unaligned_erms", 99 1, 100 "mozilla::detail::EndianUtils::copyAndSwapTo<.*0,.*0", 101 "aSrc", 102 "(size_t) aDest", 103 ), 104 ] 105 106 107 class JitSource(gdb.Command): 108 def __init__(self): 109 super().__init__("jitsrc", gdb.COMMAND_RUNNING) 110 self.dont_repeat() 111 112 def disable_breakpoints(self): 113 self.disabled_breakpoints = [b for b in gdb.breakpoints() if b.enabled] 114 for b in self.disabled_breakpoints: 115 b.enabled = False 116 117 def enable_breakpoints(self): 118 for b in self.disabled_breakpoints: 119 b.enabled = True 120 121 def search_stack(self, base_name, hops, name, src, dst, address): 122 current_frame_name = gdb.newest_frame().name() or "<unnamed>" 123 if not re.match(base_name, current_frame_name): 124 return None 125 f = gdb.newest_frame() 126 for _ in range(hops): 127 f = f.older() 128 if not re.match(name, f.name() or "<unnamed>"): 129 return None 130 f.select() 131 src_val = gdb.parse_and_eval(src) 132 dst_val = gdb.parse_and_eval(dst) 133 return hex(src_val + int(address, 16) - dst_val) 134 135 def next_address(self, old): 136 for pattern in patterns: 137 found = self.search_stack(*pattern, old) 138 if found: 139 return found 140 return None 141 142 def runback(self, address): 143 b = gdb.Breakpoint( 144 "*" + address, type=gdb.BP_WATCHPOINT, wp_class=gdb.WP_WRITE, internal=True 145 ) 146 try: 147 while b.hit_count == 0: 148 gdb.execute("rc", to_string=True) 149 finally: 150 b.delete() 151 152 def invoke(self, arg, from_tty): 153 args = gdb.string_to_argv(arg) 154 address = args[0] 155 self.disable_breakpoints() 156 while address: 157 self.runback(address) 158 address = self.next_address(address) 159 self.enable_breakpoints() 160 161 162 # Register to the gdb runtime 163 JitSource()