forked from OSchip/llvm-project
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:
parent
f5b4d655d2
commit
0c10a85000
|
@ -353,6 +353,9 @@ public:
|
|||
|
||||
lldb::SBType
|
||||
GetType();
|
||||
|
||||
lldb::SBValue
|
||||
Persist ();
|
||||
|
||||
bool
|
||||
GetDescription (lldb::SBStream &description);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
||||
//----------------------------------------------------------------------
|
||||
|
|
|
@ -413,6 +413,9 @@ public:
|
|||
lldb::SBAddress
|
||||
GetAddress();
|
||||
|
||||
lldb::SBValue
|
||||
Persist ();
|
||||
|
||||
%feature("docstring", "Returns an expression path for this value."
|
||||
) GetExpressionPath;
|
||||
bool
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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 (),
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
LEVEL = ../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
# Clean renamed executable on 'make clean'
|
||||
clean: OBJECTS+=no_synth
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -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()
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue