Add the ability for an SBValue to create a persisted version of itself.

Such a persisted version is equivalent to evaluating the value via the expression evaluator, and holding on to the $n result of the expression, except this API can be used on SBValues that do not obviously come from an expression (e.g. are the result of a memory lookup)

Expose this via SBValue::Persist() in our public API layer, and ValueObject::Persist() in the lldb_private layer

Includes testcase

Fixes rdar://19136664

llvm-svn: 223711
This commit is contained in:
Enrico Granata 2014-12-08 23:13:56 +00:00
parent f5b4d655d2
commit 0c10a85000
12 changed files with 192 additions and 6 deletions

View File

@ -353,6 +353,9 @@ public:
lldb::SBType
GetType();
lldb::SBValue
Persist ();
bool
GetDescription (lldb::SBStream &description);

View File

@ -814,6 +814,9 @@ public:
const DumpValueObjectOptions& options);
lldb::ValueObjectSP
Persist ();
// returns true if this is a char* or a char[]
// if it is a char* and check_pointer is true,
// it also checks that the pointer is valid

View File

@ -59,7 +59,8 @@ public:
static lldb::ValueObjectSP
Create (ExecutionContextScope *exe_scope,
Value &value,
const ConstString &name);
const ConstString &name,
Module* module = nullptr);
// When an expression fails to evaluate, we return an error
static lldb::ValueObjectSP
@ -172,7 +173,8 @@ private:
ValueObjectConstResult (ExecutionContextScope *exe_scope,
const Value &value,
const ConstString &name);
const ConstString &name,
Module* module = nullptr);
ValueObjectConstResult (ExecutionContextScope *exe_scope,
const Error& error);

View File

@ -65,6 +65,11 @@ class ClangExpressionVariable
public:
ClangExpressionVariable(ExecutionContextScope *exe_scope, lldb::ByteOrder byte_order, uint32_t addr_byte_size);
ClangExpressionVariable (ExecutionContextScope *exe_scope,
Value &value,
const ConstString &name,
uint16_t flags = EVNone);
ClangExpressionVariable(const lldb::ValueObjectSP &valobj_sp);
//----------------------------------------------------------------------

View File

@ -413,6 +413,9 @@ public:
lldb::SBAddress
GetAddress();
lldb::SBValue
Persist ();
%feature("docstring", "Returns an expression path for this value."
) GetExpressionPath;
bool

View File

@ -1892,3 +1892,16 @@ SBValue::WatchPointee (bool resolve_location, bool read, bool write, SBError &er
sb_watchpoint = Dereference().Watch (resolve_location, read, write, error);
return sb_watchpoint;
}
lldb::SBValue
SBValue::Persist ()
{
ValueLocker locker;
lldb::ValueObjectSP value_sp(GetSP(locker));
SBValue persisted_sb;
if (value_sp)
{
persisted_sb.SetSP(value_sp->Persist());
}
return persisted_sb;
}

View File

@ -37,6 +37,9 @@
#include "lldb/DataFormatters/StringPrinter.h"
#include "lldb/DataFormatters/ValueObjectPrinter.h"
#include "lldb/Expression/ClangExpressionVariable.h"
#include "lldb/Expression/ClangPersistentVariables.h"
#include "lldb/Host/Endian.h"
#include "lldb/Interpreter/CommandInterpreter.h"
@ -4180,3 +4183,26 @@ ValueObject::CanProvideValue ()
{
return (false == GetClangType().IsAggregateType());
}
ValueObjectSP
ValueObject::Persist ()
{
if (!UpdateValueIfNeeded())
return nullptr;
TargetSP target_sp(GetTargetSP());
if (!target_sp)
return nullptr;
ConstString name(target_sp->GetPersistentVariables().GetNextPersistentVariableName());
ClangExpressionVariableSP clang_var_sp(new ClangExpressionVariable(target_sp.get(), GetValue(), name));
if (clang_var_sp)
{
clang_var_sp->m_live_sp = clang_var_sp->m_frozen_sp;
clang_var_sp->m_flags |= ClangExpressionVariable::EVIsProgramReference;
target_sp->GetPersistentVariables().AddVariable(clang_var_sp);
}
return clang_var_sp->GetValueObject();
}

View File

