forked from OSchip/llvm-project
- Now using ${var} as the summary for an aggregate type will produce "name-of-type @ object-location" instead of giving an error
e.g. you may get "foo_class @ 0x123456" when typing "type summary add -f ${var} foo_class" - Added a new special formatting token %T for summaries. This shows the type of the object. Using it, the new "type @ location" summary could be manually generated by writing ${var%T} @ ${var%L} - Bits and pieces required to support "frame variable array[n-m]" The feature is not enabled yet because some additional design and support code is required, but the basics are getting there - Fixed a potential issue where a ValueObjectSyntheticFilter was not holding on to its SyntheticChildrenSP Because of the way VOSF are being built now, this has never been an actual issue, but it is still sensible for a VOSF to hold on to the SyntheticChildrenSP as well as to its FrontEnd llvm-svn: 138080
This commit is contained in:
parent
4ad83e69a0
commit
d64d0bc0ea
|
@ -384,6 +384,241 @@ public:
|
|||
|
||||
};
|
||||
|
||||
struct SyntheticArrayRange
|
||||
{
|
||||
private:
|
||||
int m_low;
|
||||
int m_high;
|
||||
SyntheticArrayRange* m_next;
|
||||
|
||||
public:
|
||||
|
||||
SyntheticArrayRange () :
|
||||
m_low(-1),
|
||||
m_high(-2),
|
||||
m_next(NULL)
|
||||
{}
|
||||
|
||||
SyntheticArrayRange (int L) :
|
||||
m_low(L),
|
||||
m_high(L),
|
||||
m_next(NULL)
|
||||
{}
|
||||
|
||||
SyntheticArrayRange (int L, int H) :
|
||||
m_low(L),
|
||||
m_high(H),
|
||||
m_next(NULL)
|
||||
{}
|
||||
|
||||
SyntheticArrayRange (int L, int H, SyntheticArrayRange* N) :
|
||||
m_low(L),
|
||||
m_high(H),
|
||||
m_next(N)
|
||||
{}
|
||||
|
||||
inline int
|
||||
GetLow ()
|
||||
{
|
||||
return m_low;
|
||||
}
|
||||
|
||||
inline int
|
||||
GetHigh ()
|
||||
{
|
||||
return m_high;
|
||||
}
|
||||
|
||||
inline void
|
||||
SetLow (int L)
|
||||
{
|
||||
m_low = L;
|
||||
}
|
||||
|
||||
inline void
|
||||
SetHigh (int H)
|
||||
{
|
||||
m_high = H;
|
||||
}
|
||||
|
||||
inline int
|
||||
GetSelfCount()
|
||||
{
|
||||
return GetHigh() - GetLow() + 1;
|
||||
}
|
||||
|
||||
int
|
||||
GetCount()
|
||||
{
|
||||
int count = GetSelfCount();
|
||||
if (m_next)
|
||||
count += m_next->GetCount();
|
||||
return count;
|
||||
}
|
||||
|
||||
inline SyntheticArrayRange*
|
||||
GetNext()
|
||||
{
|
||||
return m_next;
|
||||
}
|
||||
|
||||
void
|
||||
SetNext(SyntheticArrayRange* N)
|
||||
{
|
||||
if (m_next)
|
||||
delete m_next;
|
||||
m_next = N;
|
||||
}
|
||||
|
||||
void
|
||||
SetNext(int L, int H)
|
||||
{
|
||||
if (m_next)
|
||||
delete m_next;
|
||||
m_next = new SyntheticArrayRange(L, H);
|
||||
}
|
||||
|
||||
void
|
||||
SetNext(int L)
|
||||
{
|
||||
if (m_next)
|
||||
delete m_next;
|
||||
m_next = new SyntheticArrayRange(L);
|
||||
}
|
||||
|
||||
~SyntheticArrayRange()
|
||||
{
|
||||
delete m_next;
|
||||
m_next = NULL;
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
class SyntheticArrayView : public SyntheticChildren
|
||||
{
|
||||
SyntheticArrayRange m_head;
|
||||
SyntheticArrayRange *m_tail;
|
||||
public:
|
||||
SyntheticArrayView(bool casc = false,
|
||||
bool skipptr = false,
|
||||
bool skipref = false) :
|
||||
SyntheticChildren(casc, skipptr, skipref),
|
||||
m_head(),
|
||||
m_tail(&m_head)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
AddRange(int L, int H)
|
||||
{
|
||||
m_tail->SetLow(L);
|
||||
m_tail->SetHigh(H);
|
||||
m_tail->SetNext(new SyntheticArrayRange());
|
||||
m_tail = m_tail->GetNext();
|
||||
}
|
||||
|
||||
int
|
||||
GetCount()
|
||||
{
|
||||
return m_head.GetCount();
|
||||
}
|
||||
|
||||
const int
|
||||
GetRealIndexForIndex(int i)
|
||||
{
|
||||
if (i >= GetCount())
|
||||
return -1;
|
||||
|
||||
SyntheticArrayRange* ptr = &m_head;
|
||||
|
||||
int residual = i;
|
||||
|
||||
while(ptr && ptr != m_tail)
|
||||
{
|
||||
if (residual >= ptr->GetSelfCount())
|
||||
{
|
||||
residual -= ptr->GetSelfCount();
|
||||
ptr = ptr->GetNext();
|
||||
}
|
||||
|
||||
return ptr->GetLow() + residual;
|
||||
}
|
||||
|
||||
return -1;
|
||||
|
||||
}
|
||||
|
||||
bool
|
||||
IsScripted()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
std::string
|
||||
GetDescription();
|
||||
|
||||
class FrontEnd : public SyntheticChildrenFrontEnd
|
||||
{
|
||||
private:
|
||||
SyntheticArrayView* filter;
|
||||
public:
|
||||
|
||||
FrontEnd(SyntheticArrayView* flt,
|
||||
lldb::ValueObjectSP be) :
|
||||
SyntheticChildrenFrontEnd(be),
|
||||
filter(flt)
|
||||
{}
|
||||
|
||||
virtual
|
||||
~FrontEnd()
|
||||
{
|
||||
}
|
||||
|
||||
virtual uint32_t
|
||||
CalculateNumChildren()
|
||||
{
|
||||
return filter->GetCount();
|
||||
}
|
||||
|
||||
virtual lldb::ValueObjectSP
|
||||
GetChildAtIndex (uint32_t idx, bool can_create)
|
||||
{
|
||||
if (idx >= filter->GetCount())
|
||||
return lldb::ValueObjectSP();
|
||||
return m_backend->GetSyntheticArrayMember(filter->GetRealIndexForIndex(idx), can_create);
|
||||
}
|
||||
|
||||
virtual void
|
||||
Update() {}
|
||||
|
||||
virtual uint32_t
|
||||
GetIndexOfChildWithName (const ConstString &name_cs)
|
||||
{
|
||||
const char* name_cstr = name_cs.GetCString();
|
||||
if (*name_cstr != '[')
|
||||
return UINT32_MAX;
|
||||
std::string name(name_cstr+1);
|
||||
if (name[name.size()-1] != ']')
|
||||
return UINT32_MAX;
|
||||
name = name.erase(name.size()-1,1);
|
||||
int index = Args::StringToSInt32 (name.c_str(), -1);
|
||||
if (index < 0)
|
||||
return UINT32_MAX;
|
||||
return index;
|
||||
}
|
||||
|
||||
typedef lldb::SharedPtr<SyntheticChildrenFrontEnd>::Type SharedPointer;
|
||||
|
||||
};
|
||||
|
||||
virtual SyntheticChildrenFrontEnd::SharedPointer
|
||||
GetFrontEnd(lldb::ValueObjectSP backend)
|
||||
{
|
||||
return SyntheticChildrenFrontEnd::SharedPointer(new FrontEnd(this, backend));
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
|
||||
struct SummaryFormat
|
||||
{
|
||||
|
|
|
@ -79,7 +79,8 @@ public:
|
|||
eDisplaySummary,
|
||||
eDisplayLanguageSpecific,
|
||||
eDisplayLocation,
|
||||
eDisplayChildrenCount
|
||||
eDisplayChildrenCount,
|
||||
eDisplayType
|
||||
};
|
||||
|
||||
enum ExpressionPathScanEndReason
|
||||
|
@ -657,6 +658,10 @@ public:
|
|||
ValueObjectRepresentationStyle val_obj_display = eDisplaySummary,
|
||||
lldb::Format custom_format = lldb::eFormatInvalid);
|
||||
|
||||
bool
|
||||
HasSpecialCasesForPrintableRepresentation(ValueObjectRepresentationStyle val_obj_display,
|
||||
lldb::Format custom_format);
|
||||
|
||||
bool
|
||||
DumpPrintableRepresentation(Stream& s,
|
||||
ValueObjectRepresentationStyle val_obj_display = eDisplaySummary,
|
||||
|
@ -693,6 +698,9 @@ protected:
|
|||
public:
|
||||
lldb::ValueObjectSP
|
||||
GetSyntheticChild (const ConstString &key) const;
|
||||
|
||||
lldb::ValueObjectSP
|
||||
GetSyntheticArrayMember (int32_t index, bool can_create);
|
||||
|
||||
lldb::ValueObjectSP
|
||||
GetSyntheticArrayMemberFromPointer (int32_t index, bool can_create);
|
||||
|
@ -703,6 +711,9 @@ public:
|
|||
lldb::ValueObjectSP
|
||||
GetSyntheticBitFieldChild (uint32_t from, uint32_t to, bool can_create);
|
||||
|
||||
lldb::ValueObjectSP
|
||||
GetSyntheticArrayRangeChild (uint32_t from, uint32_t to, bool can_create);
|
||||
|
||||
lldb::ValueObjectSP
|
||||
GetSyntheticExpressionPathChild(const char* expression, bool can_create);
|
||||
|
||||
|
|
|
@ -24,9 +24,9 @@ namespace lldb_private {
|
|||
//----------------------------------------------------------------------
|
||||
// A ValueObject that obtains its children from some source other than
|
||||
// real information
|
||||
// This is currently used to implement children filtering, where only
|
||||
// a subset of the real children are shown, but it can be used for any
|
||||
// source of made-up children information
|
||||
// This is currently used to implement Python-based children and filters
|
||||
// but you can bind it to any source of synthetic information and have
|
||||
// it behave accordingly
|
||||
//----------------------------------------------------------------------
|
||||
class ValueObjectSynthetic : public ValueObject
|
||||
{
|
||||
|
@ -121,6 +121,7 @@ protected:
|
|||
lldb::TypeSP m_type_sp;
|
||||
lldb::ValueObjectSP m_owning_valobj_sp;
|
||||
lldb::SyntheticValueType m_use_synthetic;
|
||||
lldb::SyntheticChildrenSP m_synth_sp; // hold on to your synthetic children provider
|
||||
lldb::SyntheticChildrenFrontEndSP m_synth_filter;
|
||||
|
||||
typedef std::map<uint32_t, lldb::ValueObjectSP> ByIndexMap;
|
||||
|
|
|
@ -32,9 +32,10 @@ class StackFrame :
|
|||
public:
|
||||
enum ExpressionPathOption
|
||||
{
|
||||
eExpressionPathOptionCheckPtrVsMember = (1u << 0),
|
||||
eExpressionPathOptionsNoFragileObjcIvar = (1u << 1),
|
||||
eExpressionPathOptionsNoSyntheticChildren = (1u << 2)
|
||||
eExpressionPathOptionCheckPtrVsMember = (1u << 0),
|
||||
eExpressionPathOptionsNoFragileObjcIvar = (1u << 1),
|
||||
eExpressionPathOptionsNoSyntheticChildren = (1u << 2),
|
||||
eExpressionPathOptionsNoSyntheticArrayRange = (1u << 3),
|
||||
};
|
||||
//------------------------------------------------------------------
|
||||
// Constructors and Destructors
|
||||
|
|
|
@ -744,6 +744,8 @@ ScanFormatDescriptor(const char* var_name_begin,
|
|||
*val_obj_display = ValueObject::eDisplaySummary;
|
||||
else if (*format_name == '#')
|
||||
*val_obj_display = ValueObject::eDisplayChildrenCount;
|
||||
else if (*format_name == 'T')
|
||||
*val_obj_display = ValueObject::eDisplayType;
|
||||
else if (log)
|
||||
log->Printf("%s is an error, leaving the previous value alone", format_name);
|
||||
}
|
||||
|
@ -1165,25 +1167,52 @@ Debugger::FormatPrompt
|
|||
StreamString str_temp;
|
||||
if (log)
|
||||
log->Printf("I am into array || pointer && !range");
|
||||
// try to use the special cases
|
||||
var_success = target->DumpPrintableRepresentation(str_temp,
|
||||
val_obj_display,
|
||||
custom_format);
|
||||
if (log)
|
||||
log->Printf("special cases did%s match", var_success ? "" : "n't");
|
||||
if (!var_success)
|
||||
|
||||
if (target->HasSpecialCasesForPrintableRepresentation(val_obj_display,
|
||||
custom_format))
|
||||
{
|
||||
s << "<invalid usage of pointer value as object>";
|
||||
// try to use the special cases
|
||||
var_success = target->DumpPrintableRepresentation(str_temp,
|
||||
val_obj_display,
|
||||
custom_format);
|
||||
if (log)
|
||||
log->Printf("special cases did%s match", var_success ? "" : "n't");
|
||||
|
||||
// should not happen
|
||||
if (!var_success)
|
||||
s << "<invalid usage of pointer value as object>";
|
||||
else
|
||||
s << str_temp.GetData();
|
||||
var_success = true;
|
||||
break;
|
||||
}
|
||||
else
|
||||
s << str_temp.GetData();
|
||||
{
|
||||
// if ${var}
|
||||
if (was_plain_var)
|
||||
{
|
||||
s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
|
||||
}
|
||||
else
|
||||
{
|
||||
s << "<invalid usage of pointer value as object>";
|
||||
}
|
||||
var_success = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// if directly trying to print ${var}, and this is an aggregate, display a nice
|
||||
// type @ location message
|
||||
if (is_aggregate && was_plain_var)
|
||||
{
|
||||
s << target->GetTypeName() << " @ " << target->GetLocationAsCString();
|
||||
var_success = true;
|
||||
break;
|
||||
}
|
||||
|
||||
// if directly trying to print ${var} using its value, and this is an aggregate, display a nice
|
||||
// error message about it (and avoid recursion in DumpPrintableRepresentation)
|
||||
if (is_aggregate && ((was_var_format && val_obj_display == ValueObject::eDisplayValue) || was_plain_var))
|
||||
// if directly trying to print ${var%V}, and this is an aggregate, do not let the user do it
|
||||
if (is_aggregate && ((was_var_format && val_obj_display == ValueObject::eDisplayValue)))
|
||||
{
|
||||
s << "<invalid use of aggregate type>";
|
||||
var_success = true;
|
||||
|
|
|
@ -181,6 +181,31 @@ SyntheticFilter::GetDescription()
|
|||
return sstr.GetString();
|
||||
}
|
||||
|
||||
std::string
|
||||
SyntheticArrayView::GetDescription()
|
||||
{
|
||||
StreamString sstr;
|
||||
sstr.Printf("%s%s%s {\n",
|
||||
m_cascades ? "" : " (not cascading)",
|
||||
m_skip_pointers ? " (skip pointers)" : "",
|
||||
m_skip_references ? " (skip references)" : "");
|
||||
SyntheticArrayRange* ptr = &m_head;
|
||||
while (ptr && ptr != m_tail)
|
||||
{
|
||||
if (ptr->GetLow() == ptr->GetHigh())
|
||||
sstr.Printf(" [%d]\n",
|
||||
ptr->GetLow());
|
||||
else
|
||||
sstr.Printf(" [%d-%d]\n",
|
||||
ptr->GetLow(),
|
||||
ptr->GetHigh());
|
||||
ptr = ptr->GetNext();
|
||||
}
|
||||
|
||||
sstr.Printf("}");
|
||||
return sstr.GetString();
|
||||
}
|
||||
|
||||
SyntheticScriptProvider::FrontEnd::FrontEnd(std::string pclass,
|
||||
lldb::ValueObjectSP be) :
|
||||
SyntheticChildrenFrontEnd(be),
|
||||
|
|
|
@ -989,6 +989,9 @@ ValueObject::GetPrintableRepresentation(Stream& s,
|
|||
snprintf((char*)return_value, 512, "%d", count);
|
||||
break;
|
||||
}
|
||||
case eDisplayType:
|
||||
return_value = GetTypeName().AsCString();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -1007,7 +1010,11 @@ ValueObject::GetPrintableRepresentation(Stream& s,
|
|||
// this thing has no value, and it seems to have no summary
|
||||
// some combination of unitialized data and other factors can also
|
||||
// raise this condition, so let's print a nice generic error message
|
||||
return_value = "<no available summary>";
|
||||
{
|
||||
alloc_mem.resize(684);
|
||||
return_value = &alloc_mem[0];
|
||||
snprintf((char*)return_value, 684, "%s @ %s", GetTypeName().AsCString(), GetLocationAsCString());
|
||||
}
|
||||
}
|
||||
else
|
||||
return_value = GetValueAsCString();
|
||||
|
@ -1026,6 +1033,50 @@ ValueObject::GetPrintableRepresentation(Stream& s,
|
|||
|
||||
}
|
||||
|
||||
// if any more "special cases" are added to ValueObject::DumpPrintableRepresentation() please keep
|
||||
// this call up to date by returning true for your new special cases. We will eventually move
|
||||
// to checking this call result before trying to display special cases
|
||||
bool
|
||||
ValueObject::HasSpecialCasesForPrintableRepresentation(ValueObjectRepresentationStyle val_obj_display,
|
||||
lldb::Format custom_format)
|
||||
{
|
||||
clang_type_t elem_or_pointee_type;
|
||||
Flags flags(ClangASTContext::GetTypeInfo(GetClangType(), GetClangAST(), &elem_or_pointee_type));
|
||||
|
||||
if (flags.AnySet(ClangASTContext::eTypeIsArray | ClangASTContext::eTypeIsPointer)
|
||||
&& val_obj_display == ValueObject::eDisplayValue)
|
||||
{
|
||||
if (IsCStringContainer(true) &&
|
||||
(custom_format == lldb::eFormatCString ||
|
||||
custom_format == lldb::eFormatCharArray ||
|
||||
custom_format == lldb::eFormatChar ||
|
||||
custom_format == lldb::eFormatVectorOfChar))
|
||||
return true;
|
||||
|
||||
if (flags.Test(ClangASTContext::eTypeIsArray))
|
||||
{
|
||||
if ((custom_format == lldb::eFormatBytes) ||
|
||||
(custom_format == lldb::eFormatBytesWithASCII))
|
||||
return true;
|
||||
|
||||
if ((custom_format == lldb::eFormatVectorOfChar) ||
|
||||
(custom_format == lldb::eFormatVectorOfFloat32) ||
|
||||
(custom_format == lldb::eFormatVectorOfFloat64) ||
|
||||
(custom_format == lldb::eFormatVectorOfSInt16) ||
|
||||
(custom_format == lldb::eFormatVectorOfSInt32) ||
|
||||
(custom_format == lldb::eFormatVectorOfSInt64) ||
|
||||
(custom_format == lldb::eFormatVectorOfSInt8) ||
|
||||
(custom_format == lldb::eFormatVectorOfUInt128) ||
|
||||
(custom_format == lldb::eFormatVectorOfUInt16) ||
|
||||
(custom_format == lldb::eFormatVectorOfUInt32) ||
|
||||
(custom_format == lldb::eFormatVectorOfUInt64) ||
|
||||
(custom_format == lldb::eFormatVectorOfUInt8))
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool
|
||||
ValueObject::DumpPrintableRepresentation(Stream& s,
|
||||
ValueObjectRepresentationStyle val_obj_display,
|
||||
|
@ -1380,6 +1431,19 @@ ValueObject::IsPossibleDynamicType ()
|
|||
return ClangASTContext::IsPossibleDynamicType (GetClangAST (), GetClangType());
|
||||
}
|
||||
|
||||
lldb::ValueObjectSP
|
||||
ValueObject::GetSyntheticArrayMember (int32_t index, bool can_create)
|
||||
{
|
||||
if (IsArrayType())
|
||||
return GetSyntheticArrayMemberFromArray(index, can_create);
|
||||
|
||||
if (IsPointerType())
|
||||
return GetSyntheticArrayMemberFromPointer(index, can_create);
|
||||
|
||||
return ValueObjectSP();
|
||||
|
||||
}
|
||||
|
||||
ValueObjectSP
|
||||
ValueObject::GetSyntheticArrayMemberFromPointer (int32_t index, bool can_create)
|
||||
{
|
||||
|
@ -1496,6 +1560,42 @@ ValueObject::GetSyntheticBitFieldChild (uint32_t from, uint32_t to, bool can_cre
|
|||
return synthetic_child_sp;
|
||||
}
|
||||
|
||||
lldb::ValueObjectSP
|
||||
ValueObject::GetSyntheticArrayRangeChild (uint32_t from, uint32_t to, bool can_create)
|
||||
{
|
||||
ValueObjectSP synthetic_child_sp;
|
||||
if (IsArrayType () || IsPointerType ())
|
||||
{
|
||||
char index_str[64];
|
||||
snprintf(index_str, sizeof(index_str), "[%i-%i]", from, to);
|
||||
ConstString index_const_str(index_str);
|
||||
// Check if we have already created a synthetic array member in this
|
||||
// valid object. If we have we will re-use it.
|
||||
synthetic_child_sp = GetSyntheticChild (index_const_str);
|
||||
if (!synthetic_child_sp)
|
||||
{
|
||||
ValueObjectSynthetic *synthetic_child;
|
||||
|
||||
// We haven't made a synthetic array member for INDEX yet, so
|
||||
// lets make one and cache it for any future reference.
|
||||
SyntheticArrayView *view = new SyntheticArrayView();
|
||||
view->AddRange(from,to);
|
||||
SyntheticChildrenSP view_sp(view);
|
||||
synthetic_child = new ValueObjectSynthetic(*this, view_sp);
|
||||
|
||||
// Cache the value if we got one back...
|
||||
if (synthetic_child)
|
||||
{
|
||||
AddSyntheticChild(index_const_str, synthetic_child);
|
||||
synthetic_child_sp = synthetic_child->GetSP();
|
||||
synthetic_child_sp->SetName(ConstString(index_str));
|
||||
synthetic_child_sp->m_is_bitfield_for_scalar = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return synthetic_child_sp;
|
||||
}
|
||||
|
||||
lldb::ValueObjectSP
|
||||
ValueObject::GetSyntheticChildAtOffset(uint32_t offset, const ClangASTType& type, bool can_create)
|
||||
{
|
||||
|
|
|
@ -40,6 +40,7 @@ ValueObjectSynthetic::ValueObjectSynthetic (ValueObject &parent, lldb::Synthetic
|
|||
m_address (),
|
||||
m_type_sp(),
|
||||
m_use_synthetic (lldb::eUseSyntheticFilter),
|
||||
m_synth_sp(filter),
|
||||
m_synth_filter(filter->GetFrontEnd(parent.GetSP())),
|
||||
m_children_byindex(),
|
||||
m_name_toindex()
|
||||
|
|
|
@ -526,6 +526,7 @@ StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr,
|
|||
const bool check_ptr_vs_member = (options & eExpressionPathOptionCheckPtrVsMember) != 0;
|
||||
const bool no_fragile_ivar = (options & eExpressionPathOptionsNoFragileObjcIvar) != 0;
|
||||
const bool no_synth_child = (options & eExpressionPathOptionsNoSyntheticChildren) != 0;
|
||||
const bool no_synth_array = (options & eExpressionPathOptionsNoSyntheticArrayRange) != 0;
|
||||
error.Clear();
|
||||
bool deref = false;
|
||||
bool address_of = false;
|
||||
|
@ -861,6 +862,7 @@ StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr,
|
|||
// this is most probably a BitField, let's take a look
|
||||
char *real_end = NULL;
|
||||
long final_index = ::strtol (end+1, &real_end, 0);
|
||||
bool expand_bitfield = true;
|
||||
if (real_end && *real_end == ']')
|
||||
{
|
||||
// if the format given is [high-low], swap range
|
||||
|
@ -908,15 +910,31 @@ StackFrame::GetValueForVariableExpressionPath (const char *var_expr_cstr,
|
|||
valobj_sp = temp;
|
||||
deref = false;
|
||||
}
|
||||
|
||||
child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild(child_index, final_index, true);
|
||||
if (!child_valobj_sp)
|
||||
/*else if (valobj_sp->IsArrayType() || valobj_sp->IsPointerType())
|
||||
{
|
||||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||||
error.SetErrorStringWithFormat ("bitfield range %i-%i is not valid for \"(%s) %s\"",
|
||||
child_index, final_index,
|
||||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||||
var_expr_path_strm.GetString().c_str());
|
||||
child_valobj_sp = valobj_sp->GetSyntheticArrayRangeChild(child_index, final_index, true);
|
||||
expand_bitfield = false;
|
||||
if (!child_valobj_sp)
|
||||
{
|
||||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||||
error.SetErrorStringWithFormat ("array range %i-%i is not valid for \"(%s) %s\"",
|
||||
child_index, final_index,
|
||||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||||
var_expr_path_strm.GetString().c_str());
|
||||
}
|
||||
}*/
|
||||
|
||||
if (expand_bitfield)
|
||||
{
|
||||
child_valobj_sp = valobj_sp->GetSyntheticBitFieldChild(child_index, final_index, true);
|
||||
if (!child_valobj_sp)
|
||||
{
|
||||
valobj_sp->GetExpressionPath (var_expr_path_strm, false);
|
||||
error.SetErrorStringWithFormat ("bitfield range %i-%i is not valid for \"(%s) %s\"",
|
||||
child_index, final_index,
|
||||
valobj_sp->GetTypeName().AsCString("<invalid type>"),
|
||||
var_expr_path_strm.GetString().c_str());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -195,7 +195,34 @@ class DataFormatterTestCase(TestBase):
|
|||
'[2] = cool object @ 0x',
|
||||
'[3] = cool object @ 0x',
|
||||
'[4] = cool object @ 0x'])
|
||||
|
||||
# test getting similar output by exploiting ${var} = 'type @ location' for aggregates
|
||||
self.runCmd("type summary add -f \"${var}\" i_am_cool")
|
||||
|
||||
# this test might fail if the compiler tries to store
|
||||
# these values into registers.. hopefully this is not
|
||||
# going to be the case
|
||||
self.expect("frame variable cool_array",
|
||||
substrs = ['[0] = i_am_cool @ 0x',
|
||||
'[1] = i_am_cool @ 0x',
|
||||
'[2] = i_am_cool @ 0x',
|
||||
'[3] = i_am_cool @ 0x',
|
||||
'[4] = i_am_cool @ 0x'])
|
||||
|
||||
|
||||
# test getting same output by exploiting %T and %L together for aggregates
|
||||
self.runCmd("type summary add -f \"${var%T} @ ${var%L}\" i_am_cool")
|
||||
|
||||
# this test might fail if the compiler tries to store
|
||||
# these values into registers.. hopefully this is not
|
||||
# going to be the case
|
||||
self.expect("frame variable cool_array",
|
||||
substrs = ['[0] = i_am_cool @ 0x',
|
||||
'[1] = i_am_cool @ 0x',
|
||||
'[2] = i_am_cool @ 0x',
|
||||
'[3] = i_am_cool @ 0x',
|
||||
'[4] = i_am_cool @ 0x'])
|
||||
|
||||
self.runCmd("type summary add -f \"goofy\" i_am_cool")
|
||||
self.runCmd("type summary add -f \"${var.second_cool%S}\" i_am_cooler")
|
||||
|
||||
|
|
|
@ -56,10 +56,10 @@ class DataFormatterTestCase(TestBase):
|
|||
self.runCmd("type summary add -f \"SUMMARY SUCCESS ${var}\" Summarize")
|
||||
|
||||
self.expect('frame variable mine_ptr',
|
||||
substrs = ['SUMMARY SUCCESS <invalid usage of pointer value as object>'])
|
||||
substrs = ['SUMMARY SUCCESS summarize_ptr_t @ '])
|
||||
|
||||
self.expect('frame variable *mine_ptr',
|
||||
substrs = ['SUMMARY SUCCESS <invalid use of aggregate type>'])
|
||||
substrs = ['SUMMARY SUCCESS summarize_t @'])
|
||||
|
||||
self.runCmd("type summary add -f \"SUMMARY SUCCESS ${var.first}\" Summarize")
|
||||
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
LEVEL = ../../../make
|
||||
|
||||
CXX_SOURCES := main.cpp
|
||||
|
||||
include $(LEVEL)/Makefile.rules
|
|
@ -0,0 +1,90 @@
|
|||
"""
|
||||
Test lldb data formatter subsystem.
|
||||
"""
|
||||
|
||||
import os, time
|
||||
import unittest2
|
||||
import lldb
|
||||
from lldbtest import *
|
||||
|
||||
class DataFormatterTestCase(TestBase):
|
||||
|
||||
# test for rdar://problem/9973992 (What should we do for "${var}" in summaries of aggregate types?)
|
||||
mydir = os.path.join("functionalities", "data-formatter", "rdar-9973992")
|
||||
|
||||
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
|
||||
def test_with_dsym_and_run_command(self):
|
||||
"""Test data formatter commands."""
|
||||
self.buildDsym()
|
||||
self.data_formatter_commands()
|
||||
|
||||
def test_with_dwarf_and_run_command(self):
|
||||
"""Test data formatter commands."""
|
||||
self.buildDwarf()
|
||||
self.data_formatter_commands()
|
||||
|
||||
def setUp(self):
|
||||
# Call super's setUp().
|
||||
TestBase.setUp(self)
|
||||
# Find the line number to break at.
|
||||
self.line = line_number('main.cpp', '// Set break point at this line.')
|
||||
|
||||
def data_formatter_commands(self):
|
||||
"""Test that that file and class static variables display correctly."""
|
||||
self.runCmd("file a.out", CURRENT_EXECUTABLE_SET)
|
||||
|
||||
self.expect("breakpoint set -f main.cpp -l %d" % self.line,
|
||||
BREAKPOINT_CREATED,
|
||||
startstr = "Breakpoint created: 1: file ='main.cpp', line = %d, locations = 1" %
|
||||
self.line)
|
||||
|
||||
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 summary clear', check=False)
|
||||
|
||||
# Execute the cleanup function during test case tear down.
|
||||
self.addTearDownHook(cleanup)
|
||||
|
||||
self.runCmd("type summary add -f \"SUMMARY SUCCESS ${var}\" Summarize")
|
||||
|
||||
self.expect('frame variable mine_ptr',
|
||||
substrs = ['SUMMARY SUCCESS summarize_ptr_t @ '])
|
||||
|
||||
self.expect('frame variable *mine_ptr',
|
||||
substrs = ['SUMMARY SUCCESS summarize_t @'])
|
||||
|
||||
self.runCmd("type summary add -f \"SUMMARY SUCCESS ${var.first}\" Summarize")
|
||||
|
||||
self.expect('frame variable mine_ptr',
|
||||
substrs = ['SUMMARY SUCCESS 10'])
|
||||
|
||||
self.expect('frame variable *mine_ptr',
|
||||
substrs = ['SUMMARY SUCCESS 10'])
|
||||
|
||||
self.runCmd("type summary add -f \"${var}\" Summarize")
|
||||
self.runCmd("type summary add -f \"${var}\" -e TwoSummarizes")
|
||||
|
||||
self.expect('frame variable',
|
||||
substrs = ['(TwoSummarizes) twos = TwoSummarizes @ ',
|
||||
'first = summarize_t @ ',
|
||||
'second = summarize_t @ '])
|
||||
|
||||
self.runCmd("type summary add -f \"SUMMARY SUCCESS ${var.first}\" Summarize")
|
||||
self.expect('frame variable',
|
||||
substrs = ['(TwoSummarizes) twos = TwoSummarizes @ ',
|
||||
'first = SUMMARY SUCCESS 1',
|
||||
'second = SUMMARY SUCCESS 3'])
|
||||
|
||||
if __name__ == '__main__':
|
||||
import atexit
|
||||
lldb.SBDebugger.Initialize()
|
||||
atexit.register(lambda: lldb.SBDebugger.Terminate())
|
||||
unittest2.main()
|
|
@ -0,0 +1,41 @@
|
|||
//===-- main.cpp ------------------------------------------------*- C++ -*-===//
|
||||
//
|
||||
// The LLVM Compiler Infrastructure
|
||||
//
|
||||
// This file is distributed under the University of Illinois Open Source
|
||||
// License. See LICENSE.TXT for details.
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include <stdio.h>
|
||||
struct Summarize
|
||||
{
|
||||
int first;
|
||||
int second;
|
||||
};
|
||||
|
||||
typedef struct Summarize summarize_t;
|
||||
typedef summarize_t *summarize_ptr_t;
|
||||
|
||||
summarize_t global_mine = {30, 40};
|
||||
|
||||
struct TwoSummarizes
|
||||
{
|
||||
summarize_t first;
|
||||
summarize_t second;
|
||||
};
|
||||
|
||||
int
|
||||
main()
|
||||
{
|
||||
summarize_t mine = {10, 20};
|
||||
summarize_ptr_t mine_ptr = &mine;
|
||||
|
||||
TwoSummarizes twos = { {1,2}, {3,4} };
|
||||
|
||||
printf ("Summarize: first: %d second: %d and address: 0x%p\n", mine.first, mine.second, mine_ptr); // Set break point at this line.
|
||||
printf ("Global summarize: first: %d second: %d.\n", global_mine.first, global_mine.second);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue