(based on a conversation I had with @labath yesterday in https://github.com/llvm/llvm-project/pull/106442) Most APIs that currently vend a Status would be better served by returning llvm::Expected<> instead. If possibles APIs should be refactored to avoid Status. The only legitimate long-term uses of Status are objects that need to store an error for a long time (which should be questioned as a design decision, too). This patch makes the transition to llvm::Error easier by making the places that cannot switch to llvm::Error explicit: They are marked with a call to Status::clone(). Every other API can and should be refactored to use llvm::Expected. In the end Status should only be used in very few places. Whenever an unchecked Error is dropped by Status it logs this to the verbose API channel. Implementation notes: This patch introduces two new kinds of error_category as well as new llvm::Error types. Here is the mapping of lldb::ErrorType to llvm::Errors: ``` (eErrorTypeInvalid) eErrorTypeGeneric llvm::StringError eErrorTypePOSIX llvm::ECError eErrorTypeMachKernel MachKernelError eErrorTypeExpression llvm::ErrorList<ExpressionError> eErrorTypeWin32 Win32Error ``` Relanding with built-in cloning support for llvm::ECError, and support for initializing a Windows error with a NO_ERROR error code, and modifying TestGDBRemotePlatformFile.py to support different renderings of ENOSYS.
288 lines
9.4 KiB
Python
288 lines
9.4 KiB
Python
from lldbsuite.test.gdbclientutils import *
|
|
from lldbsuite.test.decorators import *
|
|
from lldbsuite.test.lldbgdbclient import GDBPlatformClientTestBase
|
|
|
|
|
|
class TestGDBRemotePlatformFile(GDBPlatformClientTestBase):
|
|
def test_file(self):
|
|
"""Test mock operations on a remote file"""
|
|
|
|
class Responder(MockGDBServerResponder):
|
|
def vFile(self, packet):
|
|
if packet.startswith("vFile:open:"):
|
|
return "F10"
|
|
elif packet.startswith("vFile:pread:"):
|
|
return "Fd;frobnicator"
|
|
elif packet.startswith("vFile:pwrite:"):
|
|
return "Fa"
|
|
elif packet.startswith("vFile:close:"):
|
|
return "F0"
|
|
return "F-1,58"
|
|
|
|
self.server.responder = Responder()
|
|
|
|
self.match(
|
|
"platform file open /some/file.txt -v 0755", [r"File Descriptor = 16"]
|
|
)
|
|
self.match(
|
|
"platform file read 16 -o 11 -c 13",
|
|
[r"Return = 11\nData = \"frobnicator\""],
|
|
)
|
|
self.match("platform file write 16 -o 11 -d teststring", [r"Return = 10"])
|
|
self.match("platform file close 16", [r"file 16 closed."])
|
|
self.assertPacketLogContains(
|
|
[
|
|
"vFile:open:2f736f6d652f66696c652e747874,00000202,000001ed",
|
|
"vFile:pread:10,d,b",
|
|
"vFile:pwrite:10,b,teststring",
|
|
"vFile:close:10",
|
|
]
|
|
)
|
|
|
|
def test_file_fail(self):
|
|
"""Test mocked failures of remote operations"""
|
|
|
|
class Responder(MockGDBServerResponder):
|
|
def vFile(self, packet):
|
|
# use ENOSYS as this constant differs between GDB Remote
|
|
# Protocol and Linux, so we can test the translation
|
|
return "F-1,58"
|
|
|
|
self.server.responder = Responder()
|
|
enosys_regex = r"error: (Function not implemented|function not supported)"
|
|
self.match(
|
|
"platform file open /some/file.txt -v 0755",
|
|
[enosys_regex],
|
|
error=True,
|
|
)
|
|
self.match(
|
|
"platform file read 16 -o 11 -c 13",
|
|
[enosys_regex],
|
|
error=True,
|
|
)
|
|
self.match(
|
|
"platform file write 16 -o 11 -d teststring",
|
|
[enosys_regex],
|
|
error=True,
|
|
)
|
|
self.match("platform file close 16", [enosys_regex], error=True)
|
|
self.assertPacketLogContains(
|
|
[
|
|
"vFile:open:2f736f6d652f66696c652e747874,00000202,000001ed",
|
|
"vFile:pread:10,d,b",
|
|
"vFile:pwrite:10,b,teststring",
|
|
"vFile:close:10",
|
|
]
|
|
)
|
|
|
|
def test_file_size(self):
|
|
"""Test 'platform get-size'"""
|
|
|
|
class Responder(MockGDBServerResponder):
|
|
def vFile(self, packet):
|
|
return "F1000"
|
|
|
|
self.server.responder = Responder()
|
|
|
|
self.match(
|
|
"platform get-size /some/file.txt",
|
|
[r"File size of /some/file\.txt \(remote\): 4096"],
|
|
)
|
|
self.assertPacketLogContains(
|
|
[
|
|
"vFile:size:2f736f6d652f66696c652e747874",
|
|
]
|
|
)
|
|
|
|
def test_file_size_fallback(self):
|
|
"""Test 'platform get-size fallback to vFile:fstat'"""
|
|
|
|
class Responder(MockGDBServerResponder):
|
|
def vFile(self, packet):
|
|
if packet.startswith("vFile:open:"):
|
|
return "F5"
|
|
elif packet.startswith("vFile:fstat:"):
|
|
return "F40;" + 28 * "\0" + "\0\0\0\0\0\1\2\3" + 28 * "\0"
|
|
if packet.startswith("vFile:close:"):
|
|
return "F0"
|
|
return ""
|
|
|
|
self.server.responder = Responder()
|
|
|
|
self.match(
|
|
"platform get-size /some/file.txt",
|
|
[r"File size of /some/file\.txt \(remote\): 66051"],
|
|
)
|
|
self.assertPacketLogContains(
|
|
[
|
|
"vFile:size:2f736f6d652f66696c652e747874",
|
|
"vFile:open:2f736f6d652f66696c652e747874,00000000,00000000",
|
|
"vFile:fstat:5",
|
|
"vFile:close:5",
|
|
]
|
|
)
|
|
|
|
self.runCmd("platform disconnect")
|
|
|
|
# For a new onnection, we should attempt vFile:size once again.
|
|
server2 = MockGDBServer(self.server_socket_class())
|
|
server2.responder = Responder()
|
|
server2.start()
|
|
self.addTearDownHook(lambda: server2.stop())
|
|
self.runCmd("platform connect " + server2.get_connect_url())
|
|
self.match(
|
|
"platform get-size /other/file.txt",
|
|
[r"File size of /other/file\.txt \(remote\): 66051"],
|
|
)
|
|
|
|
self.assertPacketLogContains(
|
|
[
|
|
"vFile:size:2f6f746865722f66696c652e747874",
|
|
"vFile:open:2f6f746865722f66696c652e747874,00000000,00000000",
|
|
"vFile:fstat:5",
|
|
"vFile:close:5",
|
|
],
|
|
log=server2.responder.packetLog,
|
|
)
|
|
|
|
@expectedFailureAll(
|
|
hostoslist=["windows"], bugnumber="github.com/llvm/llvm-project/issues/92255"
|
|
)
|
|
def test_file_permissions(self):
|
|
"""Test 'platform get-permissions'"""
|
|
|
|
class Responder(MockGDBServerResponder):
|
|
def vFile(self, packet):
|
|
return "F1a4"
|
|
|
|
self.server.responder = Responder()
|
|
|
|
self.match(
|
|
"platform get-permissions /some/file.txt",
|
|
[r"File permissions of /some/file\.txt \(remote\): 0o0644"],
|
|
)
|
|
self.assertPacketLogContains(
|
|
[
|
|
"vFile:mode:2f736f6d652f66696c652e747874",
|
|
]
|
|
)
|
|
|
|
@expectedFailureAll(
|
|
hostoslist=["windows"], bugnumber="github.com/llvm/llvm-project/issues/92255"
|
|
)
|
|
def test_file_permissions_fallback(self):
|
|
"""Test 'platform get-permissions' fallback to fstat"""
|
|
|
|
class Responder(MockGDBServerResponder):
|
|
def vFile(self, packet):
|
|
if packet.startswith("vFile:open:"):
|
|
return "F5"
|
|
elif packet.startswith("vFile:fstat:"):
|
|
return "F40;" + 8 * "\0" + "\0\0\1\xA4" + 52 * "\0"
|
|
if packet.startswith("vFile:close:"):
|
|
return "F0"
|
|
return ""
|
|
|
|
self.server.responder = Responder()
|
|
|
|
try:
|
|
self.match(
|
|
"platform get-permissions /some/file.txt",
|
|
[r"File permissions of /some/file\.txt \(remote\): 0o0644"],
|
|
)
|
|
self.assertPacketLogContains(
|
|
[
|
|
"vFile:mode:2f736f6d652f66696c652e747874",
|
|
"vFile:open:2f736f6d652f66696c652e747874,00000000,00000000",
|
|
"vFile:fstat:5",
|
|
"vFile:close:5",
|
|
]
|
|
)
|
|
finally:
|
|
self.dbg.GetSelectedPlatform().DisconnectRemote()
|
|
|
|
def test_file_exists(self):
|
|
"""Test 'platform file-exists'"""
|
|
|
|
class Responder(MockGDBServerResponder):
|
|
def vFile(self, packet):
|
|
return "F,1"
|
|
|
|
self.server.responder = Responder()
|
|
|
|
self.match(
|
|
"platform file-exists /some/file.txt",
|
|
[r"File /some/file\.txt \(remote\) exists"],
|
|
)
|
|
self.assertPacketLogContains(
|
|
[
|
|
"vFile:exists:2f736f6d652f66696c652e747874",
|
|
]
|
|
)
|
|
|
|
def test_file_exists_not(self):
|
|
"""Test 'platform file-exists' with non-existing file"""
|
|
|
|
class Responder(MockGDBServerResponder):
|
|
def vFile(self, packet):
|
|
return "F,0"
|
|
|
|
self.server.responder = Responder()
|
|
|
|
self.match(
|
|
"platform file-exists /some/file.txt",
|
|
[r"File /some/file\.txt \(remote\) does not exist"],
|
|
)
|
|
self.assertPacketLogContains(
|
|
[
|
|
"vFile:exists:2f736f6d652f66696c652e747874",
|
|
]
|
|
)
|
|
|
|
def test_file_exists_fallback(self):
|
|
"""Test 'platform file-exists' fallback to open"""
|
|
|
|
class Responder(MockGDBServerResponder):
|
|
def vFile(self, packet):
|
|
if packet.startswith("vFile:open:"):
|
|
return "F5"
|
|
if packet.startswith("vFile:close:"):
|
|
return "F0"
|
|
return ""
|
|
|
|
self.server.responder = Responder()
|
|
|
|
self.match(
|
|
"platform file-exists /some/file.txt",
|
|
[r"File /some/file\.txt \(remote\) exists"],
|
|
)
|
|
self.assertPacketLogContains(
|
|
[
|
|
"vFile:exists:2f736f6d652f66696c652e747874",
|
|
"vFile:open:2f736f6d652f66696c652e747874,00000000,00000000",
|
|
"vFile:close:5",
|
|
]
|
|
)
|
|
|
|
def test_file_exists_not_fallback(self):
|
|
"""Test 'platform file-exists' fallback to open with non-existing file"""
|
|
|
|
class Responder(MockGDBServerResponder):
|
|
def vFile(self, packet):
|
|
if packet.startswith("vFile:open:"):
|
|
return "F-1,2"
|
|
return ""
|
|
|
|
self.server.responder = Responder()
|
|
|
|
self.match(
|
|
"platform file-exists /some/file.txt",
|
|
[r"File /some/file\.txt \(remote\) does not exist"],
|
|
)
|
|
self.assertPacketLogContains(
|
|
[
|
|
"vFile:exists:2f736f6d652f66696c652e747874",
|
|
"vFile:open:2f736f6d652f66696c652e747874,00000000,00000000",
|
|
]
|
|
)
|