blob: 1d7bbdb0c5a90999091bc6b2f72b1eb5f60c00a4 [file] [log] [blame]
"""
Test lldb-dap memory support
"""
from base64 import b64decode
import dap_server
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
import lldbdap_testcase
import os
class TestDAP_memory(lldbdap_testcase.DAPTestCaseBase):
@skipIfWindows
def test_memory_refs_variables(self):
"""
Tests memory references for evaluate
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
source = "main.cpp"
self.source_path = os.path.join(os.getcwd(), source)
self.set_source_breakpoints(
source,
[line_number(source, "// Breakpoint")],
)
self.continue_to_next_stop()
locals = {l["name"]: l for l in self.dap_server.get_local_variables()}
# Pointers should have memory-references
self.assertIn("memoryReference", locals["rawptr"].keys())
# Non-pointers should also have memory-references
self.assertIn("memoryReference", locals["not_a_ptr"].keys())
@skipIfWindows
def test_memory_refs_evaluate(self):
"""
Tests memory references for evaluate
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
source = "main.cpp"
self.source_path = os.path.join(os.getcwd(), source)
self.set_source_breakpoints(
source,
[line_number(source, "// Breakpoint")],
)
self.continue_to_next_stop()
self.assertIn(
"memoryReference",
self.dap_server.request_evaluate("rawptr")["body"].keys(),
)
@skipIfWindows
def test_memory_refs_set_variable(self):
"""
Tests memory references for `setVariable`
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
source = "main.cpp"
self.source_path = os.path.join(os.getcwd(), source)
self.set_source_breakpoints(
source,
[line_number(source, "// Breakpoint")],
)
self.continue_to_next_stop()
ptr_value = self.get_local_as_int("rawptr")
self.assertIn(
"memoryReference",
self.dap_server.request_setVariable(1, "rawptr", ptr_value + 2)[
"body"
].keys(),
)
@skipIfWindows
def test_readMemory(self):
"""
Tests the 'readMemory' request
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
source = "main.cpp"
self.source_path = os.path.join(os.getcwd(), source)
self.set_source_breakpoints(
source,
[line_number(source, "// Breakpoint")],
)
self.continue_to_next_stop()
ptr_deref = self.dap_server.request_evaluate("*rawptr")["body"]
memref = ptr_deref["memoryReference"]
# We can read the complete string
mem = self.dap_server.request_readMemory(memref, 0, 5)["body"]
self.assertEqual(b64decode(mem["data"]), b"dead\0")
# We can read large chunks, potentially returning partial results
mem = self.dap_server.request_readMemory(memref, 0, 4096)["body"]
self.assertEqual(b64decode(mem["data"])[0:5], b"dead\0")
# Use an offset
mem = self.dap_server.request_readMemory(memref, 2, 3)["body"]
self.assertEqual(b64decode(mem["data"]), b"ad\0")
# Reads of size 0 are successful
# VS Code sends those in order to check if a `memoryReference` can actually be dereferenced.
mem = self.dap_server.request_readMemory(memref, 0, 0)
self.assertEqual(mem["success"], True)
self.assertNotIn(
"data", mem["body"], f"expects no data key in response: {mem!r}"
)
# Reads at offset 0x0 return unreadable bytes
bytes_to_read = 6
mem = self.dap_server.request_readMemory("0x0", 0, bytes_to_read)
self.assertEqual(mem["body"]["unreadableBytes"], bytes_to_read)
# Reads with invalid address fails.
mem = self.dap_server.request_readMemory("-3204", 0, 10)
self.assertFalse(mem["success"], "expect fail on reading memory.")
self.continue_to_exit()
# Flakey on 32-bit Arm Linux.
@skipif(oslist=["linux"], archs=["arm$"])
def test_writeMemory(self):
"""
Tests the 'writeMemory' request
"""
program = self.getBuildArtifact("a.out")
self.build_and_launch(program)
source = "main.cpp"
self.source_path = os.path.join(os.getcwd(), source)
self.set_source_breakpoints(
source,
[line_number(source, "// Breakpoint")],
)
self.continue_to_next_stop()
# Get the 'not_a_ptr' writable variable reference address.
ptr_deref = self.dap_server.request_evaluate("not_a_ptr")["body"]
memref = ptr_deref["memoryReference"]
# Write the decimal value 50 (0x32 in hexadecimal) to memory.
# This corresponds to the ASCII character '2' and encodes to Base64 as "Mg==".
mem_response = self.writeMemory(memref, 50, 0, True)
self.assertEqual(mem_response["success"], True)
self.assertEqual(mem_response["body"]["bytesWritten"], 1)
# Read back the modified memory and verify that the written data matches
# the expected result.
mem_response = self.dap_server.request_readMemory(memref, 0, 1)
self.assertEqual(mem_response["success"], True)
self.assertEqual(mem_response["body"]["data"], "Mg==")
# Write the decimal value 100 (0x64 in hexadecimal) to memory.
# This corresponds to the ASCII character 'd' and encodes to Base64 as "ZA==".
# allowPartial=False
mem_response = self.writeMemory(memref, 100, 0, False)
self.assertEqual(mem_response["success"], True)
self.assertEqual(mem_response["body"]["bytesWritten"], 1)
# Read back the modified memory and verify that the written data matches
# the expected result.
mem_response = self.dap_server.request_readMemory(memref, 0, 1)
self.assertEqual(mem_response["success"], True)
self.assertEqual(mem_response["body"]["data"], "ZA==")
# Memory write failed for 0x0.
mem_response = self.writeMemory("0x0", 50, 0, True)
self.assertEqual(mem_response["success"], False)
# Malformed memory reference.
mem_response = self.writeMemory("12345", 50, 0, True)
self.assertEqual(mem_response["success"], False)
ptr_deref = self.dap_server.request_evaluate("nonWritable")["body"]
memref = ptr_deref["memoryReference"]
# Writing to non-writable region should return an appropriate error.
mem_response = self.writeMemory(memref, 50, 0, False)
self.assertEqual(mem_response["success"], False)
self.assertRegex(
mem_response["body"]["error"]["format"],
r"Memory " + memref + " region is not writable",
)
# Trying to write empty value; data=""
mem_response = self.writeMemory(memref)
self.assertEqual(mem_response["success"], False)
self.assertRegex(
mem_response["body"]["error"]["format"],
r"Data cannot be empty value. Provide valid data",
)
# Verify that large memory writes fail if the range spans non-writable
# or non -contiguous regions.
data = bytes([0xFF] * 8192)
mem_response = self.writeMemory(
memref, int.from_bytes(data, byteorder="little"), 0, False
)
self.assertEqual(mem_response["success"], False)
self.assertRegex(
mem_response["body"]["error"]["format"],
r"Memory " + memref + " region is not writable",
)