[lldb] Add settings for expression evaluation memory allocations.

Expression evaluation allocates memory for storing intermediate data during evaluation. For it to work properly it has to be allocated within target's available address space, for example within first 0xFFFF bytes for the 16-bit MSP430. The memory for such targets can be very tightly packed, but not all targets support GetMemoryRegionInfo API to pick an unused region, like MSP430 with MSPDebug GDB server.

These settings allow the programmer to manually pick precisely where and how much memory to allocate for expression evaluation in order not to overlap with existing data in process memory.

Reviewed By: bulbazord

Differential Revision: https://reviews.llvm.org/D149262
This commit is contained in:
Ilya Kuklin
2023-05-02 11:02:30 -07:00
committed by Anton Korobeynikov
parent 7e7bd9833b
commit 8be139fc12
10 changed files with 131 additions and 35 deletions

View File

@@ -147,6 +147,8 @@ public:
virtual bool GetPointerReturnRegister(const char *&name) { return false; }
virtual uint64_t GetStackFrameSize() { return 512 * 1024; }
static lldb::ABISP FindPlugin(lldb::ProcessSP process_sp, const ArchSpec &arch);
protected:

View File

@@ -204,6 +204,12 @@ public:
uint64_t GetExprErrorLimit() const;
uint64_t GetExprAllocAddress() const;
uint64_t GetExprAllocSize() const;
uint64_t GetExprAllocAlign() const;
bool GetUseHexImmediates() const;
bool GetUseFastStepping() const;

View File

