brintos

brintos / llvm-project-archived public Read only

0
0
Text · 25.0 KiB · 8cf0452 Raw
730 lines · python
1import lldb2import binascii3import os.path4from lldbsuite.test.lldbtest import *5from lldbsuite.test.decorators import *6from lldbsuite.test.gdbclientutils import *7from lldbsuite.test.lldbgdbclient import GDBRemoteTestBase8 9 10class TestGDBRemoteClient(GDBRemoteTestBase):11    class gPacketResponder(MockGDBServerResponder):12        registers = [13            "name:rax;bitsize:64;offset:0;encoding:uint;format:hex;set:General Purpose Registers;ehframe:0;dwarf:0;",14            "name:rbx;bitsize:64;offset:8;encoding:uint;format:hex;set:General Purpose Registers;ehframe:3;dwarf:3;",15            "name:rcx;bitsize:64;offset:16;encoding:uint;format:hex;set:General Purpose Registers;ehframe:2;dwarf:2;generic:arg4;",16            "name:rdx;bitsize:64;offset:24;encoding:uint;format:hex;set:General Purpose Registers;ehframe:1;dwarf:1;generic:arg3;",17            "name:rdi;bitsize:64;offset:32;encoding:uint;format:hex;set:General Purpose Registers;ehframe:5;dwarf:5;generic:arg1;",18            "name:rsi;bitsize:64;offset:40;encoding:uint;format:hex;set:General Purpose Registers;ehframe:4;dwarf:4;generic:arg2;",19            "name:rbp;bitsize:64;offset:48;encoding:uint;format:hex;set:General Purpose Registers;ehframe:6;dwarf:6;generic:fp;",20            "name:rsp;bitsize:64;offset:56;encoding:uint;format:hex;set:General Purpose Registers;ehframe:7;dwarf:7;generic:sp;",21        ]22 23        def qRegisterInfo(self, num):24            try:25                return self.registers[num]26            except IndexError:27                return "E45"28 29        def readRegisters(self):30            return len(self.registers) * 16 * "0"31 32        def readRegister(self, register):33            return "0000000000000000"34 35    def test_connect(self):36        """Test connecting to a remote gdb server"""37        target = self.createTarget("a.yaml")38        process = self.connect(target)39        self.assertPacketLogReceived(["qProcessInfo", "qfThreadInfo"])40 41    def test_attach_fail(self):42        error_msg = "mock-error-msg"43 44        class MyResponder(MockGDBServerResponder):45            # Pretend we don't have any process during the initial queries.46            def qC(self):47                return "E42"48 49            def qfThreadInfo(self):50                return "OK"  # No threads.51 52            # Then, when we are asked to attach, error out.53            def vAttach(self, pid):54                return "E42;" + binascii.hexlify(error_msg.encode()).decode()55 56        self.server.responder = MyResponder()57 58        target = self.dbg.CreateTarget("")59        process = self.connect(target)60        lldbutil.expect_state_changes(61            self, self.dbg.GetListener(), process, [lldb.eStateConnected]62        )63 64        error = lldb.SBError()65        target.AttachToProcessWithID(lldb.SBListener(), 47, error)66        self.assertEqual(error_msg, error.GetCString())67 68    def test_launch_fail(self):69        class MyResponder(MockGDBServerResponder):70            # Pretend we don't have any process during the initial queries.71            def qC(self):72                return "E42"73 74            def qfThreadInfo(self):75                return "OK"  # No threads.76 77            # Then, when we are asked to attach, error out.78            def A(self, packet):79                return "E47"80 81        self.server.responder = MyResponder()82 83        target = self.createTarget("a.yaml")84        process = self.connect(target)85        lldbutil.expect_state_changes(86            self, self.dbg.GetListener(), process, [lldb.eStateConnected]87        )88 89        error = lldb.SBError()90        target.Launch(91            lldb.SBListener(), None, None, None, None, None, None, 0, True, error92        )93        self.assertRegex(error.GetCString(), "Cannot launch '.*a': Error 71")94 95    def test_launch_rich_error(self):96        class MyResponder(MockGDBServerResponder):97            def qC(self):98                return "E42"99 100            def qfThreadInfo(self):101                return "OK"  # No threads.102 103            # Then, when we are asked to attach, error out.104            def vRun(self, packet):105                return "Eff;" + seven.hexlify("I'm a teapot")106 107        self.server.responder = MyResponder()108 109        target = self.createTarget("a.yaml")110        process = self.connect(target)111        lldbutil.expect_state_changes(112            self, self.dbg.GetListener(), process, [lldb.eStateConnected]113        )114 115        error = lldb.SBError()116        target.Launch(117            lldb.SBListener(), None, None, None, None, None, None, 0, True, error118        )119        self.assertRegex(error.GetCString(), "Cannot launch '.*a': I'm a teapot")120 121    def test_read_registers_using_g_packets(self):122        """Test reading registers using 'g' packets (default behavior)"""123        self.dbg.HandleCommand(124            "settings set plugin.process.gdb-remote.use-g-packet-for-reading true"125        )126        self.addTearDownHook(127            lambda: self.runCmd(128                "settings set plugin.process.gdb-remote.use-g-packet-for-reading false"129            )130        )131        self.server.responder = self.gPacketResponder()132        target = self.createTarget("a.yaml")133        process = self.connect(target)134 135        # We want to make sure that the process is using the g packet, but it's136        # not required the "connect" should read all registers.  However, it might137        # have...  So we need to wait till we explicitly 'read_registers' to do138        # test.139        # Also, even with the use-g-packet-for-reading lldb will sometimes send p0140        # early on to see if the packet is supported.  So we can't say that there141        # will be NO p packets.142        # But there certainly should be no p packets after the g packet.143 144        self.read_registers(process)145        received = self.server.responder.packetLog.get_received()146        print(f"\nPACKET LOG:\n{received}\n")147        g_pos = 0148        try:149            g_pos = received.index("g")150        except err:151            self.fail("'g' packet not found after fetching registers")152 153        try:154            second_g = received.index("g", g_pos + 1)155            self.fail("Found more than one 'g' packet")156        except:157            pass158 159        # Make sure there aren't any `p` packets after the `g` packet:160        self.assertEqual(161            0,162            len([p for p in received[g_pos:] if p.startswith("p")]),163        )164 165    def test_read_registers_using_p_packets(self):166        """Test reading registers using 'p' packets"""167        self.dbg.HandleCommand(168            "settings set plugin.process.gdb-remote.use-g-packet-for-reading false"169        )170        self.server.responder = self.gPacketResponder()171        target = self.createTarget("a.yaml")172        process = self.connect(target)173 174        self.read_registers(process)175        received = self.server.responder.packetLog.get_received()176        self.assertNotIn("g", received)177        self.assertGreater(len([p for p in received if p.startswith("p")]), 0)178 179    def test_write_registers_using_P_packets(self):180        """Test writing registers using 'P' packets (default behavior)"""181        self.server.responder = self.gPacketResponder()182        target = self.createTarget("a.yaml")183        process = self.connect(target)184 185        self.write_registers(process)186        received = self.server.responder.packetLog.get_received()187        self.assertEqual(0, len([p for p in received if p.startswith("G")]))188        self.assertGreater(len([p for p in received if p.startswith("P")]), 0)189 190    def test_write_registers_using_G_packets(self):191        """Test writing registers using 'G' packets"""192 193        class MyResponder(self.gPacketResponder):194            def readRegister(self, register):195                # empty string means unsupported196                return ""197 198        self.server.responder = MyResponder()199        target = self.createTarget("a.yaml")200        process = self.connect(target)201 202        self.write_registers(process)203        received = self.server.responder.packetLog.get_received()204        self.assertEqual(0, len([p for p in received if p.startswith("P")]))205        self.assertGreater(len([p for p in received if p.startswith("G")]), 0)206 207    def read_registers(self, process):208        self.for_each_gpr(209            process, lambda r: self.assertEqual("0x0000000000000000", r.GetValue())210        )211 212    def write_registers(self, process):213        self.for_each_gpr(214            process, lambda r: r.SetValueFromCString("0x0000000000000000")215        )216 217    def for_each_gpr(self, process, operation):218        registers = process.GetThreadAtIndex(0).GetFrameAtIndex(0).GetRegisters()219        self.assertGreater(registers.GetSize(), 0)220        regSet = registers[0]221        numChildren = regSet.GetNumChildren()222        self.assertGreater(numChildren, 0)223        for i in range(numChildren):224            operation(regSet.GetChildAtIndex(i))225 226    def test_launch_A(self):227        class MyResponder(MockGDBServerResponder):228            def __init__(self, *args, **kwargs):229                self.started = False230                return super().__init__(*args, **kwargs)231 232            def qC(self):233                if self.started:234                    return "QCp10.10"235                else:236                    return "E42"237 238            def qfThreadInfo(self):239                if self.started:240                    return "mp10.10"241                else:242                    return "E42"243 244            def qsThreadInfo(self):245                return "l"246 247            def A(self, packet):248                self.started = True249                return "OK"250 251            def qLaunchSuccess(self):252                if self.started:253                    return "OK"254                return "E42"255 256        self.server.responder = MyResponder()257 258        target = self.createTarget("a.yaml")259        # NB: apparently GDB packets are using "/" on Windows too260        exe_path = self.getBuildArtifact("a").replace(os.path.sep, "/")261        exe_hex = binascii.b2a_hex(exe_path.encode()).decode()262        process = self.connect(target)263        lldbutil.expect_state_changes(264            self, self.dbg.GetListener(), process, [lldb.eStateConnected]265        )266 267        target.Launch(268            lldb.SBListener(),269            ["arg1", "arg2", "arg3"],  # argv270            [],  # envp271            None,  # stdin_path272            None,  # stdout_path273            None,  # stderr_path274            None,  # working_directory275            0,  # launch_flags276            True,  # stop_at_entry277            lldb.SBError(),278        )  # error279        self.assertTrue(process, PROCESS_IS_VALID)280        self.assertEqual(process.GetProcessID(), 16)281 282        self.assertPacketLogReceived(283            [284                "A%d,0,%s,8,1,61726731,8,2,61726732,8,3,61726733"285                % (len(exe_hex), exe_hex),286            ]287        )288 289    def test_launch_vRun(self):290        class MyResponder(MockGDBServerResponder):291            def __init__(self, *args, **kwargs):292                self.started = False293                return super().__init__(*args, **kwargs)294 295            def qC(self):296                if self.started:297                    return "QCp10.10"298                else:299                    return "E42"300 301            def qfThreadInfo(self):302                if self.started:303                    return "mp10.10"304                else:305                    return "E42"306 307            def qsThreadInfo(self):308                return "l"309 310            def vRun(self, packet):311                self.started = True312                return "T13"313 314            def A(self, packet):315                return "E28"316 317        self.server.responder = MyResponder()318 319        target = self.createTarget("a.yaml")320        # NB: apparently GDB packets are using "/" on Windows too321        exe_path = self.getBuildArtifact("a").replace(os.path.sep, "/")322        exe_hex = binascii.b2a_hex(exe_path.encode()).decode()323        process = self.connect(target)324        lldbutil.expect_state_changes(325            self, self.dbg.GetListener(), process, [lldb.eStateConnected]326        )327 328        process = target.Launch(329            lldb.SBListener(),330            ["arg1", "arg2", "arg3"],  # argv331            [],  # envp332            None,  # stdin_path333            None,  # stdout_path334            None,  # stderr_path335            None,  # working_directory336            0,  # launch_flags337            True,  # stop_at_entry338            lldb.SBError(),339        )  # error340        self.assertTrue(process, PROCESS_IS_VALID)341        self.assertEqual(process.GetProcessID(), 16)342 343        self.assertPacketLogReceived(344            ["vRun;%s;61726731;61726732;61726733" % (exe_hex,)]345        )346 347    def test_launch_lengthy_vRun(self):348        class MyResponder(MockGDBServerResponder):349            def __init__(self, *args, **kwargs):350                self.started = False351                return super().__init__(*args, **kwargs)352 353            def qC(self):354                if self.started:355                    return "QCp10.10"356                else:357                    return "E42"358 359            def qfThreadInfo(self):360                if self.started:361                    return "mp10.10"362                else:363                    return "E42"364 365            def qsThreadInfo(self):366                return "l"367 368            def qEcho(self, num):369                resp = "qEcho:" + str(num)370                if num >= 2:371                    # We have launched our program372                    self.started = True373                    return [resp, "T13"]374 375                return resp376 377            def qSupported(self, client_supported):378                return "PacketSize=3fff;QStartNoAckMode+;qEcho+;"379 380            def qHostInfo(self):381                return "default_packet_timeout:1;"382 383            def vRun(self, packet):384                return [self.RESPONSE_NONE]385 386            def A(self, packet):387                return "E28"388 389        self.server.responder = MyResponder()390 391        target = self.createTarget("a.yaml")392        # NB: apparently GDB packets are using "/" on Windows too393        exe_path = self.getBuildArtifact("a").replace(os.path.sep, "/")394        exe_hex = binascii.b2a_hex(exe_path.encode()).decode()395        process = self.connect(target)396        lldbutil.expect_state_changes(397            self, self.dbg.GetListener(), process, [lldb.eStateConnected]398        )399 400        process = target.Launch(401            lldb.SBListener(),402            ["arg1", "arg2", "arg3"],  # argv403            [],  # envp404            None,  # stdin_path405            None,  # stdout_path406            None,  # stderr_path407            None,  # working_directory408            0,  # launch_flags409            True,  # stop_at_entry410            lldb.SBError(),411        )  # error412        self.assertTrue(process, PROCESS_IS_VALID)413        self.assertEqual(process.GetProcessID(), 16)414 415        self.assertPacketLogReceived(416            ["vRun;%s;61726731;61726732;61726733" % (exe_hex,)]417        )418 419    def test_launch_QEnvironment(self):420        class MyResponder(MockGDBServerResponder):421            def qC(self):422                return "E42"423 424            def qfThreadInfo(self):425                return "E42"426 427            def vRun(self, packet):428                self.started = True429                return "E28"430 431        self.server.responder = MyResponder()432 433        target = self.createTarget("a.yaml")434        process = self.connect(target)435        lldbutil.expect_state_changes(436            self, self.dbg.GetListener(), process, [lldb.eStateConnected]437        )438 439        target.Launch(440            lldb.SBListener(),441            [],  # argv442            [443                "PLAIN=foo",444                "NEEDSENC=frob$",445                "NEEDSENC2=fr*ob",446                "NEEDSENC3=fro}b",447                "NEEDSENC4=f#rob",448                "EQUALS=foo=bar",449            ],  # envp450            None,  # stdin_path451            None,  # stdout_path452            None,  # stderr_path453            None,  # working_directory454            0,  # launch_flags455            True,  # stop_at_entry456            lldb.SBError(),457        )  # error458 459        self.assertPacketLogReceived(460            [461                "QEnvironment:EQUALS=foo=bar",462                "QEnvironmentHexEncoded:4e45454453454e433d66726f6224",463                "QEnvironmentHexEncoded:4e45454453454e43323d66722a6f62",464                "QEnvironmentHexEncoded:4e45454453454e43333d66726f7d62",465                "QEnvironmentHexEncoded:4e45454453454e43343d6623726f62",466                "QEnvironment:PLAIN=foo",467            ]468        )469 470    def test_launch_QEnvironmentHexEncoded_only(self):471        class MyResponder(MockGDBServerResponder):472            def qC(self):473                return "E42"474 475            def qfThreadInfo(self):476                return "E42"477 478            def vRun(self, packet):479                self.started = True480                return "E28"481 482            def QEnvironment(self, packet):483                return ""484 485        self.server.responder = MyResponder()486 487        target = self.createTarget("a.yaml")488        process = self.connect(target)489        lldbutil.expect_state_changes(490            self, self.dbg.GetListener(), process, [lldb.eStateConnected]491        )492 493        target.Launch(494            lldb.SBListener(),495            [],  # argv496            [497                "PLAIN=foo",498                "NEEDSENC=frob$",499                "NEEDSENC2=fr*ob",500                "NEEDSENC3=fro}b",501                "NEEDSENC4=f#rob",502                "EQUALS=foo=bar",503            ],  # envp504            None,  # stdin_path505            None,  # stdout_path506            None,  # stderr_path507            None,  # working_directory508            0,  # launch_flags509            True,  # stop_at_entry510            lldb.SBError(),511        )  # error512 513        self.assertPacketLogReceived(514            [515                "QEnvironmentHexEncoded:455155414c533d666f6f3d626172",516                "QEnvironmentHexEncoded:4e45454453454e433d66726f6224",517                "QEnvironmentHexEncoded:4e45454453454e43323d66722a6f62",518                "QEnvironmentHexEncoded:4e45454453454e43333d66726f7d62",519                "QEnvironmentHexEncoded:4e45454453454e43343d6623726f62",520                "QEnvironmentHexEncoded:504c41494e3d666f6f",521            ]522        )523 524    def test_detach_no_multiprocess(self):525        class MyResponder(MockGDBServerResponder):526            def __init__(self):527                super().__init__()528                self.detached = None529 530            def qfThreadInfo(self):531                return "10200"532 533            def D(self, packet):534                self.detached = packet535                return "OK"536 537        self.server.responder = MyResponder()538        target = self.dbg.CreateTarget("")539        process = self.connect(target)540        process.Detach()541        self.assertEqual(self.server.responder.detached, "D")542 543    def test_detach_pid(self):544        class MyResponder(MockGDBServerResponder):545            def __init__(self, test_case):546                super().__init__()547                self.test_case = test_case548                self.detached = None549 550            def qSupported(self, client_supported):551                self.test_case.assertIn("multiprocess+", client_supported)552                return "multiprocess+;" + super().qSupported(client_supported)553 554            def qfThreadInfo(self):555                return "mp400.10200"556 557            def D(self, packet):558                self.detached = packet559                return "OK"560 561        self.server.responder = MyResponder(self)562        target = self.dbg.CreateTarget("")563        process = self.connect(target)564        process.Detach()565        self.assertRegex(self.server.responder.detached, r"D;0*400")566 567    def test_signal_gdb(self):568        class MyResponder(MockGDBServerResponder):569            def qSupported(self, client_supported):570                return "PacketSize=3fff;QStartNoAckMode+"571 572            def haltReason(self):573                return "S0a"574 575            def cont(self):576                return self.haltReason()577 578        self.server.responder = MyResponder()579 580        self.runCmd("platform select remote-linux")581        target = self.createTarget("a.yaml")582        process = self.connect(target)583 584        self.assertEqual(process.threads[0].GetStopReason(), lldb.eStopReasonSignal)585        self.assertEqual(process.threads[0].stop_description, "signal SIGBUS")586 587    def test_signal_lldb_old(self):588        class MyResponder(MockGDBServerResponder):589            def qSupported(self, client_supported):590                return "PacketSize=3fff;QStartNoAckMode+"591 592            def qHostInfo(self):593                return "triple:61726d76372d756e6b6e6f776e2d6c696e75782d676e75;"594 595            def QThreadSuffixSupported(self):596                return "OK"597 598            def haltReason(self):599                return "S0a"600 601            def cont(self):602                return self.haltReason()603 604        self.server.responder = MyResponder()605 606        self.runCmd("platform select remote-linux")607        target = self.createTarget("a.yaml")608        process = self.connect(target)609 610        self.assertEqual(process.threads[0].GetStopReason(), lldb.eStopReasonSignal)611        self.assertEqual(process.threads[0].stop_description, "signal SIGUSR1")612 613    def test_signal_lldb(self):614        class MyResponder(MockGDBServerResponder):615            def qSupported(self, client_supported):616                return "PacketSize=3fff;QStartNoAckMode+;native-signals+"617 618            def qHostInfo(self):619                return "triple:61726d76372d756e6b6e6f776e2d6c696e75782d676e75;"620 621            def haltReason(self):622                return "S0a"623 624            def cont(self):625                return self.haltReason()626 627        self.server.responder = MyResponder()628 629        self.runCmd("platform select remote-linux")630        target = self.createTarget("a.yaml")631        process = self.connect(target)632 633        self.assertEqual(process.threads[0].GetStopReason(), lldb.eStopReasonSignal)634        self.assertEqual(process.threads[0].stop_description, "signal SIGUSR1")635 636    def do_siginfo_test(self, platform, target_yaml, raw_data, expected):637        class MyResponder(MockGDBServerResponder):638            def qSupported(self, client_supported):639                return "PacketSize=3fff;QStartNoAckMode+;qXfer:siginfo:read+"640 641            def qXferRead(self, obj, annex, offset, length):642                if obj == "siginfo":643                    return raw_data, False644                else:645                    return None, False646 647            def haltReason(self):648                return "T02"649 650            def cont(self):651                return self.haltReason()652 653        self.server.responder = MyResponder()654 655        self.runCmd("platform select " + platform)656        target = self.createTarget(target_yaml)657        process = self.connect(target)658 659        siginfo = process.threads[0].GetSiginfo()660        self.assertSuccess(siginfo.GetError())661 662        for key, value in expected.items():663            self.assertEqual(664                siginfo.GetValueForExpressionPath("." + key).GetValueAsUnsigned(), value665            )666 667    def test_siginfo_linux_amd64(self):668        data = (669            # si_signo         si_errno        si_code670            "\x11\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00"671            # __pad0           si_pid          si_uid672            "\x00\x00\x00\x00\xbf\xf7\x0b\x00\xe8\x03\x00\x00"673            # si_status674            "\x0c\x00\x00\x00"675            + "\x00" * 100676        )677        expected = {678            "si_signo": 17,  # SIGCHLD679            "si_errno": 0,680            "si_code": 1,  # CLD_EXITED681            "_sifields._sigchld.si_pid": 784319,682            "_sifields._sigchld.si_uid": 1000,683            "_sifields._sigchld.si_status": 12,684            "_sifields._sigchld.si_utime": 0,685            "_sifields._sigchld.si_stime": 0,686        }687        self.do_siginfo_test("remote-linux", "basic_eh_frame.yaml", data, expected)688 689    def test_siginfo_linux_i386(self):690        data = (691            # si_signo         si_errno        si_code692            "\x11\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00"693            # si_pid           si_uid          si_status694            "\x49\x43\x07\x00\xe8\x03\x00\x00\x0c\x00\x00\x00"695            + "\x00" * 104696        )697        expected = {698            "si_signo": 17,  # SIGCHLD699            "si_errno": 0,700            "si_code": 1,  # CLD_EXITED701            "_sifields._sigchld.si_pid": 475977,702            "_sifields._sigchld.si_uid": 1000,703            "_sifields._sigchld.si_status": 12,704            "_sifields._sigchld.si_utime": 0,705            "_sifields._sigchld.si_stime": 0,706        }707        self.do_siginfo_test("remote-linux", "basic_eh_frame-i386.yaml", data, expected)708 709    def test_siginfo_freebsd_amd64(self):710        data = (711            # si_signo         si_errno        si_code712            "\x0b\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00"713            # si_pid           si_uid          si_status714            "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"715            # si_addr716            "\x76\x98\xba\xdc\xfe\x00\x00\x00"717            # si_status                        si_trapno718            "\x00\x00\x00\x00\x00\x00\x00\x00\x0c\x00\x00\x00"719            + "\x00" * 36720        )721 722        expected = {723            "si_signo": 11,  # SIGSEGV724            "si_errno": 0,725            "si_code": 1,  # SEGV_MAPERR726            "si_addr": 0xFEDCBA9876,727            "_reason._fault._trapno": 12,728        }729        self.do_siginfo_test("remote-freebsd", "basic_eh_frame.yaml", data, expected)730