Skip to content
Open
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
10 changes: 10 additions & 0 deletions lldb/bindings/interface/SBValueDocstrings.i
Original file line number Diff line number Diff line change
Expand Up @@ -207,3 +207,13 @@ linked list."

%feature("docstring", "Returns an expression path for this value."
) lldb::SBValue::GetExpressionPath;

%feature("docstring", "
Returns whether this value can be modified through SetValueFromCString()
or SetData().
Returns False when the value is not writable. An example would be a
variable values reconstructed from debug info via a computation or a constant.
A True result does not guarantee a write will succeed; other
runtime conditions may still prevent a successful write."
) lldb::SBValue::CanSetValue;
39 changes: 39 additions & 0 deletions lldb/bindings/python/static-binding/LLDBWrapPython.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -93547,6 +93547,34 @@ SWIGINTERN PyObject *_wrap_SBValue_SetValueFromCString(PyObject *self, PyObject
}


SWIGINTERN PyObject *_wrap_SBValue_CanSetValue(PyObject *self, PyObject *args) {
PyObject *resultobj = 0;
lldb::SBValue *arg1 = 0 ;
void *argp1 = 0 ;
int res1 = 0 ;
PyObject *swig_obj[1] ;
bool result;

(void)self;
if (!args) SWIG_fail;
swig_obj[0] = args;
res1 = SWIG_ConvertPtr(swig_obj[0], &argp1,SWIGTYPE_p_lldb__SBValue, 0 | 0 );
if (!SWIG_IsOK(res1)) {
SWIG_exception_fail(SWIG_ArgError(res1), "in method '" "SBValue_CanSetValue" "', argument " "1"" of type '" "lldb::SBValue *""'");
}
arg1 = reinterpret_cast< lldb::SBValue * >(argp1);
{
SWIG_PYTHON_THREAD_BEGIN_ALLOW;
result = (bool)(arg1)->CanSetValue();
SWIG_PYTHON_THREAD_END_ALLOW;
}
resultobj = SWIG_From_bool(static_cast< bool >(result));
return resultobj;
fail:
return NULL;
}