@@ -92,26 +92,26 @@ lldb::addr_t IRMemoryMap::FindSpace(size_t size) {
ret = llvm::alignTo(addr + alloc_size, 4096);
}
uint64_t end_of_memory;
switch (GetAddressByteSize()) {
case 2:
end_of_memory = 0xffffull;
break;
case 4:
end_of_memory = 0xffffffffull;
break;
case 8:
end_of_memory = 0xffffffffffffffffull;
break;
default:
lldbassert(false && "Invalid address size.");
return LLDB_INVALID_ADDRESS;
}
// Now, if it's possible to use the GetMemoryRegionInfo API to detect mapped
// regions, walk forward through memory until a region is found that has
// adequate space for our allocation.
if (process_is_alive) {
uint64_t end_of_memory;
switch (process_sp->GetAddressByteSize()) {
case 2:
end_of_memory = 0xffffull;
break;
case 4:
end_of_memory = 0xffffffffull;
break;
case 8:
end_of_memory = 0xffffffffffffffffull;
break;
default:
lldbassert(false && "Invalid address size.");
return LLDB_INVALID_ADDRESS;
}
MemoryRegionInfo region_info;
Status err = process_sp->GetMemoryRegionInfo(ret, region_info);
if (err.Success()) {
@@ -147,29 +147,40 @@ lldb::addr_t IRMemoryMap::FindSpace(size_t size) {
// to the end of the allocations we've already reported, or use a 'sensible'
// default if this is our first allocation.
if (m_allocations.empty()) {
uint32_t address_byte_size = GetAddressByteSize();
if (address_byte_size != UINT32_MAX) {
switch (address_byte_size) {
case 2:
ret = 0x8000ull;
break;
case 4:
ret = 0xee000000ull;
break;
case 8:
ret = 0xdead0fff00000000ull;
break;
default:
lldbassert(false && "Invalid address size.");
uint64_t alloc_address = target_sp->GetExprAllocAddress();
if (alloc_address > 0) {
if (alloc_address >= end_of_memory) {
lldbassert(0 && "The allocation address for expression evaluation must "
"be within process address space");
return LLDB_INVALID_ADDRESS;
}
ret = alloc_address;
} else {
uint32_t address_byte_size = GetAddressByteSize();
if (address_byte_size != UINT32_MAX) {
switch (address_byte_size) {
case 2:
ret = 0x8000ull;
break;
case 4:
ret = 0xee000000ull;
break;
case 8:
ret = 0xdead0fff00000000ull;
break;
default:
lldbassert(false && "Invalid address size.");
return LLDB_INVALID_ADDRESS;
}
}
}
} else {
auto back = m_allocations.rbegin();
lldb::addr_t addr = back->first;
size_t alloc_size = back->second.m_size;
auto arch = target_sp->GetArchitecture().GetTriple().getArch();
auto align = arch == llvm::Triple::msp430 ? 512 : 4096;
uint64_t align = target_sp->GetExprAllocAlign();
if (align == 0)
align = 4096;
ret = llvm::alignTo(addr + alloc_size, align);
}

View File

@@ -23,6 +23,7 @@
#include "lldb/Symbol/SymbolVendor.h"
#include "lldb/Symbol/Type.h"
#include "lldb/Symbol/VariableList.h"
#include "lldb/Target/ABI.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/StackFrame.h"
@@ -34,6 +35,7 @@
#include "lldb/Utility/Log.h"
#include "lldb/Utility/StreamString.h"
using namespace lldb;
using namespace lldb_private;
char LLVMUserExpression::ID;
@@ -333,9 +335,14 @@ bool LLVMUserExpression::PrepareToExecuteJITExpression(
if (m_can_interpret && m_stack_frame_bottom == LLDB_INVALID_ADDRESS) {
Status alloc_error;
auto arch = target->GetArchitecture().GetTriple().getArch();
const size_t stack_frame_size =
arch == llvm::Triple::msp430 ? 512 : 512 * 1024;
size_t stack_frame_size = target->GetExprAllocSize();
if (stack_frame_size == 0) {
ABISP abi_sp;
if (process && (abi_sp = process->GetABI()))
stack_frame_size = abi_sp->GetStackFrameSize();
else
stack_frame_size = 512 * 1024;
}
const bool zero_memory = false;

View File

@@ -55,6 +55,8 @@ public:
const lldb_private::RegisterInfo *
GetRegisterInfoArray(uint32_t &count) override;
uint64_t GetStackFrameSize() override { return 512; }
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------

View File

@@ -4586,6 +4586,24 @@ uint64_t TargetProperties::GetExprErrorLimit() const {
g_target_properties[idx].default_uint_value);
}
uint64_t TargetProperties::GetExprAllocAddress() const {
const uint32_t idx = ePropertyExprAllocAddress;
return m_collection_sp->GetPropertyAtIndexAsUInt64(
nullptr, idx, g_target_properties[idx].default_uint_value);
}
uint64_t TargetProperties::GetExprAllocSize() const {
const uint32_t idx = ePropertyExprAllocSize;
return m_collection_sp->GetPropertyAtIndexAsUInt64(
nullptr, idx, g_target_properties[idx].default_uint_value);
}
uint64_t TargetProperties::GetExprAllocAlign() const {
const uint32_t idx = ePropertyExprAllocAlign;
return m_collection_sp->GetPropertyAtIndexAsUInt64(
nullptr, idx, g_target_properties[idx].default_uint_value);
}
bool TargetProperties::GetBreakpointsConsultPlatformAvoidList() {
const uint32_t idx = ePropertyBreakpointUseAvoidList;
return m_collection_sp->GetPropertyAtIndexAsBoolean(idx).value_or(

View File

@@ -24,6 +24,15 @@ let Definition = "target" in {
DefaultUnsignedValue<5>,
Desc<"The maximum amount of errors to emit while parsing an expression. "
"A value of 0 means to always continue parsing if possible.">;
def ExprAllocAddress: Property<"expr-alloc-address", "UInt64">,
DefaultUnsignedValue<0>,
Desc<"Start address within the process address space of memory allocation for expression evaluation.">;
def ExprAllocSize: Property<"expr-alloc-size", "UInt64">,
DefaultUnsignedValue<0>,
Desc<"Amount of memory in bytes to allocate for expression evaluation.">;
def ExprAllocAlign: Property<"expr-alloc-align", "UInt64">,
DefaultUnsignedValue<0>,
Desc<"Alignment for each memory allocation for expression evaluation.">;
def PreferDynamic: Property<"prefer-dynamic-value", "Enum">,
DefaultEnumValue<"eDynamicDontRunTarget">,
EnumValues<"OptionEnumValues(g_dynamic_value_types)">,

View File

@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp
include Makefile.rules

View File

@@ -0,0 +1,35 @@
"""
Test changing setting for expression memory allocation.
"""
import lldb
from lldbsuite.test.decorators import *
from lldbsuite.test.lldbtest import *
from lldbsuite.test import lldbutil
class TestMemoryAllocSettings(TestBase):
def test(self):
"""Test changing settings for expression memory allocation."""
self.build()
target = self.createTestTarget()
self.log_file = self.getBuildArtifact("log-expr.txt")
self.runCmd("settings set target.expr-alloc-address 0xdead0000")
self.runCmd("settings set target.expr-alloc-size 10000")
self.runCmd("settings set target.expr-alloc-align 0x1000")
self.runCmd("log enable lldb expr -f " + self.log_file)
self.runCmd("expression -- int foo; &foo")
self.assertTrue(os.path.isfile(self.log_file))
with open(self.log_file, 'r') as f:
log = f.read()
alloc0 = re.search('^.*IRMemoryMap::Malloc.+?0xdead0000.*$', log, re.MULTILINE)
# Malloc adds additional bytes to allocation size, hence 10007
alloc1 = re.search('^.*IRMemoryMap::Malloc\s*?\(10007.+?0xdead1000.*$', log, re.MULTILINE)
self.assertTrue(alloc0, "Couldn't find an allocation at a given address.")
self.assertTrue(alloc1, "Couldn't find an allocation of a given size at a given address.")

View File

@@ -0,0 +1,3 @@
int main() {
return 0;
}