brintos

brintos / llvm-project-archived public Read only

0
0
Text · 13.0 KiB · 73c81ef Raw
382 lines · python
1import lldb2import os3import binascii4from lldbsuite.test.lldbtest import *5from lldbsuite.test.decorators import *6from lldbsuite.test.gdbclientutils import *7from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase8 9MODULE_ID = 410LOAD_ADDRESS = MODULE_ID << 3211WASM_LOCAL_ADDR = 0x103E012 13 14def format_register_value(val):15    """16    Encode each byte by two hex digits in little-endian order.17    """18    result = ""19    mask = 0xFF20    shift = 021    for i in range(0, 8):22        x = (val & mask) >> shift23        result += format(x, "02x")24        mask <<= 825        shift += 826    return result27 28 29class WasmStackFrame:30    def __init__(self, address):31        self._address = address32 33    def __str__(self):34        return format_register_value(LOAD_ADDRESS | self._address)35 36 37class WasmCallStack:38    def __init__(self, wasm_stack_frames):39        self._wasm_stack_frames = wasm_stack_frames40 41    def __str__(self):42        result = ""43        for frame in self._wasm_stack_frames:44            result += str(frame)45        return result46 47 48class FakeMemory:49    def __init__(self, start_addr, end_addr):50        self._base_addr = start_addr51        self._memory = bytearray(end_addr - start_addr)52        self._memoryview = memoryview(self._memory)53 54    def store_bytes(self, addr, bytes_obj):55        assert addr > self._base_addr56        assert addr < self._base_addr + len(self._memoryview)57        offset = addr - self._base_addr58        chunk = self._memoryview[offset : offset + len(bytes_obj)]59        for i in range(len(bytes_obj)):60            chunk[i] = bytes_obj[i]61 62    def get_bytes(self, addr, length):63        assert addr > self._base_addr64        assert addr < self._base_addr + len(self._memoryview)65 66        offset = addr - self._base_addr67        return self._memoryview[offset : offset + length]68 69    def contains(self, addr):70        return addr - self._base_addr < len(self._memoryview)71 72 73class MyResponder(MockGDBServerResponder):74    current_pc = LOAD_ADDRESS | 0x01AD75 76    def __init__(self, obj_path, module_name="", wasm_call_stacks=[], memory=None):77        self._obj_path = obj_path78        self._module_name = module_name or obj_path79        self._wasm_call_stacks = wasm_call_stacks80        self._call_stack_request_count = 081        self._memory = memory82        MockGDBServerResponder.__init__(self)83 84    def respond(self, packet):85        if packet[0:13] == "qRegisterInfo":86            return self.qRegisterInfo(packet[13:])87        if packet.startswith("qWasmCallStack"):88            return self.qWasmCallStack()89        if packet.startswith("qWasmLocal"):90            return self.qWasmLocal(packet)91        return MockGDBServerResponder.respond(self, packet)92 93    def qSupported(self, client_supported):94        return "qXfer:libraries:read+;PacketSize=1000;vContSupported-"95 96    def qHostInfo(self):97        return ""98 99    def QEnableErrorStrings(self):100        return ""101 102    def qfThreadInfo(self):103        return "m1,"104 105    def qRegisterInfo(self, index):106        if index == 0:107            return "name:pc;alt-name:pc;bitsize:64;offset:0;encoding:uint;format:hex;set:General Purpose Registers;gcc:16;dwarf:16;generic:pc;"108        return "E45"109 110    def qProcessInfo(self):111        return "pid:1;ppid:1;uid:1;gid:1;euid:1;egid:1;name:%s;triple:%s;ptrsize:4" % (112            hex_encode_bytes("lldb"),113            hex_encode_bytes("wasm32-unknown-unknown-wasm"),114        )115 116    def haltReason(self):117        return "T02thread:1;"118 119    def readRegister(self, register):120        return format_register_value(self.current_pc)121 122    def qXferRead(self, obj, annex, offset, length):123        if obj == "libraries":124            xml = (125                '<library-list><library name="%s"><section address="%d"/></library></library-list>'126                % (self._module_name, LOAD_ADDRESS)127            )128            return xml, False129        else:130            return None, False131 132    def readMemory(self, addr, length):133        if self._memory and self._memory.contains(addr):134            chunk = self._memory.get_bytes(addr, length)135            return chunk.hex()136        if addr < LOAD_ADDRESS:137            return "E02"138        result = ""139        with open(self._obj_path, mode="rb") as file:140            file_content = bytearray(file.read())141            if addr >= LOAD_ADDRESS + len(file_content):142                return "E03"143            addr_from = addr - LOAD_ADDRESS144            addr_to = addr_from + min(length, len(file_content) - addr_from)145            for i in range(addr_from, addr_to):146                result += format(file_content[i], "02x")147            file.close()148        return result149 150    def setBreakpoint(self, packet):151        bp_data = packet[1:].split(",")152        self._bp_address = bp_data[1]153        return "OK"154 155    def qfThreadInfo(self):156        return "m1"157 158    def cont(self):159        # Continue execution. Simulates running the Wasm engine until a breakpoint is hit.160        return (161            "T05thread-pcs:"162            + format(int(self._bp_address, 16) & 0x3FFFFFFFFFFFFFFF, "x")163            + ";thread:1"164        )165 166    def qWasmCallStack(self):167        if len(self._wasm_call_stacks) == 0:168            return ""169        result = str(self._wasm_call_stacks[self._call_stack_request_count])170        self._call_stack_request_count += 1171        return result172 173    def qWasmLocal(self, packet):174        # Format: qWasmLocal:frame_index;index175        data = packet.split(":")176        data = data[1].split(";")177        frame_index, local_index = data178        if frame_index == "0" and local_index == "2":179            return format_register_value(WASM_LOCAL_ADDR)180        return "E03"181 182 183class TestWasm(GDBRemoteTestBase):184    @skipIfAsan185    @skipIfXmlSupportMissing186    def test_load_module_with_embedded_symbols_from_remote(self):187        """Test connecting to a WebAssembly engine via GDB-remote and loading a Wasm module with embedded DWARF symbols"""188 189        yaml_path = "test_wasm_embedded_debug_sections.yaml"190        yaml_base, ext = os.path.splitext(yaml_path)191        obj_path = self.getBuildArtifact(yaml_base)192        self.yaml2obj(yaml_path, obj_path)193 194        self.server.responder = MyResponder(obj_path, "test_wasm")195 196        target = self.dbg.CreateTarget("")197        process = self.connect(target, "wasm")198        lldbutil.expect_state_changes(199            self, self.dbg.GetListener(), process, [lldb.eStateStopped]200        )201 202        num_modules = target.GetNumModules()203        self.assertEqual(1, num_modules)204 205        module = target.GetModuleAtIndex(0)206        num_sections = module.GetNumSections()207        self.assertEqual(5, num_sections)208 209        code_section = module.GetSectionAtIndex(0)210        self.assertEqual("code", code_section.GetName())211        self.assertEqual(212            LOAD_ADDRESS | code_section.GetFileOffset(),213            code_section.GetLoadAddress(target),214        )215 216        debug_info_section = module.GetSectionAtIndex(1)217        self.assertEqual(".debug_info", debug_info_section.GetName())218        self.assertEqual(219            LOAD_ADDRESS | debug_info_section.GetFileOffset(),220            debug_info_section.GetLoadAddress(target),221        )222 223        debug_abbrev_section = module.GetSectionAtIndex(2)224        self.assertEqual(".debug_abbrev", debug_abbrev_section.GetName())225        self.assertEqual(226            LOAD_ADDRESS | debug_abbrev_section.GetFileOffset(),227            debug_abbrev_section.GetLoadAddress(target),228        )229 230        debug_line_section = module.GetSectionAtIndex(3)231        self.assertEqual(".debug_line", debug_line_section.GetName())232        self.assertEqual(233            LOAD_ADDRESS | debug_line_section.GetFileOffset(),234            debug_line_section.GetLoadAddress(target),235        )236 237        debug_str_section = module.GetSectionAtIndex(4)238        self.assertEqual(".debug_str", debug_str_section.GetName())239        self.assertEqual(240            LOAD_ADDRESS | debug_line_section.GetFileOffset(),241            debug_line_section.GetLoadAddress(target),242        )243 244    @skipIfAsan245    @skipIfXmlSupportMissing246    def test_load_module_with_stripped_symbols_from_remote(self):247        """Test connecting to a WebAssembly engine via GDB-remote and loading a Wasm module with symbols stripped into a separate Wasm file"""248 249        sym_yaml_path = "test_sym.yaml"250        sym_yaml_base, ext = os.path.splitext(sym_yaml_path)251        sym_obj_path = self.getBuildArtifact(sym_yaml_base) + ".wasm"252        self.yaml2obj(sym_yaml_path, sym_obj_path)253 254        yaml_path = "test_wasm_external_debug_sections.yaml"255        yaml_base, ext = os.path.splitext(yaml_path)256        obj_path = self.getBuildArtifact(yaml_base) + ".wasm"257        self.yaml2obj(yaml_path, obj_path)258 259        self.server.responder = MyResponder(obj_path, "test_wasm")260 261        folder, _ = os.path.split(obj_path)262        self.runCmd(263            "settings set target.debug-file-search-paths " + os.path.abspath(folder)264        )265 266        target = self.dbg.CreateTarget("")267        process = self.connect(target, "wasm")268        lldbutil.expect_state_changes(269            self, self.dbg.GetListener(), process, [lldb.eStateStopped]270        )271 272        num_modules = target.GetNumModules()273        self.assertEqual(1, num_modules)274 275        module = target.GetModuleAtIndex(0)276        num_sections = module.GetNumSections()277        self.assertEqual(5, num_sections)278 279        code_section = module.GetSectionAtIndex(0)280        self.assertEqual("code", code_section.GetName())281        self.assertEqual(282            LOAD_ADDRESS | code_section.GetFileOffset(),283            code_section.GetLoadAddress(target),284        )285 286        debug_info_section = module.GetSectionAtIndex(1)287        self.assertEqual(".debug_info", debug_info_section.GetName())288        self.assertEqual(289            lldb.LLDB_INVALID_ADDRESS, debug_info_section.GetLoadAddress(target)290        )291 292        debug_abbrev_section = module.GetSectionAtIndex(2)293        self.assertEqual(".debug_abbrev", debug_abbrev_section.GetName())294        self.assertEqual(295            lldb.LLDB_INVALID_ADDRESS, debug_abbrev_section.GetLoadAddress(target)296        )297 298        debug_line_section = module.GetSectionAtIndex(3)299        self.assertEqual(".debug_line", debug_line_section.GetName())300        self.assertEqual(301            lldb.LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target)302        )303 304        debug_str_section = module.GetSectionAtIndex(4)305        self.assertEqual(".debug_str", debug_str_section.GetName())306        self.assertEqual(307            lldb.LLDB_INVALID_ADDRESS, debug_line_section.GetLoadAddress(target)308        )309 310    @skipIfAsan311    @skipIfXmlSupportMissing312    def test_simple_wasm_debugging_session(self):313        """Test connecting to a WebAssembly engine via GDB-remote, loading a314        Wasm module with embedded DWARF symbols, setting a breakpoint and315        checking the debuggee state"""316 317        # simple.yaml was created by compiling simple.c to wasm and using318        # obj2yaml on the output.319        #320        # $ clang -target wasm32 -nostdlib -Wl,--no-entry -Wl,--export-all -O0 -g -o simple.wasm simple.c321        # $ obj2yaml simple.wasm -o simple.yaml322        yaml_path = "simple.yaml"323        yaml_base, _ = os.path.splitext(yaml_path)324        obj_path = self.getBuildArtifact(yaml_base)325        self.yaml2obj(yaml_path, obj_path)326 327        # Create a fake call stack.328        call_stacks = [329            WasmCallStack(330                [WasmStackFrame(0x019C), WasmStackFrame(0x01E5), WasmStackFrame(0x01FE)]331            ),332        ]333 334        # Create fake memory for our wasm locals.335        self.memory = FakeMemory(0x10000, 0x20000)336        self.memory.store_bytes(337            WASM_LOCAL_ADDR,338            bytes.fromhex(339                "0000000000000000020000000100000000000000020000000100000000000000"340            ),341        )342 343        self.server.responder = MyResponder(344            obj_path, "test_wasm", call_stacks, self.memory345        )346 347        target = self.dbg.CreateTarget("")348        breakpoint = target.BreakpointCreateByName("add")349        process = self.connect(target, "wasm")350        lldbutil.expect_state_changes(351            self, self.dbg.GetListener(), process, [lldb.eStateStopped]352        )353 354        location = breakpoint.GetLocationAtIndex(0)355        self.assertTrue(location and location.IsEnabled(), VALID_BREAKPOINT_LOCATION)356 357        num_modules = target.GetNumModules()358        self.assertEqual(1, num_modules)359 360        thread = process.GetThreadAtIndex(0)361        self.assertTrue(thread.IsValid())362 363        # Check that our frames match our fake call stack.364        frame0 = thread.GetFrameAtIndex(0)365        self.assertTrue(frame0.IsValid())366        self.assertEqual(frame0.GetPC(), LOAD_ADDRESS | 0x019C)367        self.assertIn("add", frame0.GetFunctionName())368 369        frame1 = thread.GetFrameAtIndex(1)370        self.assertTrue(frame1.IsValid())371        self.assertEqual(frame1.GetPC(), LOAD_ADDRESS | 0x01E5)372        self.assertIn("main", frame1.GetFunctionName())373 374        # Check that we can resolve local variables.375        a = frame0.FindVariable("a")376        self.assertTrue(a.IsValid())377        self.assertEqual(a.GetValueAsUnsigned(), 1)378 379        b = frame0.FindVariable("b")380        self.assertTrue(b.IsValid())381        self.assertEqual(b.GetValueAsUnsigned(), 2)382