Fixed a bug where a variable could not be formatted in a summary if its datatype already had a custom format

Fixed a bug where Objective-C variables coming out of the expression parser could crash the Python synthetic providers:
 - expression parser output has a "frozen data" component, which is a byte-exact copy of the value (in host memory),
   if trying to read into memory based on the host address, LLDB would crash. we are now passing the correct (target)
   pointer to the Python code
Objective-C "id" variables are now formatted according to their dynamic type, if the -d option to frame variable is used:
 - Code based on the Objective-C 2.0 runtime is used to obtain this information without running code on the target

llvm-svn: 136695
This commit is contained in:
Enrico Granata 2011-08-02 17:27:39 +00:00
parent 9ab728bb05
commit c3e320a7a0
20 changed files with 546 additions and 93 deletions

View File

@ -486,7 +486,7 @@ public:
{
public:
static bool
Get(ValueObject& vobj, ValueFormat::SharedPointer &entry);
Get(ValueObject& vobj, lldb::DynamicValueType use_dynamic, ValueFormat::SharedPointer &entry);
static void
Add(const ConstString &type, const ValueFormat::SharedPointer &entry);
@ -509,9 +509,11 @@ public:
static bool
GetSummaryFormat(ValueObject& vobj,
lldb::DynamicValueType use_dynamic,
lldb::SummaryFormatSP& entry);
static bool
GetSyntheticFilter(ValueObject& vobj,
lldb::DynamicValueType use_dynamic,
lldb::SyntheticChildrenSP& entry);
class NamedSummaryFormats

View File

@ -55,7 +55,9 @@ namespace std
#include "lldb/Core/ValueObject.h"
#include "lldb/Interpreter/ScriptInterpreterPython.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/Target/Platform.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/TargetList.h"
@ -189,6 +191,8 @@ private:
typedef FormatMap<KeyType,ValueType> BackEndType;
BackEndType m_format_map;
std::string m_name;
public:
typedef typename BackEndType::MapType MapType;
@ -201,8 +205,10 @@ public:
friend class FormatCategory;
FormatNavigator(IFormatChangeListener* lst = NULL) :
m_format_map(lst)
FormatNavigator(std::string name,
IFormatChangeListener* lst = NULL) :
m_format_map(lst),
m_name(name)
{
}
@ -223,11 +229,12 @@ public:
bool
Get(ValueObject& vobj,
MapValueType& entry,
lldb::DynamicValueType use_dynamic,
uint32_t* why = NULL)
{
uint32_t value = lldb::eFormatterChoiceCriterionDirectChoice;
clang::QualType type = clang::QualType::getFromOpaquePtr(vobj.GetClangType());
bool ret = Get(vobj, type, entry, value);
bool ret = Get(vobj, type, entry, use_dynamic, value);
if (ret)
entry = MapValueType(entry);
else
@ -267,9 +274,64 @@ private:
return m_format_map.Get(type, entry);
}
bool Get_ObjC(ValueObject& vobj,
ObjCLanguageRuntime::ObjCISA isa,
MapValueType& entry,
uint32_t& reason)
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES));
if (log)
log->Printf("going to an Objective-C dynamic scanning");
Process* process = vobj.GetUpdatePoint().GetProcessSP().get();
ObjCLanguageRuntime* runtime = process->GetObjCLanguageRuntime();
if (runtime == NULL)
{
if (log)
log->Printf("no valid ObjC runtime, bailing out");
return false;
}
if (runtime->IsValidISA(isa) == false)
{
if (log)
log->Printf("invalid ISA, bailing out");
return false;
}
ConstString name = runtime->GetActualTypeName(isa);
if (log)
log->Printf("looking for formatter for %s", name.GetCString());
if (Get(name.GetCString(), entry))
{
if (log)
log->Printf("direct match found, returning");
return true;
}
if (log)
log->Printf("no direct match");
ObjCLanguageRuntime::ObjCISA parent = runtime->GetParentClass(isa);
if (runtime->IsValidISA(parent) == false)
{
if (log)
log->Printf("invalid parent ISA, bailing out");
return false;
}
if (parent == isa)
{
if (log)
log->Printf("parent-child loop, bailing out");
return false;
}
if (Get_ObjC(vobj, parent, entry, reason))
{
reason |= lldb::eFormatterChoiceCriterionNavigatedBaseClasses;
return true;
}
return false;
}
bool Get(ValueObject& vobj,
clang::QualType type,
MapValueType& entry,
lldb::DynamicValueType use_dynamic,
uint32_t& reason)
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES));
@ -299,7 +361,10 @@ private:
log->Printf("appended bitfield info, final result is %s", name.GetCString());
}
if (log)
log->Printf("trying to get format for VO name %s of type %s",vobj.GetName().AsCString(),name.AsCString());
log->Printf("trying to get %s for VO name %s of type %s",
m_name.c_str(),
vobj.GetName().AsCString(),
name.AsCString());
if (Get(name.GetCString(), entry))
{
if (log)
@ -313,17 +378,48 @@ private:
{
if (log)
log->Printf("stripping reference");
if (Get(vobj,type.getNonReferenceType(),entry, reason) && !entry->m_skip_references)
if (Get(vobj,type.getNonReferenceType(),entry, use_dynamic, reason) && !entry->m_skip_references)
{
reason |= lldb::eFormatterChoiceCriterionStrippedPointerReference;
return true;
}
}
if (use_dynamic != lldb::eNoDynamicValues &&
typePtr == vobj.GetClangAST()->ObjCBuiltinIdTy.getTypePtr())
{
if (log)
log->Printf("this is an ObjC 'id', let's do dynamic search");
Process* process = vobj.GetUpdatePoint().GetProcessSP().get();
ObjCLanguageRuntime* runtime = process->GetObjCLanguageRuntime();
if (runtime == NULL)
{
if (log)
log->Printf("no valid ObjC runtime, skipping dynamic");
}
else
{
if (Get_ObjC(vobj, runtime->GetISA(vobj), entry, reason))
{
reason |= lldb::eFormatterChoiceCriterionDynamicObjCHierarchy;
return true;
}
}
}
else if (use_dynamic != lldb::eNoDynamicValues && log)
{
log->Printf("typename: %s, typePtr = %p, id = %p",
name.AsCString(), typePtr, vobj.GetClangAST()->ObjCBuiltinIdTy.getTypePtr());
}
else if (log)
{
log->Printf("no dynamic");
}
if (typePtr->isPointerType())
{
if (log)
log->Printf("stripping pointer");
if (Get(vobj, typePtr->getPointeeType(), entry, reason) && !entry->m_skip_pointers)
clang::QualType pointee = typePtr->getPointeeType();
if (Get(vobj, pointee, entry, use_dynamic, reason) && !entry->m_skip_pointers)
{
reason |= lldb::eFormatterChoiceCriterionStrippedPointerReference;
return true;
@ -331,6 +427,27 @@ private:
}
if (typePtr->isObjCObjectPointerType())
{
if (use_dynamic != lldb::eNoDynamicValues &&
name.GetCString() == ConstString("id").GetCString())
{
if (log)
log->Printf("this is an ObjC 'id', let's do dynamic search");
Process* process = vobj.GetUpdatePoint().GetProcessSP().get();
ObjCLanguageRuntime* runtime = process->GetObjCLanguageRuntime();
if (runtime == NULL)
{
if (log)
log->Printf("no valid ObjC runtime, skipping dynamic");
}
else
{
if (Get_ObjC(vobj, runtime->GetISA(vobj), entry, reason))
{
reason |= lldb::eFormatterChoiceCriterionDynamicObjCHierarchy;
return true;
}
}
}
if (log)
log->Printf("stripping ObjC pointer");
/*
@ -343,7 +460,7 @@ private:
ValueObject* target = vobj.Dereference(error).get();
if (error.Fail() || !target)
return false;
if (Get(*target, typePtr->getPointeeType(), entry, reason) && !entry->m_skip_pointers)
if (Get(*target, typePtr->getPointeeType(), entry, use_dynamic, reason) && !entry->m_skip_pointers)
{
reason |= lldb::eFormatterChoiceCriterionStrippedPointerReference;
return true;
@ -368,7 +485,7 @@ private:
if (log)
log->Printf("got a parent class for this ObjC class");
clang::QualType ivar_qual_type(ast->getObjCInterfaceType(superclass_interface_decl));
if (Get(vobj, ivar_qual_type, entry, reason) && entry->m_cascades)
if (Get(vobj, ivar_qual_type, entry, use_dynamic, reason) && entry->m_cascades)
{
reason |= lldb::eFormatterChoiceCriterionNavigatedBaseClasses;
return true;
@ -397,7 +514,7 @@ private:
end = record->bases_end();
for (pos = record->bases_begin(); pos != end; pos++)
{
if ((Get(vobj, pos->getType(), entry, reason)) && entry->m_cascades)
if ((Get(vobj, pos->getType(), entry, use_dynamic, reason)) && entry->m_cascades)
{
reason |= lldb::eFormatterChoiceCriterionNavigatedBaseClasses;
return true;
@ -411,7 +528,7 @@ private:
end = record->vbases_end();
for (pos = record->vbases_begin(); pos != end; pos++)
{
if ((Get(vobj, pos->getType(), entry, reason)) && entry->m_cascades)
if ((Get(vobj, pos->getType(), entry, use_dynamic, reason)) && entry->m_cascades)
{
reason |= lldb::eFormatterChoiceCriterionNavigatedBaseClasses;
return true;
@ -427,7 +544,7 @@ private:
{
if (log)
log->Printf("stripping typedef");
if ((Get(vobj, type_tdef->getDecl()->getUnderlyingType(), entry, reason)) && entry->m_cascades)
if ((Get(vobj, type_tdef->getDecl()->getUnderlyingType(), entry, use_dynamic, reason)) && entry->m_cascades)
{
reason |= lldb::eFormatterChoiceCriterionNavigatedTypedefs;
return true;
@ -495,9 +612,9 @@ public:
FormatCategory(IFormatChangeListener* clist,
std::string name) :
m_summary_nav(new SummaryNavigator(clist)),
m_regex_summary_nav(new RegexSummaryNavigator(clist)),
m_filter_nav(new FilterNavigator(clist)),
m_summary_nav(new SummaryNavigator("summary",clist)),
m_regex_summary_nav(new RegexSummaryNavigator("regex-summary",clist)),
m_filter_nav(new FilterNavigator("filter",clist)),
m_enabled(false),
m_change_listener(clist),
m_mutex(Mutex::eMutexTypeRecursive),
@ -531,13 +648,14 @@ public:
bool
Get(ValueObject& vobj,
lldb::SummaryFormatSP& entry,
lldb::DynamicValueType use_dynamic,
uint32_t* reason = NULL)
{
if (!IsEnabled())
return false;
if (Summary()->Get(vobj, entry, reason))
if (Summary()->Get(vobj, entry, use_dynamic, reason))
return true;
bool regex = RegexSummary()->Get(vobj, entry, reason);
bool regex = RegexSummary()->Get(vobj, entry, use_dynamic, reason);
if (regex && reason)
*reason |= lldb::eFormatterChoiceCriterionRegularExpressionSummary;
return regex;
@ -546,11 +664,12 @@ public:
bool
Get(ValueObject& vobj,
lldb::SyntheticChildrenSP& entry,
lldb::DynamicValueType use_dynamic,
uint32_t* reason = NULL)
{
if (!IsEnabled())
return false;
return (Filter()->Get(vobj, entry, reason));
return (Filter()->Get(vobj, entry, use_dynamic, reason));
}
// just a shortcut for Summary()->Clear; RegexSummary()->Clear()
@ -760,43 +879,44 @@ public:
bool
Get(ValueObject& vobj,
lldb::SummaryFormatSP& entry)
lldb::SummaryFormatSP& entry,
lldb::DynamicValueType use_dynamic)
{
Mutex::Locker(m_map_mutex);
uint32_t reason_why;
bool first = true;
uint32_t reason_why;
ActiveCategoriesIterator begin, end = m_active_categories.end();
for (begin = m_active_categories.begin(); begin != end; begin++)
{
FormatCategory::SharedPointer category = *begin;
lldb::SummaryFormatSP current_format;
if (!category->Get(vobj, current_format, &reason_why))
if (!category->Get(vobj, current_format, use_dynamic, &reason_why))
continue;
if (reason_why == lldb::eFormatterChoiceCriterionDirectChoice)
{
entry = current_format;
return true;
}
else if (first)
{
entry = current_format;
first = false;
}
/*if (reason_why == lldb::eFormatterChoiceCriterionDirectChoice)
{
entry = current_format;
return true;
}
else if (first)
{
entry = current_format;
first = false;
}*/
entry = current_format;
return true;
}
return !first;
return false;
}
bool
Get(ValueObject& vobj,
lldb::SyntheticChildrenSP& entry)
lldb::SyntheticChildrenSP& entry,
lldb::DynamicValueType use_dynamic)
{
Mutex::Locker(m_map_mutex);
uint32_t reason_why;
bool first = true;
ActiveCategoriesIterator begin, end = m_active_categories.end();
@ -804,9 +924,9 @@ public:
{
FormatCategory::SharedPointer category = *begin;
lldb::SyntheticChildrenSP current_format;
if (!category->Get(vobj, current_format, &reason_why))
if (!category->Get(vobj, current_format, use_dynamic, &reason_why))
continue;
if (reason_why == lldb::eFormatterChoiceCriterionDirectChoice)
/*if (reason_why == lldb::eFormatterChoiceCriterionDirectChoice)
{
entry = current_format;
return true;
@ -815,9 +935,11 @@ public:
{
entry = current_format;
first = false;
}
}*/
entry = current_format;
return true;
}
return !first;
return false;
}
};
@ -847,7 +969,7 @@ public:
typedef bool (*CategoryCallback)(void*, const char*, const FormatCategory::SharedPointer&);
FormatManager() :
m_value_nav(this),
m_value_nav("format",this),
m_named_summaries_map(this),
m_last_revision(0),
m_categories_map(this)
@ -941,15 +1063,17 @@ public:
bool
Get(ValueObject& vobj,
lldb::SummaryFormatSP& entry)
lldb::SummaryFormatSP& entry,
lldb::DynamicValueType use_dynamic)
{
return m_categories_map.Get(vobj, entry);
return m_categories_map.Get(vobj, entry, use_dynamic);
}
bool
Get(ValueObject& vobj,
lldb::SyntheticChildrenSP& entry)
lldb::SyntheticChildrenSP& entry,
lldb::DynamicValueType use_dynamic)
{
return m_categories_map.Get(vobj, entry);
return m_categories_map.Get(vobj, entry, use_dynamic);
}
static bool