SWIGINTERN PyObject *_wrap_SBValue_GetTypeFormat(PyObject *self, PyObject *args) {
PyObject *resultobj = 0;
lldb::SBValue *arg1 = 0 ;
Expand Down Expand Up @@ -104703,6 +104731,17 @@ static PyMethodDef SwigMethods[] = {
"SBValue_SetValueFromCString(SBValue self, char const * value_str) -> bool\n"
"SBValue_SetValueFromCString(SBValue self, char const * value_str, SBError error) -> bool\n"
""},
{ "SBValue_CanSetValue", _wrap_SBValue_CanSetValue, METH_O, "\n"
"SBValue_CanSetValue(SBValue self) -> bool\n"
"\n"
" Returns whether this value can be modified through SetValueFromCString()\n"
" or SetData().\n"
"\n"
" Returns False when the value is not writable. An example would be a\n"
" variable values reconstructed from debug info via a computation or a constant.\n"
" A True result does not guarantee a write will succeed; other\n"
" runtime conditions may still prevent a successful write.\n"
""},
{ "SBValue_GetTypeFormat", _wrap_SBValue_GetTypeFormat, METH_O, "SBValue_GetTypeFormat(SBValue self) -> SBTypeFormat"},
{ "SBValue_GetTypeSummary", _wrap_SBValue_GetTypeSummary, METH_O, "SBValue_GetTypeSummary(SBValue self) -> SBTypeSummary"},
{ "SBValue_GetTypeFilter", _wrap_SBValue_GetTypeFilter, METH_O, "SBValue_GetTypeFilter(SBValue self) -> SBTypeFilter"},
Expand Down
14 changes: 14 additions & 0 deletions lldb/bindings/python/static-binding/lldb.py
Original file line number Diff line number Diff line change
Expand Up @@ -18079,6 +18079,20 @@ def SetValueFromCString(self, *args):
"""
return _lldb.SBValue_SetValueFromCString(self, *args)

def CanSetValue(self):
r"""
CanSetValue(SBValue self) -> bool

Returns whether this value can be modified through SetValueFromCString()
or SetData().

Returns False when the value is not writable. An example would be a
variable values reconstructed from debug info via a computation or a constant.
A True result does not guarantee a write will succeed; other
runtime conditions may still prevent a successful write.
"""
return _lldb.SBValue_CanSetValue(self)

def GetTypeFormat(self):
r"""GetTypeFormat(SBValue self) -> SBTypeFormat"""
return _lldb.SBValue_GetTypeFormat(self)
Expand Down
8 changes: 8 additions & 0 deletions lldb/include/lldb/API/SBValue.h
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,14 @@ class LLDB_API SBValue {

bool SetValueFromCString(const char *value_str, lldb::SBError &error);

/// Returns false if this value cannot be modified through
/// SetValueFromCString() or SetData(), for instance because it
/// exists in the target, but has no writable storage (for example a
/// constant or variable value that was reconstructed from debug
/// info as the result of a computation). A true result does not
/// guarantee a write will succeed.
bool CanSetValue();

lldb::SBTypeFormat GetTypeFormat();

lldb::SBTypeSummary GetTypeSummary();
Expand Down
6 changes: 6 additions & 0 deletions lldb/include/lldb/Expression/DWARFExpression.h
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,12 @@ class DWARFExpression {

bool ContainsThreadLocalStorage(const Delegate *dwarf_cu) const;

/// Return true if this expression produces a DWARF implicit or
/// composite location description that LLDB cannot write a new
/// value back to. Scans the opcodes without evaluating the
/// expression.
bool IsImplicit(const Delegate *dwarf_cu) const;

bool LinkThreadLocalStorage(
const Delegate *dwarf_cu,
std::function<lldb::addr_t(lldb::addr_t file_addr)> const
Expand Down
13 changes: 13 additions & 0 deletions lldb/include/lldb/Expression/DWARFExpressionList.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,20 @@ class DWARFExpressionList {
const Value *initial_value_ptr,
const Value *object_address_ptr) const;

/// Return true if the expression in scope at the current PC produces an
/// implicit location, or if no location is in scope. See
/// DWARFExpression::IsImplicit. \p exe_ctx and \p reg_ctx may be null for an
/// always-valid single expression.
bool IsImplicit(ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
lldb::addr_t func_load_addr) const;

private:
/// Return the expression in scope at the current PC, or an error if no
/// location is in scope.
llvm::Expected<const DWARFExpression *>
GetExpressionAtPC(ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
lldb::addr_t func_load_addr) const;

// RangeDataVector requires a comparator for DWARFExpression, but it doesn't
// make sense to do so.
struct DWARFExpressionCompare {
Expand Down
8 changes: 8 additions & 0 deletions lldb/include/lldb/ValueObject/ValueObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,14 @@ class ValueObject {

virtual bool GetIsConstant() const { return m_update_point.IsConstant(); }

/// Returns false when this value cannot be modified through
/// SetValueFromCString() or SetData() because it exists in the
/// target but has no writable storage, e.g., a constant or a
/// computed variable value. A true result does not guarantee a
/// write will succeed; other runtime conditions can still cause
/// SetValue* to fail.
virtual bool CanSetValue() { return !GetIsConstant(); }

bool NeedsUpdating() {
const bool accept_invalid_exe_ctx =
(CanUpdateWithInvalidExecutionContext() == eLazyBoolYes);
Expand Down
7 changes: 7 additions & 0 deletions lldb/include/lldb/ValueObject/ValueObjectVariable.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,8 @@ class ValueObjectVariable : public ValueObject {

bool SetData(DataExtractor &data, Status &error) override;

bool CanSetValue() override;

lldb::VariableSP GetVariable() override { return m_variable_sp; }

protected:
Expand All @@ -80,6 +82,11 @@ class ValueObjectVariable : public ValueObject {
/// it up.
Value m_resolved_value;

/// True when the resolved value has no writable storage in the inferior (an
/// implicit location, a constant, or an unresolved location) and so cannot be
/// the target of SetValueFromCString() or SetData(). Set by UpdateValue().
bool m_resolved_value_is_implicit = false;

private:
ValueObjectVariable(ExecutionContextScope *exe_scope,
ValueObjectManager &manager,
Expand Down
11 changes: 11 additions & 0 deletions lldb/source/API/SBValue.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,17 @@ bool SBValue::SetValueFromCString(const char *value_str, lldb::SBError &error) {
return success;
}

bool SBValue::CanSetValue() {
LLDB_INSTRUMENT_VA(this);

ValueLocker locker;
lldb::ValueObjectSP value_sp(GetSP(locker));
if (!value_sp)
return false;

return value_sp->CanSetValue();
}

lldb::SBTypeFormat SBValue::GetTypeFormat() {
LLDB_INSTRUMENT_VA(this);

Expand Down
30 changes: 30 additions & 0 deletions lldb/source/Expression/DWARFExpression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,36 @@ bool DWARFExpression::ContainsThreadLocalStorage(
}
return false;
}

bool DWARFExpression::IsImplicit(
const DWARFExpression::Delegate *dwarf_cu) const {
lldb::offset_t offset = 0;
while (m_data.ValidOffset(offset)) {
const LocationAtom op = static_cast<LocationAtom>(m_data.GetU8(&offset));

switch (op) {
// Implicit locations have no storage in the inferior. Composite locations
// might, but we conservatively treat them as non-writable because LLDB
// does not write their pieces back.
case DW_OP_stack_value:
case DW_OP_implicit_value:
case DW_OP_implicit_pointer:
case DW_OP_piece:
case DW_OP_bit_piece:
return true;
default:
break;
}

const lldb::offset_t op_arg_size =
GetOpcodeDataSize(m_data, offset, op, dwarf_cu);
if (op_arg_size == LLDB_INVALID_OFFSET)
return false;
offset += op_arg_size;
}
return false;
}

bool DWARFExpression::LinkThreadLocalStorage(
const DWARFExpression::Delegate *dwarf_cu,
std::function<lldb::addr_t(lldb::addr_t file_addr)> const
Expand Down
81 changes: 51 additions & 30 deletions lldb/source/Expression/DWARFExpressionList.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -229,43 +229,64 @@ void DWARFExpressionList::GetDescription(Stream *s,
}
}

llvm::Expected<const DWARFExpression *>
DWARFExpressionList::GetExpressionAtPC(ExecutionContext *exe_ctx,
RegisterContext *reg_ctx,
lldb::addr_t func_load_addr) const {
if (const DWARFExpression *always = GetAlwaysValidExpr())
return always;

Address pc;
StackFrame *frame = nullptr;
if (!reg_ctx || !reg_ctx->GetPCForSymbolication(pc)) {
if (exe_ctx)
frame = exe_ctx->GetFramePtr();
if (!frame)
return llvm::createStringError("no frame");
RegisterContextSP reg_ctx_sp = frame->GetRegisterContext();
if (!reg_ctx_sp)
return llvm::createStringError("no register context");
reg_ctx_sp->GetPCForSymbolication(pc);
}

if (!pc.IsValid())
return llvm::createStringError("invalid PC in frame");

addr_t pc_load_addr = pc.GetLoadAddress(exe_ctx->GetTargetPtr());
const DWARFExpression *entry =
GetExpressionAtAddress(func_load_addr, pc_load_addr);
if (!entry)
return llvm::createStringError("variable not available");
return entry;
}

llvm::Expected<Value> DWARFExpressionList::Evaluate(
ExecutionContext *exe_ctx, RegisterContext *reg_ctx,
lldb::addr_t func_load_addr, const Value *initial_value_ptr,
const Value *object_address_ptr) const {
llvm::Expected<const DWARFExpression *> expr =
GetExpressionAtPC(exe_ctx, reg_ctx, func_load_addr);
if (!expr)
return expr.takeError();

ModuleSP module_sp = m_module_wp.lock();
DataExtractor data;
RegisterKind reg_kind;
DWARFExpression expr;
if (IsAlwaysValidSingleExpr()) {
expr = m_exprs.Back()->data;
} else {
Address pc;
StackFrame *frame = nullptr;
if (!reg_ctx || !reg_ctx->GetPCForSymbolication(pc)) {
if (exe_ctx)
frame = exe_ctx->GetFramePtr();
if (!frame)
return llvm::createStringError("no frame");
RegisterContextSP reg_ctx_sp = frame->GetRegisterContext();
if (!reg_ctx_sp)
return llvm::createStringError("no register context");
reg_ctx_sp->GetPCForSymbolication(pc);
}

if (!pc.IsValid()) {
return llvm::createStringError("Invalid PC in frame.");
}
addr_t pc_load_addr = pc.GetLoadAddress(exe_ctx->GetTargetPtr());
const DWARFExpression *entry =
GetExpressionAtAddress(func_load_addr, pc_load_addr);
if (!entry)
return llvm::createStringError("variable not available");
expr = *entry;
}
expr.GetExpressionData(data);
reg_kind = expr.GetRegisterKind();
(*expr)->GetExpressionData(data);
RegisterKind reg_kind = (*expr)->GetRegisterKind();
return DWARFExpression::Evaluate(exe_ctx, reg_ctx, module_sp, data,
m_dwarf_cu, reg_kind, initial_value_ptr,
object_address_ptr);
}

bool DWARFExpressionList::IsImplicit(ExecutionContext *exe_ctx,
RegisterContext *reg_ctx,
lldb::addr_t func_load_addr) const {
llvm::Expected<const DWARFExpression *> expr =
GetExpressionAtPC(exe_ctx, reg_ctx, func_load_addr);
if (!expr) {
// No location in scope, so nothing to write back to.
llvm::consumeError(expr.takeError());
return true;
}
return (*expr)->IsImplicit(m_dwarf_cu);
}
8 changes: 8 additions & 0 deletions lldb/source/ValueObject/ValueObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -820,6 +820,10 @@ uint64_t ValueObject::GetData(DataExtractor &data, Status &error) {

bool ValueObject::SetData(DataExtractor &data, Status &error) {
error.Clear();
if (GetIsConstant()) {
error = Status::FromErrorString("Cannot change the value of a constant");
return false;
}
// Make sure our value is up to date first so that our location and location
// type is valid.
if (!UpdateValueIfNeeded(false)) {
Expand Down Expand Up @@ -1705,6 +1709,10 @@ static const char *ConvertBoolean(lldb::LanguageType language_type,

bool ValueObject::SetValueFromCString(const char *value_str, Status &error) {
error.Clear();
if (GetIsConstant()) {
error = Status::FromErrorString("Cannot change the value of a constant");
return false;
}
// Make sure our value is up to date first so that our location and location
// type is valid.
if (!UpdateValueIfNeeded(false)) {
Expand Down
Loading