1# lldb test suite imports 2from lldbsuite.test.decorators import * 3from lldbsuite.test.lldbtest import TestBase 4from lldbsuite.test import lldbutil 5 6# gdb-remote-specific imports 7import lldbgdbserverutils 8from gdbremote_testcase import GdbRemoteTestCaseBase 9 10import binascii 11import os 12import stat 13import struct 14import typing 15 16 17class GDBStat(typing.NamedTuple): 18 st_dev: int 19 st_ino: int 20 st_mode: int 21 st_nlink: int 22 st_uid: int 23 st_gid: int 24 st_rdev: int 25 st_size: int 26 st_blksize: int 27 st_blocks: int 28 st_atime: int 29 st_mtime: int 30 st_ctime: int 31 32 33def uint32_or_zero(x): 34 return x if x < 2**32 and x >= 0 else 0 35 36 37def uint32_or_max(x): 38 return x if x < 2**32 and x >= 0 else 2**32 - 1 39 40 41def uint32_trunc(x): 42 return x & (2**32 - 1) 43 44 45class TestGdbRemotePlatformFile(GdbRemoteTestCaseBase): 46 @skipIfWindows 47 @add_test_categories(["llgs"]) 48 def test_platform_file_rdonly(self): 49 self.vFile_test(read=True) 50 51 @skipIfWindows 52 @add_test_categories(["llgs"]) 53 def test_platform_file_wronly(self): 54 self.vFile_test(write=True) 55 56 @skipIfWindows 57 @add_test_categories(["llgs"]) 58 def test_platform_file_rdwr(self): 59 self.vFile_test(read=True, write=True) 60 61 @skipIfWindows 62 @add_test_categories(["llgs"]) 63 def test_platform_file_wronly_append(self): 64 self.vFile_test(write=True, append=True) 65 66 @skipIfWindows 67 @add_test_categories(["llgs"]) 68 def test_platform_file_rdwr_append(self): 69 self.vFile_test(read=True, write=True, append=True) 70 71 @skipIfWindows 72 @add_test_categories(["llgs"]) 73 def test_platform_file_wronly_trunc(self): 74 self.vFile_test(write=True, trunc=True) 75 76 @skipIfWindows 77 @add_test_categories(["llgs"]) 78 def test_platform_file_rdwr_trunc(self): 79 self.vFile_test(read=True, write=True, trunc=True) 80 81 @skipIfWindows 82 @add_test_categories(["llgs"]) 83 def test_platform_file_wronly_creat(self): 84 self.vFile_test(write=True, creat=True) 85 86 @skipIfWindows 87 @add_test_categories(["llgs"]) 88 def test_platform_file_wronly_creat_excl(self): 89 self.vFile_test(write=True, creat=True, excl=True) 90 91 @skipIfWindows 92 @add_test_categories(["llgs"]) 93 def test_platform_file_wronly_fail(self): 94 server = self.connect_to_debug_monitor() 95 self.assertIsNotNone(server) 96 97 temp_path = self.getBuildArtifact("test") 98 self.assertFalse(os.path.exists(temp_path)) 99 100 # attempt to open the file without O_CREAT 101 self.do_handshake() 102 self.test_sequence.add_log_lines( 103 [ 104 "read packet: $vFile:open:%s,1,0#00" 105 % (binascii.b2a_hex(temp_path.encode()).decode(),), 106 {"direction": "send", "regex": r"^\$F-1,[0-9a-fA-F]+#[0-9a-fA-F]{2}$"}, 107 ], 108 True, 109 ) 110 self.expect_gdbremote_sequence() 111 112 @skipIfWindows 113 @add_test_categories(["llgs"]) 114 def test_platform_file_wronly_creat_excl_fail(self): 115 server = self.connect_to_debug_monitor() 116 self.assertIsNotNone(server) 117 118 temp_file = self.getBuildArtifact("test") 119 with open(temp_file, "wb"): 120 pass 121 temp_file = lldbutil.install_to_target(self, temp_file) 122 123 # attempt to open the file with O_CREAT|O_EXCL 124 self.do_handshake() 125 self.test_sequence.add_log_lines( 126 [ 127 "read packet: $vFile:open:%s,a01,0#00" 128 % (binascii.b2a_hex(temp_file.encode()).decode(),), 129 {"direction": "send", "regex": r"^\$F-1,[0-9a-fA-F]+#[0-9a-fA-F]{2}$"}, 130 ], 131 True, 132 ) 133 self.expect_gdbremote_sequence() 134 135 @skipIfWindows 136 @add_test_categories(["llgs"]) 137 def test_platform_file_size(self): 138 server = self.connect_to_debug_monitor() 139 self.assertIsNotNone(server) 140 141 temp_path = self.getBuildArtifact("test") 142 test_data = b"test data of some length" 143 with open(temp_path, "wb") as temp_file: 144 temp_file.write(test_data) 145 temp_path = lldbutil.install_to_target(self, temp_path) 146 147 self.do_handshake() 148 self.test_sequence.add_log_lines( 149 [ 150 "read packet: $vFile:size:%s#00" 151 % (binascii.b2a_hex(temp_path.encode()).decode(),), 152 { 153 "direction": "send", 154 "regex": r"^\$F([0-9a-fA-F]+)+#[0-9a-fA-F]{2}$", 155 "capture": {1: "size"}, 156 }, 157 ], 158 True, 159 ) 160 context = self.expect_gdbremote_sequence() 161 self.assertEqual(int(context["size"], 16), len(test_data)) 162 163 @skipIfWindows 164 @add_test_categories(["llgs"]) 165 def test_platform_file_mode(self): 166 server = self.connect_to_debug_monitor() 167 self.assertIsNotNone(server) 168 169 temp_path = self.getBuildArtifact("test") 170 test_mode = 0o751 171 172 with open(temp_path, "wb") as temp_file: 173 if lldbplatformutil.getHostPlatform() == "windows": 174 test_mode = 0o700 175 else: 176 os.chmod(temp_file.fileno(), test_mode) 177 temp_path = lldbutil.install_to_target(self, temp_path) 178 179 self.do_handshake() 180 self.test_sequence.add_log_lines( 181 [ 182 "read packet: $vFile:mode:%s#00" 183 % (binascii.b2a_hex(temp_path.encode()).decode(),), 184 { 185 "direction": "send", 186 "regex": r"^\$F([0-9a-fA-F]+)+#[0-9a-fA-F]{2}$", 187 "capture": {1: "mode"}, 188 }, 189 ], 190 True, 191 ) 192 context = self.expect_gdbremote_sequence() 193 self.assertEqual(int(context["mode"], 16), test_mode) 194 195 @skipIfWindows 196 @add_test_categories(["llgs"]) 197 def test_platform_file_mode_fail(self): 198 server = self.connect_to_debug_monitor() 199 self.assertIsNotNone(server) 200 201 temp_path = self.getBuildArtifact("nonexist") 202 203 self.do_handshake() 204 self.test_sequence.add_log_lines( 205 [ 206 "read packet: $vFile:mode:%s#00" 207 % (binascii.b2a_hex(temp_path.encode()).decode(),), 208 {"direction": "send", "regex": r"^\$F-1,0*2+#[0-9a-fA-F]{2}$"}, 209 ], 210 True, 211 ) 212 self.expect_gdbremote_sequence() 213 214 @skipIfWindows 215 @add_test_categories(["llgs"]) 216 def test_platform_file_exists(self): 217 server = self.connect_to_debug_monitor() 218 self.assertIsNotNone(server) 219 220 temp_path = self.getBuildArtifact("test") 221 with open(temp_path, "wb"): 222 pass 223 temp_path = lldbutil.install_to_target(self, temp_path) 224 225 self.do_handshake() 226 self.test_sequence.add_log_lines( 227 [ 228 "read packet: $vFile:exists:%s#00" 229 % (binascii.b2a_hex(temp_path.encode()).decode(),), 230 "send packet: $F,1#00", 231 ], 232 True, 233 ) 234 self.expect_gdbremote_sequence() 235 236 @skipIfWindows 237 @add_test_categories(["llgs"]) 238 def test_platform_file_exists_not(self): 239 server = self.connect_to_debug_monitor() 240 self.assertIsNotNone(server) 241 242 test_path = self.getBuildArtifact("nonexist") 243 self.do_handshake() 244 self.test_sequence.add_log_lines( 245 [ 246 "read packet: $vFile:exists:%s#00" 247 % (binascii.b2a_hex(test_path.encode()).decode(),), 248 "send packet: $F,0#00", 249 ], 250 True, 251 ) 252 self.expect_gdbremote_sequence() 253 254 @skipIfWindows 255 # FIXME: lldb.remote_platform.Install() cannot copy opened temp file on Windows. 256 # It is possible to use tempfile.NamedTemporaryFile(..., delete=False) and 257 # delete the temp file manually at the end. 258 @skipIf(hostoslist=["windows"]) 259 @add_test_categories(["llgs"]) 260 def test_platform_file_fstat(self): 261 server = self.connect_to_debug_monitor() 262 self.assertIsNotNone(server) 263 264 with tempfile.NamedTemporaryFile() as temp_file: 265 temp_file.write(b"some test data for stat") 266 temp_file.flush() 267 temp_path = lldbutil.install_to_target(self, temp_file.name) 268 269 self.do_handshake() 270 self.test_sequence.add_log_lines( 271 [ 272 "read packet: $vFile:open:%s,0,0#00" 273 % (binascii.b2a_hex(temp_path.encode()).decode(),), 274 { 275 "direction": "send", 276 "regex": r"^\$F([0-9a-fA-F]+)#[0-9a-fA-F]{2}$", 277 "capture": {1: "fd"}, 278 }, 279 ], 280 True, 281 ) 282 283 context = self.expect_gdbremote_sequence() 284 self.assertIsNotNone(context) 285 fd = int(context["fd"], 16) 286 287 self.reset_test_sequence() 288 self.test_sequence.add_log_lines( 289 [ 290 "read packet: $vFile:fstat:%x#00" % (fd,), 291 { 292 "direction": "send", 293 "regex": r"^\$F([0-9a-fA-F]+);(.*)#[0-9a-fA-F]{2}$", 294 "capture": {1: "size", 2: "data"}, 295 }, 296 ], 297 True, 298 ) 299 context = self.expect_gdbremote_sequence() 300 self.assertEqual(int(context["size"], 16), 64) 301 # NB: we're using .encode() as a hack because the test suite 302 # is wrongly using (unicode) str instead of bytes 303 gdb_stat = GDBStat( 304 *struct.unpack( 305 ">IIIIIIIQQQIII", 306 self.decode_gdbremote_binary(context["data"]).encode("iso-8859-1"), 307 ) 308 ) 309 sys_stat = os.fstat(temp_file.fileno()) 310 311 self.assertEqual(gdb_stat.st_mode, uint32_trunc(sys_stat.st_mode)) 312 self.assertEqual(gdb_stat.st_nlink, uint32_or_max(sys_stat.st_nlink)) 313 self.assertEqual(gdb_stat.st_rdev, uint32_or_zero(sys_stat.st_rdev)) 314 self.assertEqual(gdb_stat.st_size, sys_stat.st_size) 315 if not lldb.remote_platform: 316 self.assertEqual(gdb_stat.st_dev, uint32_or_zero(sys_stat.st_dev)) 317 self.assertEqual(gdb_stat.st_ino, uint32_or_zero(sys_stat.st_ino)) 318 self.assertEqual(gdb_stat.st_uid, uint32_or_zero(sys_stat.st_uid)) 319 self.assertEqual(gdb_stat.st_gid, uint32_or_zero(sys_stat.st_gid)) 320 self.assertEqual(gdb_stat.st_blksize, sys_stat.st_blksize) 321 self.assertEqual(gdb_stat.st_blocks, sys_stat.st_blocks) 322 self.assertEqual( 323 gdb_stat.st_atime, uint32_or_zero(int(sys_stat.st_atime)) 324 ) 325 self.assertEqual( 326 gdb_stat.st_mtime, uint32_or_zero(int(sys_stat.st_mtime)) 327 ) 328 self.assertEqual( 329 gdb_stat.st_ctime, uint32_or_zero(int(sys_stat.st_ctime)) 330 ) 331 332 self.reset_test_sequence() 333 self.test_sequence.add_log_lines( 334 ["read packet: $vFile:close:%x#00" % (fd,), "send packet: $F0#00"], True 335 ) 336 self.expect_gdbremote_sequence() 337 338 def expect_error(self): 339 self.test_sequence.add_log_lines( 340 [{"direction": "send", "regex": r"^\$F-1,[0-9a-fA-F]+#[0-9a-fA-F]{2}$"}], 341 True, 342 ) 343 self.expect_gdbremote_sequence() 344 345 def vFile_test( 346 self, 347 read=False, 348 write=False, 349 append=False, 350 trunc=False, 351 creat=False, 352 excl=False, 353 ): 354 if read and write: 355 mode = 2 356 elif write: 357 mode = 1 358 else: # read 359 mode = 0 360 if append: 361 mode |= 8 362 if creat: 363 mode |= 0x200 364 if trunc: 365 mode |= 0x400 366 if excl: 367 mode |= 0x800 368 369 old_umask = os.umask(0o22) 370 try: 371 server = self.connect_to_debug_monitor() 372 finally: 373 os.umask(old_umask) 374 self.assertIsNotNone(server) 375 376 # create a temporary file with some data 377 temp_path = self.getBuildArtifact("test") 378 test_data = "some test data longer than 16 bytes\n" 379 380 if creat: 381 self.assertFalse(os.path.exists(temp_path)) 382 if lldb.remote_platform: 383 temp_path = lldbutil.append_to_process_working_directory(self, "test") 384 else: 385 with open(temp_path, "wb") as temp_file: 386 temp_file.write(test_data.encode()) 387 temp_path = lldbutil.install_to_target(self, temp_path) 388 389 # open the file for reading 390 self.do_handshake() 391 self.test_sequence.add_log_lines( 392 [ 393 "read packet: $vFile:open:%s,%x,1a0#00" 394 % (binascii.b2a_hex(temp_path.encode()).decode(), mode), 395 { 396 "direction": "send", 397 "regex": r"^\$F([0-9a-fA-F]+)#[0-9a-fA-F]{2}$", 398 "capture": {1: "fd"}, 399 }, 400 ], 401 True, 402 ) 403 404 context = self.expect_gdbremote_sequence() 405 self.assertIsNotNone(context) 406 fd = int(context["fd"], 16) 407 408 # read data from the file 409 self.reset_test_sequence() 410 self.test_sequence.add_log_lines( 411 ["read packet: $vFile:pread:%x,11,10#00" % (fd,)], True 412 ) 413 if read: 414 self.test_sequence.add_log_lines( 415 [ 416 { 417 "direction": "send", 418 "regex": r"^\$F([0-9a-fA-F]+);(.*)#[0-9a-fA-F]{2}$", 419 "capture": {1: "size", 2: "data"}, 420 } 421 ], 422 True, 423 ) 424 context = self.expect_gdbremote_sequence() 425 self.assertIsNotNone(context) 426 if trunc: 427 self.assertEqual(context["size"], "0") 428 self.assertEqual(context["data"], "") 429 else: 430 self.assertEqual(context["size"], "11") # hex 431 self.assertEqual(context["data"], test_data[0x10 : 0x10 + 0x11]) 432 else: 433 self.expect_error() 434 435 # another offset 436 if read and not trunc: 437 self.reset_test_sequence() 438 self.test_sequence.add_log_lines( 439 [ 440 "read packet: $vFile:pread:%x,6,3#00" % (fd,), 441 { 442 "direction": "send", 443 "regex": r"^\$F([0-9a-fA-F]+);(.+)#[0-9a-fA-F]{2}$", 444 "capture": {1: "size", 2: "data"}, 445 }, 446 ], 447 True, 448 ) 449 context = self.expect_gdbremote_sequence() 450 self.assertIsNotNone(context) 451 self.assertEqual(context["size"], "6") # hex 452 self.assertEqual(context["data"], test_data[3 : 3 + 6]) 453 454 # write data to the file 455 self.reset_test_sequence() 456 self.test_sequence.add_log_lines( 457 ["read packet: $vFile:pwrite:%x,6,somedata#00" % (fd,)], True 458 ) 459 if write: 460 self.test_sequence.add_log_lines(["send packet: $F8#00"], True) 461 self.expect_gdbremote_sequence() 462 else: 463 self.expect_error() 464 465 # close the file 466 self.reset_test_sequence() 467 self.test_sequence.add_log_lines( 468 ["read packet: $vFile:close:%x#00" % (fd,), "send packet: $F0#00"], True 469 ) 470 self.expect_gdbremote_sequence() 471 472 if write: 473 # check if the data was actually written 474 if lldb.remote_platform: 475 local_path = self.getBuildArtifact("file_from_target") 476 error = lldb.remote_platform.Get( 477 lldb.SBFileSpec(temp_path, False), lldb.SBFileSpec(local_path, True) 478 ) 479 self.assertTrue( 480 error.Success(), 481 "Reading file {0} failed: {1}".format(temp_path, error), 482 ) 483 temp_path = local_path 484 485 with open(temp_path, "rb") as temp_file: 486 if creat and lldbplatformutil.getHostPlatform() != "windows": 487 self.assertEqual( 488 os.fstat(temp_file.fileno()).st_mode & 0o7777, 0o640 489 ) 490 data = test_data.encode() 491 if trunc or creat: 492 data = b"\0" * 6 + b"somedata" 493 elif append: 494 data += b"somedata" 495 else: 496 data = data[:6] + b"somedata" + data[6 + 8 :] 497 self.assertEqual(temp_file.read(), data) 498