View File

@ -443,6 +443,9 @@ public:
virtual const char *
GetValueAsCString ();
virtual unsigned long long
GetValueAsUnsigned();
virtual bool
SetValueFromCString (const char *value_str);
@ -513,8 +516,11 @@ public:
bool
UpdateValueIfNeeded (bool update_format = true);
bool
UpdateValueIfNeeded (lldb::DynamicValueType use_dynamic, bool update_format = true);
void
UpdateFormatsIfNeeded();
UpdateFormatsIfNeeded(lldb::DynamicValueType use_dynamic = lldb::eNoDynamicValues);
DataExtractor &
GetDataExtractor ();
@ -639,6 +645,18 @@ public:
return m_format;
}
void
SetIsExpressionResult(bool expr)
{
m_is_expression_result = expr;
}
bool
GetIsExpressionResult()
{
return m_is_expression_result;
}
void
SetFormat (lldb::Format format)
{
@ -677,7 +695,7 @@ public:
lldb::SummaryFormatSP
GetSummaryFormat()
{
UpdateFormatsIfNeeded();
UpdateFormatsIfNeeded(m_last_format_mgr_dynamic);
if (HasCustomSummaryFormat())
return m_forced_summary_format;
return m_last_summary_format;
@ -744,6 +762,7 @@ protected:
lldb::Format m_format;
uint32_t m_last_format_mgr_revision;
lldb::DynamicValueType m_last_format_mgr_dynamic;
lldb::SummaryFormatSP m_last_summary_format;
lldb::SummaryFormatSP m_forced_summary_format;
lldb::ValueFormatSP m_last_value_format;
@ -759,7 +778,8 @@ protected:
m_is_array_item_for_pointer:1,
m_is_bitfield_for_scalar:1,
m_is_expression_path_child:1,
m_is_child_at_offset:1;
m_is_child_at_offset:1,
m_is_expression_result:1;
// used to prevent endless looping into GetpPrintableRepresentation()
uint32_t m_dump_printable_counter;

View File

@ -98,7 +98,8 @@ public:
GetMinimumLanguage ();
static lldb::LanguageType
GetMinimumLanguage (lldb::clang_type_t clang_type);
GetMinimumLanguage (clang::ASTContext *ctx,
lldb::clang_type_t clang_type);
void
DumpValue (ExecutionContext *exe_ctx,

View File

@ -72,6 +72,20 @@ public:
{
return eObjC_VersionUnknown;
}
typedef lldb::addr_t ObjCISA;
virtual bool
IsValidISA(ObjCISA isa) = 0;
virtual ObjCISA
GetISA(ValueObject& valobj) = 0;
virtual ConstString
GetActualTypeName(ObjCISA isa) = 0;
virtual ObjCISA
GetParentClass(ObjCISA isa) = 0;
// Finds the byte offset of the child_type ivar in parent_type. If it can't find the
// offset, returns LLDB_INVALID_IVAR_OFFSET.

View File

@ -505,7 +505,8 @@ namespace lldb {
eFormatterChoiceCriterionStrippedPointerReference = 0x00000001,
eFormatterChoiceCriterionNavigatedTypedefs = 0x00000002,
eFormatterChoiceCriterionNavigatedBaseClasses = 0x00000004,
eFormatterChoiceCriterionRegularExpressionSummary = 0x00000008
eFormatterChoiceCriterionRegularExpressionSummary = 0x00000008,
eFormatterChoiceCriterionDynamicObjCHierarchy = 0x00000010
} FormatterChoiceCriterion;
//----------------------------------------------------------------------

View File

@ -321,6 +321,9 @@ CommandObjectExpression::EvaluateExpression
{
if (result_valobj_sp->GetError().Success())
{
result_valobj_sp.get()->SetIsExpressionResult(true);
if (m_options.format != eFormatDefault)
result_valobj_sp->SetFormat (m_options.format);

View File

@ -1010,22 +1010,35 @@ Debugger::FormatPrompt
if (!vobj)
break;
if (log)
log->Printf("initial string: %s",var_name_begin);
// check for *var and *svar
if (*var_name_begin == '*')
{
do_deref_pointer = true;
var_name_begin++;
}
if (log)
log->Printf("initial string: %s",var_name_begin);
if (*var_name_begin == 's')
{
vobj = vobj->GetSyntheticValue(lldb::eUseSyntheticFilter).get();
var_name_begin++;
}
if (log)
log->Printf("initial string: %s",var_name_begin);
// should be a 'v' by now
if (*var_name_begin != 'v')
break;
if (log)
log->Printf("initial string: %s",var_name_begin);
ValueObject::ExpressionPathAftermath what_next = (do_deref_pointer ?
ValueObject::eDereference : ValueObject::eNothing);
ValueObject::GetValueForExpressionPathOptions options;
@ -1765,9 +1778,9 @@ Debugger::Formatting::ForceUpdate()
}
bool
Debugger::Formatting::ValueFormats::Get(ValueObject& vobj, ValueFormat::SharedPointer &entry)
Debugger::Formatting::ValueFormats::Get(ValueObject& vobj, lldb::DynamicValueType use_dynamic, ValueFormat::SharedPointer &entry)
{
return GetFormatManager().Value().Get(vobj,entry);
return GetFormatManager().Value().Get(vobj,entry, use_dynamic);
}
void
@ -1808,15 +1821,17 @@ Debugger::Formatting::ValueFormats::GetCount()
bool
Debugger::Formatting::GetSummaryFormat(ValueObject& vobj,
lldb::DynamicValueType use_dynamic,
lldb::SummaryFormatSP& entry)
{
return GetFormatManager().Get(vobj, entry);
return GetFormatManager().Get(vobj, entry, use_dynamic);
}
bool
Debugger::Formatting::GetSyntheticFilter(ValueObject& vobj,
lldb::DynamicValueType use_dynamic,
lldb::SyntheticChildrenSP& entry)
{
return GetFormatManager().Get(vobj, entry);
return GetFormatManager().Get(vobj, entry, use_dynamic);
}
bool

View File

@ -21,6 +21,7 @@
#include "lldb/Core/Debugger.h"
#include "lldb/Core/FormatClasses.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Core/ValueObjectConstResult.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Symbol/ClangASTType.h"
#include "lldb/Target/StackFrame.h"
@ -121,8 +122,29 @@ StringSummaryFormat::GetDescription()
std::string
ScriptSummaryFormat::FormatObject(lldb::ValueObjectSP object)
{
lldb::ValueObjectSP target_object;
if (object->GetIsExpressionResult() &&
ClangASTContext::IsPointerType(object->GetClangType()) &&
object->GetValue().GetValueType() == Value::eValueTypeHostAddress)
{
// when using the expression parser, an additional layer of "frozen data"
// can be created, which is basically a byte-exact copy of the data returned
// by the expression, but in host memory. because Python code might need to read
// into the object memory in non-obvious ways, we need to hand it the target version
// of the expression output
lldb::addr_t tgt_address = object->GetValueAsUnsigned();
target_object = ValueObjectConstResult::Create (object->GetExecutionContextScope(),
object->GetClangAST(),
object->GetClangType(),
object->GetName(),
tgt_address,
eAddressTypeLoad,
object->GetUpdatePoint().GetProcessSP()->GetAddressByteSize());
}
else
target_object = object;
return std::string(ScriptInterpreterPython::CallPythonScriptFunction(m_function_name.c_str(),
object).c_str());
target_object).c_str());
}
std::string
@ -171,7 +193,26 @@ m_python_class(pclass)
return;
}
m_interpreter = be->GetUpdatePoint().GetTargetSP()->GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
if (be->GetIsExpressionResult() &&
ClangASTContext::IsPointerType(be->GetClangType()) &&
be->GetValue().GetValueType() == Value::eValueTypeHostAddress)
{
// when using the expression parser, an additional layer of "frozen data"
// can be created, which is basically a byte-exact copy of the data returned
// by the expression, but in host memory. because Python code might need to read
// into the object memory in non-obvious ways, we need to hand it the target version
// of the expression output
lldb::addr_t tgt_address = be->GetValueAsUnsigned();
m_backend = ValueObjectConstResult::Create (be->GetExecutionContextScope(),
be->GetClangAST(),
be->GetClangType(),
be->GetName(),
tgt_address,
eAddressTypeLoad,
be->GetUpdatePoint().GetProcessSP()->GetAddressByteSize());
}
m_interpreter = m_backend->GetUpdatePoint().GetTargetSP()->GetDebugger().GetCommandInterpreter().GetScriptInterpreter();
if (m_interpreter == NULL)
m_wrapper = NULL;

View File

@ -39,6 +39,7 @@
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/LanguageRuntime.h"
#include "lldb/Target/ObjCLanguageRuntime.h"
#include "lldb/Target/Process.h"
#include "lldb/Target/RegisterContext.h"
#include "lldb/Target/Target.h"
@ -76,6 +77,7 @@ ValueObject::ValueObject (ValueObject &parent) :
m_deref_valobj(NULL),
m_format (eFormatDefault),
m_last_format_mgr_revision(0),
m_last_format_mgr_dynamic(lldb::eNoDynamicValues),
m_last_summary_format(),
m_forced_summary_format(),
m_last_value_format(),
@ -91,6 +93,7 @@ ValueObject::ValueObject (ValueObject &parent) :
m_is_bitfield_for_scalar(false),
m_is_expression_path_child(false),
m_is_child_at_offset(false),
m_is_expression_result(false),
m_dump_printable_counter(0)
{
m_manager->ManageObject(this);
@ -120,6 +123,7 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) :
m_deref_valobj(NULL),
m_format (eFormatDefault),
m_last_format_mgr_revision(0),
m_last_format_mgr_dynamic(lldb::eNoDynamicValues),
m_last_summary_format(),
m_forced_summary_format(),
m_last_value_format(),
@ -135,6 +139,7 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) :
m_is_bitfield_for_scalar(false),
m_is_expression_path_child(false),
m_is_child_at_offset(false),
m_is_expression_result(false),
m_dump_printable_counter(0)
{
m_manager = new ValueObjectManager();
@ -150,10 +155,16 @@ ValueObject::~ValueObject ()
bool
ValueObject::UpdateValueIfNeeded (bool update_format)
{
return UpdateValueIfNeeded(m_last_format_mgr_dynamic, update_format);
}
bool
ValueObject::UpdateValueIfNeeded (lldb::DynamicValueType use_dynamic, bool update_format)
{
if (update_format)
UpdateFormatsIfNeeded();
UpdateFormatsIfNeeded(use_dynamic);
// If this is a constant value, then our success is predicated on whether
// we have an error or not
@ -204,7 +215,7 @@ ValueObject::UpdateValueIfNeeded (bool update_format)
}
void
ValueObject::UpdateFormatsIfNeeded()
ValueObject::UpdateFormatsIfNeeded(lldb::DynamicValueType use_dynamic)
{
LogSP log(lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_TYPES));
if (log)
@ -217,7 +228,8 @@ ValueObject::UpdateFormatsIfNeeded()
ClearCustomSummaryFormat();
m_summary_str.clear();
}
if (m_last_format_mgr_revision != Debugger::Formatting::ValueFormats::GetCurrentRevision())
if ( (m_last_format_mgr_revision != Debugger::Formatting::ValueFormats::GetCurrentRevision()) ||
m_last_format_mgr_dynamic != use_dynamic)
{
if (m_last_summary_format.get())
m_last_summary_format.reset((StringSummaryFormat*)NULL);
@ -228,11 +240,12 @@ ValueObject::UpdateFormatsIfNeeded()
m_synthetic_value = NULL;
Debugger::Formatting::ValueFormats::Get(*this, m_last_value_format);
Debugger::Formatting::GetSummaryFormat(*this, m_last_summary_format);
Debugger::Formatting::GetSyntheticFilter(*this, m_last_synthetic_filter);
Debugger::Formatting::ValueFormats::Get(*this, use_dynamic, m_last_value_format);
Debugger::Formatting::GetSummaryFormat(*this, use_dynamic, m_last_summary_format);
Debugger::Formatting::GetSyntheticFilter(*this, use_dynamic, m_last_synthetic_filter);
m_last_format_mgr_revision = Debugger::Formatting::ValueFormats::GetCurrentRevision();
m_last_format_mgr_dynamic = use_dynamic;
ClearUserVisibleData();
}
@ -241,14 +254,14 @@ ValueObject::UpdateFormatsIfNeeded()
DataExtractor &
ValueObject::GetDataExtractor ()
{
UpdateValueIfNeeded();
UpdateValueIfNeeded(false);
return m_data;
}
const Error &
ValueObject::GetError()
{
UpdateValueIfNeeded();
UpdateValueIfNeeded(false);
return m_error;
}
@ -261,7 +274,7 @@ ValueObject::GetName() const
const char *
ValueObject::GetLocationAsCString ()
{
if (UpdateValueIfNeeded())
if (UpdateValueIfNeeded(false))
{
if (m_location_str.empty())
{
@ -358,7 +371,7 @@ ValueObject::GetChildAtIndex (uint32_t idx, bool can_create)
ValueObjectSP child_sp;
// We may need to update our value if we are dynamic
if (IsPossibleDynamicType ())
UpdateValueIfNeeded();
UpdateValueIfNeeded(false);
if (idx < GetNumChildren())
{
// Check if we have already made the child value object?
@ -395,7 +408,7 @@ ValueObject::GetChildMemberWithName (const ConstString &name, bool can_create)
// We may need to update our value if we are dynamic
if (IsPossibleDynamicType ())
UpdateValueIfNeeded();
UpdateValueIfNeeded(false);
std::vector<uint32_t> child_indexes;
clang::ASTContext *clang_ast = GetClangAST();
@ -519,7 +532,7 @@ ValueObject::CreateChildAtIndex (uint32_t idx, bool synthetic_array_member, int3
const char *
ValueObject::GetSummaryAsCString ()
{
if (UpdateValueIfNeeded ())
if (UpdateValueIfNeeded (m_last_format_mgr_dynamic, true))
{
if (m_summary_str.empty())
{
@ -775,7 +788,7 @@ const char *
ValueObject::GetObjectDescription ()
{
if (!UpdateValueIfNeeded ())
if (!UpdateValueIfNeeded (m_last_format_mgr_dynamic, true))
return NULL;
if (!m_object_desc_str.empty())
@ -826,7 +839,7 @@ ValueObject::GetValueAsCString ()
// If our byte size is zero this is an aggregate type that has children
if (ClangASTContext::IsAggregateType (GetClangType()) == false)
{
if (UpdateValueIfNeeded())
if (UpdateValueIfNeeded(true))
{
if (m_value_str.empty())
{
@ -841,7 +854,7 @@ ValueObject::GetValueAsCString ()
clang_type_t clang_type = GetClangType ();
if (clang_type)
{
if (m_last_value_format)
if (m_format == lldb::eFormatDefault && m_last_value_format)
{
m_value_str = m_last_value_format->FormatObject(GetSP());
}
@ -905,6 +918,24 @@ ValueObject::GetValueAsCString ()
return m_value_str.c_str();
}
// if > 8bytes, 0 is returned. this method should mostly be used
// to read address values out of pointers
unsigned long long
ValueObject::GetValueAsUnsigned()
{
// If our byte size is zero this is an aggregate type that has children
if (ClangASTContext::IsAggregateType (GetClangType()) == false)
{
if (UpdateValueIfNeeded(true))
{
uint32_t offset = 0;
return m_data.GetMaxU64(&offset,
m_data.GetByteSize());
}
}
return 0;
}
// this call should only return pointers to data that needs no special memory management
// (either because they are hardcoded strings, or because they are backed by some other
// object); returning any new()-ed or malloc()-ed data here, will lead to leaks!
@ -1092,7 +1123,7 @@ ValueObject::DumpPrintableRepresentation(Stream& s,
addr_t
ValueObject::GetAddressOf (AddressType &address_type, bool scalar_is_load_address)
{
if (!UpdateValueIfNeeded())
if (!UpdateValueIfNeeded(false))
return LLDB_INVALID_ADDRESS;
switch (m_value.GetValueType())
@ -1124,7 +1155,7 @@ ValueObject::GetPointerValue (AddressType &address_type, bool scalar_is_load_add
lldb::addr_t address = LLDB_INVALID_ADDRESS;
address_type = eAddressTypeInvalid;
if (!UpdateValueIfNeeded())
if (!UpdateValueIfNeeded(false))
return address;
switch (m_value.GetValueType())
@ -1161,7 +1192,7 @@ ValueObject::SetValueFromCString (const char *value_str)
{
// Make sure our value is up to date first so that our location and location
// type is valid.
if (!UpdateValueIfNeeded())
if (!UpdateValueIfNeeded(false))
return false;
uint32_t count = 0;
@ -1256,7 +1287,8 @@ ValueObject::Write ()
lldb::LanguageType
ValueObject::GetObjectRuntimeLanguage ()
{
return ClangASTType::GetMinimumLanguage (GetClangType());
return ClangASTType::GetMinimumLanguage (GetClangAST(),
GetClangType());
}
void
@ -1521,7 +1553,7 @@ ValueObject::CalculateSyntheticValue (lldb::SyntheticValueType use_synthetic)
if (use_synthetic == lldb::eNoSyntheticFilter)
return;
UpdateFormatsIfNeeded();
UpdateFormatsIfNeeded(m_last_format_mgr_dynamic);
if (m_last_synthetic_filter.get() == NULL)
return;
@ -1601,7 +1633,7 @@ ValueObject::GetSyntheticValue (SyntheticValueType use_synthetic)
if (use_synthetic == lldb::eNoSyntheticFilter)
return GetSP();
UpdateFormatsIfNeeded();
UpdateFormatsIfNeeded(m_last_format_mgr_dynamic);
if (m_last_synthetic_filter.get() == NULL)
return GetSP();
@ -2537,7 +2569,7 @@ ValueObject::DumpValueObject
{
if (valobj)
{
bool update_success = valobj->UpdateValueIfNeeded ();
bool update_success = valobj->UpdateValueIfNeeded (use_dynamic, true);
if (update_success && use_dynamic != lldb::eNoDynamicValues)
{
@ -2566,7 +2598,33 @@ ValueObject::DumpValueObject
// Always show the type for the top level items.
if (show_types || (curr_depth == 0 && !flat_output))
s.Printf("(%s) ", valobj->GetTypeName().AsCString("<invalid type>"));
{
s.Printf("(%s", valobj->GetTypeName().AsCString("<invalid type>"));
if (use_dynamic != lldb::eNoDynamicValues &&
strcmp(valobj->GetTypeName().AsCString("NULL"), "id") == 0)
{
Process* process = valobj->GetUpdatePoint().GetProcessSP().get();
if (process == NULL)
s.Printf(") ");
else
{
ObjCLanguageRuntime *runtime = process->GetObjCLanguageRuntime();
if (runtime == NULL)
s.Printf(") ");
else
{
ObjCLanguageRuntime::ObjCISA isa = runtime->GetISA(*valobj);
if (!runtime->IsValidISA(isa))
s.Printf(") ");
else
s.Printf(", dynamic type: %s) ",
runtime->GetActualTypeName(isa).GetCString());
}
}
}
else
s.Printf(") ");
}
if (flat_output)
@ -2758,7 +2816,7 @@ ValueObject::CreateConstantValue (const ConstString &name)
{
ValueObjectSP valobj_sp;
if (UpdateValueIfNeeded() && m_error.Success())
if (UpdateValueIfNeeded(false) && m_error.Success())
{
ExecutionContextScope *exe_scope = GetExecutionContextScope();
if (exe_scope)
@ -2957,7 +3015,6 @@ ValueObject::CastPointerType (const char *name, TypeSP &type_sp)
return valobj_sp;
}
ValueObject::EvaluationPoint::EvaluationPoint () :
m_thread_id (LLDB_INVALID_UID),
m_stop_id (0)

View File

@ -97,7 +97,7 @@ ValueObjectChild::UpdateValue ()
ValueObject* parent = m_parent;
if (parent)
{
if (parent->UpdateValueIfNeeded())
if (parent->UpdateValueIfNeeded(false))
{
m_value.SetContext(Value::eContextTypeClangType, m_clang_type);

View File

@ -60,7 +60,7 @@ ValueObjectDynamicValue::GetClangType ()
ConstString
ValueObjectDynamicValue::GetTypeName()
{
const bool success = UpdateValueIfNeeded();
const bool success = UpdateValueIfNeeded(false);
if (success && m_type_sp)
return ClangASTType::GetConstTypeName (GetClangType());
else
@ -70,7 +70,7 @@ ValueObjectDynamicValue::GetTypeName()
uint32_t
ValueObjectDynamicValue::CalculateNumChildren()
{
const bool success = UpdateValueIfNeeded();
const bool success = UpdateValueIfNeeded(false);
if (success && m_type_sp)
return ClangASTContext::GetNumChildren (GetClangAST (), GetClangType(), true);
else
@ -90,7 +90,7 @@ ValueObjectDynamicValue::GetClangAST ()
size_t
ValueObjectDynamicValue::GetByteSize()
{
const bool success = UpdateValueIfNeeded();
const bool success = UpdateValueIfNeeded(false);
if (success && m_type_sp)
return m_value.GetValueByteSize(GetClangAST(), NULL);
else
@ -109,7 +109,7 @@ ValueObjectDynamicValue::UpdateValue ()
SetValueIsValid (false);
m_error.Clear();
if (!m_parent->UpdateValueIfNeeded())
if (!m_parent->UpdateValueIfNeeded(false))
{
// The dynamic value failed to get an error, pass the error along
if (m_error.Success() && m_parent->GetError().Fail())

View File

@ -63,6 +63,30 @@ public:
virtual lldb::ThreadPlanSP
GetStepThroughTrampolinePlan (Thread &thread, bool stop_others);
virtual bool
IsValidISA(ObjCISA isa)
{
return false;
}
virtual ObjCISA
GetISA(ValueObject& valobj)
{
return 0;
}
virtual ConstString
GetActualTypeName(ObjCISA isa)
{
return ConstString(NULL);
}
virtual ObjCISA
GetParentClass(ObjCISA isa)
{
return 0;
}
//------------------------------------------------------------------
// Static Functions
//------------------------------------------------------------------

View File

@ -72,6 +72,31 @@ public:
{
return eAppleObjC_V1;
}
virtual bool
IsValidISA(ObjCISA isa)
{
return false;
}
virtual ObjCISA
GetISA(ValueObject& valobj)
{
return 0;
}
virtual ConstString
GetActualTypeName(ObjCISA isa)
{
return ConstString(NULL);
}
virtual ObjCISA
GetParentClass(ObjCISA isa)
{
return 0;
}
protected:
private:

View File

@ -563,4 +563,90 @@ AppleObjCRuntimeV2::GetByteOffsetForIvar (ClangASTType &parent_ast_type, const c
return ivar_offset;
}
lldb_private::ObjCLanguageRuntime::ObjCISA
AppleObjCRuntimeV2::GetISA(ValueObject& valobj)
{
if (ClangASTType::GetMinimumLanguage(valobj.GetClangAST(),valobj.GetClangType()) != lldb::eLanguageTypeObjC)
return 0;
uint32_t offset = 0;
uint64_t isa_pointer = valobj.GetDataExtractor().GetPointer(&offset);
uint8_t pointer_size = valobj.GetUpdatePoint().GetProcessSP()->GetAddressByteSize();
Error error;
lldb_private::ObjCLanguageRuntime::ObjCISA isa =
valobj.GetUpdatePoint().GetProcessSP()->ReadUnsignedIntegerFromMemory(isa_pointer,
pointer_size,
0,
error);
return isa;
}
ConstString
AppleObjCRuntimeV2::GetActualTypeName(lldb_private::ObjCLanguageRuntime::ObjCISA isa)
{
if (!IsValidISA(isa))
return ConstString(NULL);
uint8_t pointer_size = m_process->GetAddressByteSize();
Error error;
lldb::addr_t rw_pointer = isa + (4 * pointer_size);
//printf("rw_pointer: %llx\n", rw_pointer);
uint64_t data_pointer = m_process->ReadUnsignedIntegerFromMemory(rw_pointer,
pointer_size,
0,
error);
if (error.Fail())
return ConstString("unknown");
data_pointer += 8;
//printf("data_pointer: %llx\n", data_pointer);
uint64_t ro_pointer = m_process->ReadUnsignedIntegerFromMemory(data_pointer,
pointer_size,
0,
error);
if (error.Fail())
return ConstString("unknown");
ro_pointer += 12;
if (pointer_size == 8)
ro_pointer += 4;
ro_pointer += pointer_size;
//printf("ro_pointer: %llx\n", ro_pointer);
uint64_t name_pointer = m_process->ReadUnsignedIntegerFromMemory(ro_pointer,
pointer_size,
0,
error);
if (error.Fail())
return ConstString("unknown");
//printf("name_pointer: %llx\n", name_pointer);
char* cstr = new char[512];
if (m_process->ReadCStringFromMemory(name_pointer, cstr, 512) > 0)
return ConstString(cstr);
else
return ConstString("unknown");
}
lldb_private::ObjCLanguageRuntime::ObjCISA
AppleObjCRuntimeV2::GetParentClass(lldb_private::ObjCLanguageRuntime::ObjCISA isa)
{
if (!IsValidISA(isa))
return 0;
uint8_t pointer_size = m_process->GetAddressByteSize();
Error error;
lldb::addr_t parent_pointer = isa + pointer_size;
//printf("rw_pointer: %llx\n", rw_pointer);
uint64_t parent_isa = m_process->ReadUnsignedIntegerFromMemory(parent_pointer,
pointer_size,
0,
error);
if (error.Fail())
return 0;
return parent_isa;
}

View File

@ -76,7 +76,21 @@ public:
virtual size_t
GetByteOffsetForIvar (ClangASTType &parent_qual_type, const char *ivar_name);
virtual bool
IsValidISA(ObjCISA isa)
{
return (isa != 0);
}
virtual ObjCISA
GetISA(ValueObject& valobj);
virtual ConstString
GetActualTypeName(ObjCISA isa);
virtual ObjCISA
GetParentClass(ObjCISA isa);
protected:

View File

@ -159,11 +159,13 @@ ClangASTType::GetEncoding (uint32_t &count)
lldb::LanguageType
ClangASTType::GetMinimumLanguage ()
{
return ClangASTType::GetMinimumLanguage (m_type);
return ClangASTType::GetMinimumLanguage (m_ast,
m_type);
}
lldb::LanguageType
ClangASTType::GetMinimumLanguage (lldb::clang_type_t clang_type)
ClangASTType::GetMinimumLanguage (clang::ASTContext *ctx,
lldb::clang_type_t clang_type)
{
if (clang_type == NULL)
return lldb::eLanguageTypeC;
@ -182,6 +184,8 @@ ClangASTType::GetMinimumLanguage (lldb::clang_type_t clang_type)
return lldb::eLanguageTypeObjC;
if (pointee_type->isObjCClassType())
return lldb::eLanguageTypeObjC;
if (pointee_type.getTypePtr() == ctx->ObjCBuiltinIdTy.getTypePtr())
return lldb::eLanguageTypeObjC;
}
else
{
@ -238,7 +242,8 @@ ClangASTType::GetMinimumLanguage (lldb::clang_type_t clang_type)
}
break;
case clang::Type::Typedef:
return GetMinimumLanguage(llvm::cast<clang::TypedefType>(qual_type)->getDecl()->getUnderlyingType().getAsOpaquePtr());
return GetMinimumLanguage(ctx,
llvm::cast<clang::TypedefType>(qual_type)->getDecl()->getUnderlyingType().getAsOpaquePtr());
}
}
return lldb::eLanguageTypeC;

View File

@ -286,7 +286,7 @@ class DataFormatterTestCase(TestBase):
substrs = ['ACircle',
'ARectangle',
'ACircle',
'ARectangleStar'])
'ARectangle'])
# Check that abruptly deleting an enabled category does not crash us
self.runCmd("type category delete RectangleCategory")

View File

@ -164,7 +164,7 @@ class DataFormatterTestCase(TestBase):
'inline = ',
'explicit = ',
'content = ',
'a very much boring task to write a string this way!!\\xe4\\x8c\\xb3'])
'a very much boring task to write a string this way!!\\xcf\\x83'])
self.expect('frame variable str10 -P 1 -Y',
substrs = ['mutable =',
@ -215,7 +215,7 @@ class DataFormatterTestCase(TestBase):
self.expect('frame variable str8',
substrs = ['hasVeryLongExtensionThisTime'])
self.expect('frame variable str9',
substrs = ['a very much boring task to write a string this way!!\\xe4\\x8c\\xb3'])
substrs = ['a very much boring task to write a string this way!!\\xcf\\x83'])
self.expect('frame variable str10',
substrs = ['This is a Unicode string \\xcf\\x83 number 4 right here'])
self.expect('frame variable str11',
@ -224,6 +224,25 @@ class DataFormatterTestCase(TestBase):
substrs = ['a.out'])
self.expect('frame variable str12',
substrs = ['Process Name: a.out Process Id:'])
self.expect('frame variable dyn_test', matching=False,
substrs = ['Process Name: a.out Process Id:'])
self.expect('frame variable dyn_test -d run-target',
substrs = ['(id, dynamic type:',
'Process Name: a.out Process Id:'])
# check that we can format stuff out of the expression parser
self.expect('expression ((id)@"Hello")', matching=False,
substrs = ['Hello'])
self.expect('expression -d true -- ((id)@"Hello")',
substrs = ['Hello'])
self.expect('expr -d true -- label1',
substrs = ['Process Name'])
self.expect('expr -d true -- @"Hello"',
substrs = ['Hello'])
if __name__ == '__main__':
import atexit

View File

@ -117,6 +117,8 @@ int main (int argc, const char * argv[])
NSString *processID = [NSString stringWithFormat:@"%d", [[NSProcessInfo processInfo] processIdentifier]];
NSString *str12 = [NSString stringWithFormat:@"%@ %@ %@ %@", label1, processName, label2, processID];
id dyn_test = str12;
// Set break point at this line.
[pool drain];
return 0;