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