blob: 0a6066aab067efd837b30bf5d0fbb60059b7e4b2 [file]
"""
Test lldb-dap memory support
"""
from base64 import b64decode
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import line_number
from lldbsuite.test.tools.lldb_dap import DAPTestCaseBase, DAPTestSession
from lldbsuite.test.tools.lldb_dap.types import LaunchArgs
class TestDAP_memory(DAPTestCaseBase):
def stop_at_breakpoint(self, session: DAPTestSession):
"""Build, launch, and stop at the `// Breakpoint` line. Returns the
top frame at that stop."""
program = self.getBuildArtifact("a.out")
source = self.getSourcePath("main.cpp")
bp_line = line_number(source, "// Breakpoint")
with session.configure(LaunchArgs(program)) as ctx:
session.resolve_source_breakpoints(source, [bp_line])
stop_event = session.verify_stopped_on_breakpoint(after=ctx.process_event)
thread_ctx = session.thread_context_from(stop_event)
return thread_ctx.top_frame()
def test_memory_refs_variables(self):
"""Tests memory references on local variables."""
session = self.build_and_create_session()
top_frame = self.stop_at_breakpoint(session)
locals = top_frame.locals
# Pointers should have memory references.
self.assertIsNotNone(locals["rawptr"].memoryReference)
# Non-pointers should also have memory references.
self.assertIsNotNone(locals["not_a_ptr"].memoryReference)
def test_memory_refs_evaluate(self):
"""Tests memory references on `evaluate` responses."""
session = self.build_and_create_session()
top_frame = self.stop_at_breakpoint(session)
eval_body = session.evaluate("rawptr", frameId=top_frame.id)
self.assertIsNotNone(eval_body.memoryReference)
def test_memory_refs_set_variable(self):
"""Tests memory references on `setVariable` responses."""
session = self.build_and_create_session()
top_frame = self.stop_at_breakpoint(session)
locals = top_frame.locals
ptr_value = locals["rawptr"].variable.value_as_int
response = locals.set("rawptr", ptr_value + 2)
response = self.expect_success(response)
self.assertIsNotNone(response.body.memoryReference)
@requireExpressionEvaluation
def test_readMemory(self):
"""Tests the `readMemory` request."""
session = self.build_and_create_session()
top_frame = self.stop_at_breakpoint(session)
eval_body = session.evaluate("*rawptr", frameId=top_frame.id)
memref = self.expect_not_none(eval_body.memoryReference)
# We can read the complete string.
response = session.read_memory(memref, count=5, offset=0).result()
data = self.expect_not_none(response.body.data)
self.assertEqual(b64decode(data), b"dead\0")
# Large reads return partial results.
response = session.read_memory(memref, count=4096, offset=0).result()
data = self.expect_not_none(response.body.data)
self.assertEqual(b64decode(data)[0:5], b"dead\0")
# Offsets work.
response = session.read_memory(memref, count=3, offset=2).result()
data = self.expect_not_none(response.body.data)
self.assertEqual(b64decode(data), b"ad\0")
# Reads of size 0 are successful.
# VSCode uses these to probe whether a memoryReference can actually be dereferenced.
response = session.read_memory(memref, count=0, offset=0).result()
self.assertIsNone(
response.body.data, f"expects no data in response: {response!r}"
)
# Reads at offset 0x0 return unreadable bytes.
bytes_to_read = 6
response = session.read_memory("0x0", count=bytes_to_read, offset=0).result()
self.assertEqual(response.body.unreadableBytes, bytes_to_read)
# Reads with an invalid address fail.
session.read_memory("-3204", count=10, offset=0).error(
"expect fail on reading memory."
)
session.continue_to_exit()
# Flakey on 32-bit Arm Linux.
@skipIf(oslist=["linux"], archs=["arm$"])
@requireExpressionEvaluation
def test_writeMemory(self):
"""Tests the `writeMemory` request."""
session = self.build_and_create_session()
top_frame = self.stop_at_breakpoint(session)
# Get `not_a_ptr`'s writable variable's memory reference.
eval_body = session.evaluate("not_a_ptr", frameId=top_frame.id)
memref = self.expect_not_none(eval_body.memoryReference)
# Write the decimal value 50 (0x32 in hexadecimal) to memory.
# This corresponds to the ASCII character '2' and encodes to base64
# as "Mg==".
response = session.write_memory(memref, value=50, offset=0, allowPartial=True)
response = self.expect_success(response)
self.assertEqual(response.body.bytesWritten, 1)
# Read back and verify.
read_response = session.read_memory(memref, count=1, offset=0).result()
self.assertEqual(read_response.body.data, "Mg==")
# Write the decimal value 100 (0x64 in hexadecimal) to memory with
# allowPartial=False. This corresponds to the ASCII character 'd' and
# encodes to base64 as "ZA==".
response = session.write_memory(memref, value=100, offset=0, allowPartial=False)
response = self.expect_success(response)
self.assertEqual(response.body.bytesWritten, 1)
# Read back and verify.
read_response = session.read_memory(memref, count=1, offset=0).result()
self.assertEqual(read_response.body.data, "ZA==")
# Writing to 0x0 fails.
response = session.write_memory("0x0", value=50, offset=0, allowPartial=True)
self.expect_error(response)
# Writing to a malformed memory reference fails.
response = session.write_memory("12345", value=50, offset=0, allowPartial=True)
self.expect_error(response)
# Writing to a non-writable region returns a not-writable error.
eval_body = session.evaluate("nonWritable", frameId=top_frame.id)
nonwritable_ref = self.expect_not_none(eval_body.memoryReference)
response = session.write_memory(
nonwritable_ref, value=50, offset=0, allowPartial=False
)
err = self.expect_error(response)
err_msg = self.expect_not_none(err.body and err.body.error)
self.assertRegex(
err_msg.format,
rf"Memory {nonwritable_ref} region is not writable",
)
# Writing an empty value (no data) fails.
response = session.write_memory(nonwritable_ref, value="")
err = self.expect_error(response)
err_msg = self.expect_not_none(err.body and err.body.error)
self.assertRegex(
err_msg.format,
r"Data cannot be empty value. Provide valid data",
)
# Large writes spanning non-writable regions fail.
data = bytes([0xFF] * 8192)
response = session.write_memory(
nonwritable_ref,
value=data,
offset=0,
allowPartial=False,
)
err = self.expect_error(response)
err_msg = self.expect_not_none(err.body and err.body.error)
self.assertRegex(
err_msg.format, rf"Memory {nonwritable_ref} region is not writable"
)
session.continue_to_exit()