@ -122,9 +122,10 @@ ValueObjectConstResult::Create (ExecutionContextScope *exe_scope,
ValueObjectSP
ValueObjectConstResult::Create (ExecutionContextScope *exe_scope,
Value &value,
const ConstString &name)
const ConstString &name,
Module *module)
{
return (new ValueObjectConstResult (exe_scope, value, name))->GetSP();
return (new ValueObjectConstResult (exe_scope, value, name, module))->GetSP();
}
ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope,
@ -222,15 +223,18 @@ ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope
ValueObjectConstResult::ValueObjectConstResult (ExecutionContextScope *exe_scope,
const Value &value,
const ConstString &name) :
const ConstString &name,
Module *module) :
ValueObject (exe_scope),
m_type_name (),
m_byte_size (0),
m_impl(this)
{
m_value = value;
m_value.GetData(m_data);
m_name = name;
ExecutionContext exe_ctx;
exe_scope->CalculateExecutionContext(exe_ctx);
m_error = m_value.GetValueAsData(&exe_ctx, m_data, 0, module);
}
ValueObjectConstResult::~ValueObjectConstResult()

View File

@ -28,6 +28,17 @@ ClangExpressionVariable::ClangExpressionVariable(ExecutionContextScope *exe_scop
{
}
ClangExpressionVariable::ClangExpressionVariable (ExecutionContextScope *exe_scope,
Value &value,
const ConstString &name,
uint16_t flags) :
m_parser_vars(),
m_jit_vars (),
m_flags (flags),
m_frozen_sp (ValueObjectConstResult::Create (exe_scope, value, name))
{
}
ClangExpressionVariable::ClangExpressionVariable (const lldb::ValueObjectSP &valobj_sp) :
m_parser_vars(),
m_jit_vars (),

View File

@ -0,0 +1,8 @@
LEVEL = ../../make
CXX_SOURCES := main.cpp
# Clean renamed executable on 'make clean'
clean: OBJECTS+=no_synth
include $(LEVEL)/Makefile.rules

View File

@ -0,0 +1,94 @@
"""Test SBValue::Persist"""
import os, sys, time
import unittest2
import lldb
from lldbtest import *
import lldbutil
class SBValuePersistTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@python_api_test
@dsym_test
def test_with_dsym(self):
"""Test SBValue::Persist"""
self.buildDsym()
self.setTearDownCleanup()
self.doTest()
@python_api_test
@dwarf_test
def test_with_dwarf(self):
"""Test SBValue::Persist"""
self.buildDwarf()
self.setTearDownCleanup()
self.doTest()
def setUp(self):
# Call super's setUp().
TestBase.setUp(self)
def doTest(self):
"""Test SBValue::Persist"""
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
lldbutil.run_break_set_by_source_regexp (self, "break here")
self.runCmd("run", RUN_SUCCEEDED)
# The stop reason of the thread should be breakpoint.
self.expect("thread list", STOPPED_DUE_TO_BREAKPOINT,
substrs = ['stopped',
'stop reason = breakpoint'])
# This is the function to remove the custom formats in order to have a
# clean slate for the next test case.
def cleanup():
self.runCmd('type format clear', check=False)
self.runCmd('type summary clear', check=False)
self.runCmd('type filter clear', check=False)
self.runCmd('type synthetic clear', check=False)
# Execute the cleanup function during test case tear down.
self.addTearDownHook(cleanup)
foo = self.frame().FindVariable("foo")
bar = self.frame().FindVariable("bar")
baz = self.frame().FindVariable("baz")
self.assertTrue(foo.IsValid(), "foo is not valid")
self.assertTrue(bar.IsValid(), "bar is not valid")
self.assertTrue(baz.IsValid(), "baz is not valid")
fooPersist = foo.Persist()
barPersist = bar.Persist()
bazPersist = baz.Persist()
self.assertTrue(fooPersist.IsValid(), "fooPersist is not valid")
self.assertTrue(barPersist.IsValid(), "barPersist is not valid")
self.assertTrue(bazPersist.IsValid(), "bazPersist is not valid")
self.assertTrue(fooPersist.GetValueAsUnsigned(0) == 10, "fooPersist != 10")
self.assertTrue(barPersist.GetPointeeData().sint32[0] == 4, "barPersist != 4")
self.assertTrue(bazPersist.GetSummary() == '"85"', "bazPersist != 85")
self.runCmd("continue")
self.assertTrue(fooPersist.IsValid(), "fooPersist is not valid")
self.assertTrue(barPersist.IsValid(), "barPersist is not valid")
self.assertTrue(bazPersist.IsValid(), "bazPersist is not valid")
self.assertTrue(fooPersist.GetValueAsUnsigned(0) == 10, "fooPersist != 10")
self.assertTrue(barPersist.GetPointeeData().sint32[0] == 4, "barPersist != 4")
self.assertTrue(bazPersist.GetSummary() == '"85"', "bazPersist != 85")
self.expect("expr *(%s)" % (barPersist.GetName()), substrs = ['= 4'])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -0,0 +1,14 @@
#include <vector>
#include <string>
void f() {}
int main() {
int foo = 10;
int *bar = new int(4);
std::string baz = "85";
f(); // break here
f(); // break here
return 0;
}