Skip to content

🍒[lldb][Commands] Fix memory find for Swift expressions #10827

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Jun 12, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
108 changes: 65 additions & 43 deletions lldb/source/Commands/CommandObjectMemory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -885,6 +885,58 @@ class CommandObjectMemoryRead : public CommandObjectParsed {
#define LLDB_OPTIONS_memory_find
#include "CommandOptions.inc"

static llvm::Error CopyExpressionResult(ValueObject &result,
DataBufferHeap &buffer,
ExecutionContextScope *scope) {
uint64_t value = result.GetValueAsUnsigned(0);
auto size_or_err = result.GetCompilerType().GetByteSize(scope);
if (!size_or_err)
return size_or_err.takeError();

switch (*size_or_err) {
case 1: {
uint8_t byte = (uint8_t)value;
buffer.CopyData(&byte, 1);
} break;
case 2: {
uint16_t word = (uint16_t)value;
buffer.CopyData(&word, 2);
} break;
case 4: {
uint32_t lword = (uint32_t)value;
buffer.CopyData(&lword, 4);
} break;
case 8: {
buffer.CopyData(&value, 8);
} break;
default:
return llvm::createStringError(
"Only expressions resulting in 1, 2, 4, or 8-byte-sized values are "
"supported. For other pattern sizes the --string (-s) option may be "
"used.");
}

return llvm::Error::success();
}

static llvm::Expected<ValueObjectSP>
EvaluateExpression(llvm::StringRef expression, StackFrame &frame,
Process &process) {
ValueObjectSP result_sp;
auto status =
process.GetTarget().EvaluateExpression(expression, &frame, result_sp);
if (status != eExpressionCompleted || !result_sp)
return llvm::createStringError(
"expression evaluation failed. pass a string instead");

result_sp = result_sp->GetQualifiedRepresentationIfAvailable(
result_sp->GetDynamicValueType(), /*synthValue=*/true);
if (!result_sp)
return llvm::createStringError("failed to get dynamic result type");

return result_sp;
}

// Find the specified data in memory
class CommandObjectMemoryFind : public CommandObjectParsed {
public:
Expand Down Expand Up @@ -1026,49 +1078,19 @@ class CommandObjectMemoryFind : public CommandObjectParsed {
}
buffer.CopyData(str);
} else if (m_memory_options.m_expr.OptionWasSet()) {
StackFrame *frame = m_exe_ctx.GetFramePtr();
ValueObjectSP result_sp;
if ((eExpressionCompleted ==
process->GetTarget().EvaluateExpression(
m_memory_options.m_expr.GetValueAs<llvm::StringRef>().value_or(
""),
frame, result_sp)) &&
result_sp) {
uint64_t value = result_sp->GetValueAsUnsigned(0);
std::optional<uint64_t> size = llvm::expectedToOptional(
result_sp->GetCompilerType().GetByteSize(nullptr));
if (!size)
return;
switch (*size) {
case 1: {
uint8_t byte = (uint8_t)value;
buffer.CopyData(&byte, 1);
} break;
case 2: {
uint16_t word = (uint16_t)value;
buffer.CopyData(&word, 2);
} break;
case 4: {
uint32_t lword = (uint32_t)value;
buffer.CopyData(&lword, 4);
} break;
case 8: {
buffer.CopyData(&value, 8);
} break;
case 3:
case 5:
case 6:
case 7:
result.AppendError("unknown type. pass a string instead");
return;
default:
result.AppendError(
"result size larger than 8 bytes. pass a string instead");
return;
}
} else {
result.AppendError(
"expression evaluation failed. pass a string instead");
auto result_or_err = EvaluateExpression(
m_memory_options.m_expr.GetValueAs<llvm::StringRef>().value_or(""),
m_exe_ctx.GetFrameRef(), *process);
if (!result_or_err) {
result.AppendError(llvm::toString(result_or_err.takeError()));
return;
}

ValueObjectSP result_sp = *result_or_err;

if (auto err = CopyExpressionResult(*result_sp, buffer,
m_exe_ctx.GetFramePtr())) {
result.AppendError(llvm::toString(std::move(err)));
return;
}
} else {
Expand Down
41 changes: 41 additions & 0 deletions lldb/test/API/functionalities/memory/find/TestMemoryFind.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,3 +79,44 @@ def test_memory_find(self):
'memory find -s "nothere" `stringdata` `stringdata+10`',
substrs=["data not found within the range."],
)

# Expression results with unsupported result types.
self.expect(
'memory find -e "ThreeBytes{}" `&bytedata[0]` `&bytedata[2]`',
substrs=[
"Only expressions resulting in 1, 2, 4, or 8-byte-sized values are supported"
],
error=True,
)

self.expect(
'memory find -e "FiveBytes{}" `&bytedata[0]` `&bytedata[2]`',
substrs=[
"Only expressions resulting in 1, 2, 4, or 8-byte-sized values are supported"
],
error=True,
)

self.expect(
'memory find -e "SixBytes{}" `&bytedata[0]` `&bytedata[2]`',
substrs=[
"Only expressions resulting in 1, 2, 4, or 8-byte-sized values are supported"
],
error=True,
)

self.expect(
'memory find -e "SevenBytes{}" `&bytedata[0]` `&bytedata[2]`',
substrs=[
"Only expressions resulting in 1, 2, 4, or 8-byte-sized values are supported"
],
error=True,
)

self.expect(
'memory find -e "NineBytes{}" `&bytedata[0]` `&bytedata[2]`',
substrs=[
"Only expressions resulting in 1, 2, 4, or 8-byte-sized values are supported"
],
error=True,
)
15 changes: 15 additions & 0 deletions lldb/test/API/functionalities/memory/find/main.cpp
Original file line number Diff line number Diff line change
@@ -1,9 +1,24 @@
#include <stdio.h>
#include <stdint.h>

template <size_t T> struct [[gnu::packed]] Payload {
uint8_t data[T];
};

using ThreeBytes = Payload<3>;
using FiveBytes = Payload<5>;
using SixBytes = Payload<5>;
using SevenBytes = Payload<7>;
using NineBytes = Payload<9>;

int main (int argc, char const *argv[])
{
const char* stringdata = "hello world; I like to write text in const char pointers";
uint8_t bytedata[] = {0xAA,0xBB,0xCC,0xDD,0xEE,0xFF,0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77,0x88,0x99};
ThreeBytes b1;
FiveBytes b2;
SixBytes b3;
SevenBytes b4;
NineBytes b5;
return 0; // break here
}
3 changes: 3 additions & 0 deletions lldb/test/API/lang/swift/command_memory_find/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
SWIFT_SOURCES := main.swift

include Makefile.rules
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
"""
Test that running Swift expressions in the
`memory find` command works.
"""
import lldb
from lldbsuite.test.lldbtest import *
from lldbsuite.test.decorators import *
import lldbsuite.test.lldbutil as lldbutil


class TestSwiftCommandMemoryFind(TestBase):
def memory_find(self, name: str, expr: str, target):
var = target.FindGlobalVariables(name, 1)
self.assertEqual(len(var), 1)
addr = var[0].AddressOf()
self.assertTrue(addr)
addr = addr.GetValueAsUnsigned()
self.expect(f'memory find -e "{expr}" {hex(addr)} {hex(addr + 8)}',
substrs=["data found at location"])

@swiftTest
def test(self):
self.build()
target, _, _, _ = lldbutil.run_to_source_breakpoint(
self, 'Break', lldb.SBFileSpec('main.swift'))

self.memory_find('elem1', 'elem1', target)
self.memory_find('elem1', '130 + 7', target)

self.memory_find('elem2', 'elem2', target)
self.memory_find('elem2', '-42', target)
3 changes: 3 additions & 0 deletions lldb/test/API/lang/swift/command_memory_find/main.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
let elem1: Int32 = 137
let elem2: Int64 = -42
print ("Break")