several improvements to "type summary":

- type names can now be regular expressions (exact matching is done first, and is faster)
 - integral (and floating) types can be printed as bitfields, i.e. ${var[low-high]} will extract bits low thru high of the value and print them
 - array subscripts are supported, both for arrays and for pointers. the syntax is ${*var[low-high]}, or ${*var[]} to print the whole array (the latter only works for statically sized arrays)
 - summary is now printed by default when a summary string references a variable. if that variable's type has no summary, value is printed instead. to force value, you can use %V as a format specifier
 - basic support for ObjectiveC:
  - ObjectiveC inheritance chains are now walked through
  - %@ can be specified as a summary format, to print the ObjectiveC runtime description for an object
 - some bug fixes

llvm-svn: 134293
This commit is contained in:
Enrico Granata 2011-07-02 00:25:22 +00:00
parent bf15d2b311
commit 0a3958e046
17 changed files with 815 additions and 428 deletions

View File

@ -476,48 +476,82 @@ public:
class ValueFormats
{
public:
static bool
Get(ValueObject& vobj, ValueFormat::SharedPointer &entry);
static void
Add(const ConstString &type, const ValueFormat::SharedPointer &entry);
static bool
Delete(const ConstString &type);
static void
Clear();
static void
LoopThrough(FormatManager::ValueCallback callback, void* callback_baton);
static uint32_t GetCurrentRevision();
public:
static bool
Get(ValueObject& vobj, ValueFormat::SharedPointer &entry);
static void
Add(const ConstString &type, const ValueFormat::SharedPointer &entry);
static bool
Delete(const ConstString &type);
static void
Clear();
static void
LoopThrough(ValueFormat::ValueCallback callback, void* callback_baton);
static uint32_t
GetCurrentRevision();
static uint32_t
GetCount();
};
class SummaryFormats
{
public:
public:
static bool
Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry);
static bool
Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry);
static void
Add(const ConstString &type, const SummaryFormat::SharedPointer &entry);
static bool
Delete(const ConstString &type);
static void
Clear();
static void
LoopThrough(FormatManager::SummaryCallback callback, void* callback_baton);
static uint32_t
GetCurrentRevision();
static void
Add(const ConstString &type, const SummaryFormat::SharedPointer &entry);
static bool
Delete(const ConstString &type);
static void
Clear();
static void
LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton);
static uint32_t
GetCurrentRevision();
static uint32_t
GetCount();
};
class RegexSummaryFormats
{
public:
static bool
Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry);
static void
Add(const lldb::RegularExpressionSP &type, const SummaryFormat::SharedPointer &entry);
static bool
Delete(const ConstString &type);
static void
Clear();
static void
LoopThrough(SummaryFormat::RegexSummaryCallback callback, void* callback_baton);
static uint32_t
GetCurrentRevision();
static uint32_t
GetCount();
};
};
} // namespace lldb_private

View File

@ -33,6 +33,10 @@ namespace std
#include <stack>
// Other libraries and framework includes
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
#include "clang/AST/DeclObjC.h"
// Project includes
#include "lldb/lldb-public.h"
#include "lldb/lldb-enumerations.h"
@ -40,6 +44,7 @@ namespace std
#include "lldb/Core/Communication.h"
#include "lldb/Core/InputReaderStack.h"
#include "lldb/Core/Listener.h"
#include "lldb/Core/RegularExpression.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/SourceManager.h"
#include "lldb/Core/UserID.h"
@ -49,9 +54,6 @@ namespace std
#include "lldb/Target/Platform.h"
#include "lldb/Target/TargetList.h"
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
namespace lldb_private {
class IFormatChangeListener
@ -72,12 +74,22 @@ struct SummaryFormat
bool m_dont_show_value;
bool m_show_members_oneliner;
bool m_cascades;
SummaryFormat(std::string f = "", bool c = false, bool nochildren = true, bool novalue = true, bool oneliner = false) :
bool m_skip_references;
bool m_skip_pointers;
SummaryFormat(std::string f = "",
bool c = false,
bool nochildren = true,
bool novalue = true,
bool oneliner = false,
bool skipptr = false,
bool skipref = false) :
m_format(f),
m_dont_show_children(nochildren),
m_dont_show_value(novalue),
m_show_members_oneliner(oneliner),
m_cascades(c)
m_cascades(c),
m_skip_references(skipref),
m_skip_pointers(skipptr)
{
}
@ -100,6 +112,8 @@ struct SummaryFormat
}
typedef lldb::SharedPtr<SummaryFormat>::Type SharedPointer;
typedef bool(*SummaryCallback)(void*, const char*, const SummaryFormat::SharedPointer&);
typedef bool(*RegexSummaryCallback)(void*, lldb::RegularExpressionSP, const SummaryFormat::SharedPointer&);
};
@ -107,13 +121,21 @@ struct ValueFormat
{
lldb::Format m_format;
bool m_cascades;
ValueFormat (lldb::Format f = lldb::eFormatInvalid, bool c = false) :
bool m_skip_references;
bool m_skip_pointers;
ValueFormat (lldb::Format f = lldb::eFormatInvalid,
bool c = false,
bool skipptr = false,
bool skipref = false) :
m_format (f),
m_cascades (c)
m_cascades (c),
m_skip_references(skipref),
m_skip_pointers(skipptr)
{
}
typedef lldb::SharedPtr<ValueFormat>::Type SharedPointer;
typedef bool(*ValueCallback)(void*, const char*, const ValueFormat::SharedPointer&);
~ValueFormat()
{
@ -160,7 +182,7 @@ public:
}
bool
Delete(const MapKeyType& type)
Delete(const char* type)
{
Mutex::Locker(m_map_mutex);
MapIterator iter = m_map.find(type);
@ -197,6 +219,12 @@ public:
}
}
uint32_t
GetCount()
{
return m_map.size();
}
~FormatNavigator()
{
}
@ -210,7 +238,7 @@ private:
DISALLOW_COPY_AND_ASSIGN(FormatNavigator);
bool
Get(const MapKeyType &type, MapValueType& entry)
Get(const char* type, MapValueType& entry)
{
Mutex::Locker(m_map_mutex);
MapIterator iter = m_map.find(type);
@ -228,16 +256,62 @@ private:
return false;
clang::QualType type = q_type.getUnqualifiedType();
type.removeLocalConst(); type.removeLocalVolatile(); type.removeLocalRestrict();
ConstString name(type.getAsString().c_str());
const clang::Type* typePtr = type.getTypePtrOrNull();
if (!typePtr)
return false;
ConstString name(ClangASTType::GetTypeNameForQualType(type).c_str());
//printf("trying to get format for VO name %s of type %s\n",vobj.GetName().AsCString(),name.AsCString());
if (Get(name.GetCString(), entry))
return true;
// look for a "base type", whatever that means
const clang::Type* typePtr = type.getTypePtrOrNull();
if (!typePtr)
return false;
if (typePtr->isReferenceType())
return Get(vobj,type.getNonReferenceType(),entry);
{
if (Get(vobj,type.getNonReferenceType(),entry) && !entry->m_skip_references)
return true;
}
if (typePtr->isPointerType())
{
if (Get(vobj, typePtr->getPointeeType(), entry) && !entry->m_skip_pointers)
return true;
}
if (typePtr->isObjCObjectPointerType())
{
/*
for some reason, C++ can quite easily obtain the type hierarchy for a ValueObject
even if the VO represent a pointer-to-class, as long as the typePtr is right
Objective-C on the other hand cannot really complete an @interface when
the VO refers to a pointer-to-@interface
*/
Error error;
ValueObject* target = vobj.Dereference(error).get();
if(error.Fail() || !target)
return false;
if (Get(*target, typePtr->getPointeeType(), entry) && !entry->m_skip_pointers)
return true;
}
const clang::ObjCObjectType *objc_class_type = typePtr->getAs<clang::ObjCObjectType>();
if (objc_class_type)
{
//printf("working with ObjC\n");
clang::ASTContext *ast = vobj.GetClangAST();
if (ClangASTContext::GetCompleteType(ast, vobj.GetClangType()) && !objc_class_type->isObjCId())
{
clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface();
if(class_interface_decl)
{
//printf("down here\n");
clang::ObjCInterfaceDecl *superclass_interface_decl = class_interface_decl->getSuperClass();
//printf("one further step and we're there...\n");
if(superclass_interface_decl)
{
//printf("the end is here\n");
clang::QualType ivar_qual_type(ast->getObjCInterfaceType(superclass_interface_decl));
if (Get(vobj, ivar_qual_type, entry) && entry->m_cascades)
return true;
}
}
}
}
// for C++ classes, navigate up the hierarchy
if (typePtr->isRecordType())
{
@ -245,15 +319,7 @@ private:
if (record)
{
if (!record->hasDefinition())
// dummy call to do the complete
ClangASTContext::GetNumChildren(vobj.GetClangAST(), vobj.GetClangType(), false);
clang::IdentifierInfo *info = record->getIdentifier();
if (info) {
// this is the class name, plain and simple
ConstString id_info(info->getName().str().c_str());
if (Get(id_info.GetCString(), entry))
return true;
}
ClangASTContext::GetCompleteType(vobj.GetClangAST(), vobj.GetClangType());
if (record->hasDefinition())
{
clang::CXXRecordDecl::base_class_iterator pos,end;
@ -287,25 +353,34 @@ private:
}
};
template<>
bool
FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Get(const char* key,
SummaryFormat::SharedPointer& value);
template<>
bool
FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Delete(const char* type);
class FormatManager : public IFormatChangeListener
{
public:
typedef bool(*ValueCallback)(void*, const char*, const ValueFormat::SharedPointer&);
typedef bool(*SummaryCallback)(void*, const char*, const SummaryFormat::SharedPointer&);
private:
typedef std::map<const char*, ValueFormat::SharedPointer> ValueMap;
typedef std::map<const char*, SummaryFormat::SharedPointer> SummaryMap;
typedef std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer> RegexSummaryMap;
typedef FormatNavigator<ValueMap, ValueCallback> ValueNavigator;
typedef FormatNavigator<SummaryMap, SummaryCallback> SummaryNavigator;
typedef FormatNavigator<ValueMap, ValueFormat::ValueCallback> ValueNavigator;
typedef FormatNavigator<SummaryMap, SummaryFormat::SummaryCallback> SummaryNavigator;
typedef FormatNavigator<RegexSummaryMap, SummaryFormat::RegexSummaryCallback> RegexSummaryNavigator;
ValueNavigator m_value_nav;
SummaryNavigator m_summary_nav;
RegexSummaryNavigator m_regex_summary_nav;
uint32_t m_last_revision;
@ -314,6 +389,7 @@ public:
FormatManager() :
m_value_nav(this),
m_summary_nav(this),
m_regex_summary_nav(this),
m_last_revision(0)
{
}
@ -321,6 +397,8 @@ public:
ValueNavigator& Value() { return m_value_nav; }
SummaryNavigator& Summary() { return m_summary_nav; }
RegexSummaryNavigator& RegexSummary() { return m_regex_summary_nav; }
static bool
GetFormatFromCString (const char *format_cstr,

View File

@ -149,6 +149,9 @@ public:
//------------------------------------------------------------------
bool
IsValid () const;
bool
operator < (const RegularExpression& rhs) const;
private:
//------------------------------------------------------------------

View File

@ -70,6 +70,13 @@ public:
eDereferencePointers = 1,
eHonorPointers,
};
enum ValueObjectRepresentationStyle
{
eDisplayValue,
eDisplaySummary,
eDisplayLanguageSpecific
};
class EvaluationPoint
{
@ -344,6 +351,10 @@ public:
const char *
GetObjectDescription ();
const char *
GetPrintableRepresentation(ValueObjectRepresentationStyle val_obj_display = eDisplaySummary,
lldb::Format custom_format = lldb::eFormatInvalid);
bool
GetValueIsValid () const;
@ -352,7 +363,7 @@ public:
GetValueDidChange ();
bool
UpdateValueIfNeeded ();
UpdateValueIfNeeded (bool update_format = true);
void
UpdateFormatsIfNeeded();
@ -525,7 +536,8 @@ protected:
m_children_count_valid:1,
m_old_value_valid:1,
m_pointers_point_to_load_addrs:1,
m_is_deref_of_parent:1;
m_is_deref_of_parent:1,
m_is_array_item_for_pointer:1;
friend class ClangExpressionDeclMap; // For GetValue
friend class ClangExpressionVariable; // For SetName

View File

@ -351,6 +351,7 @@ namespace lldb {
eArgTypeExprFormat,
eArgTypeFilename,
eArgTypeFormat,
eArgTypeFormatString,
eArgTypeFrameIndex,
eArgTypeFullName,
eArgTypeFunctionName,

View File

@ -53,6 +53,7 @@ namespace lldb {
typedef SharedPtr<lldb_private::Platform>::Type PlatformSP;
typedef SharedPtr<lldb_private::Process>::Type ProcessSP;
typedef SharedPtr<lldb_private::RegisterContext>::Type RegisterContextSP;
typedef SharedPtr<lldb_private::RegularExpression>::Type RegularExpressionSP;
typedef SharedPtr<lldb_private::Section>::Type SectionSP;
typedef SharedPtr<lldb_private::SearchFilter>::Type SearchFilterSP;
typedef SharedPtr<lldb_private::StackFrame>::Type StackFrameSP;

View File

@ -15,6 +15,7 @@
#include "lldb/Core/ConstString.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/FormatManager.h"
#include "lldb/Core/RegularExpression.h"
#include "lldb/Core/State.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Interpreter/CommandObject.h"
@ -54,11 +55,14 @@ private:
switch (short_option)
{
case 'c':
case 'C':
m_cascade = Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("Invalid value for cascade: %s.\n", option_arg);
break;
case 'f':
error = Args::StringToFormat(option_arg, m_format, NULL);
break;
default:
error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option);
break;
@ -71,6 +75,7 @@ private:
OptionParsingStarting ()
{
m_cascade = true;
m_format = eFormatInvalid;
}
const OptionDefinition*
@ -86,6 +91,7 @@ private:
// Instance variables to hold the values for command options.
bool m_cascade;
lldb::Format m_format;
};
CommandOptions m_options;
@ -103,21 +109,14 @@ public:
"Add a new formatting style for a type.",
NULL), m_options (interpreter)
{
CommandArgumentEntry format_arg;
CommandArgumentData format_style_arg;
CommandArgumentEntry type_arg;
CommandArgumentData type_style_arg;
format_style_arg.arg_type = eArgTypeFormat;
format_style_arg.arg_repetition = eArgRepeatPlain;
type_style_arg.arg_type = eArgTypeName;
type_style_arg.arg_repetition = eArgRepeatPlus;
format_arg.push_back (format_style_arg);
type_arg.push_back (type_style_arg);
m_arguments.push_back (format_arg);
m_arguments.push_back (type_arg);
}
@ -130,40 +129,27 @@ public:
{
const size_t argc = command.GetArgumentCount();
if (argc < 2)
if (argc < 1)
{
result.AppendErrorWithFormat ("%s takes two or more args.\n", m_cmd_name.c_str());
result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
const char* format_cstr = command.GetArgumentAtIndex(0);
if (!format_cstr || !format_cstr[0])
if(m_options.m_format == eFormatInvalid)
{
result.AppendError("empty format strings not allowed");
result.AppendErrorWithFormat ("%s needs a valid format.\n", m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
lldb::Format format;
Error error;
ValueFormatSP entry;
error = Args::StringToFormat(format_cstr, format, NULL);
ValueFormat::SharedPointer entry;
entry.reset(new ValueFormat(format,m_options.m_cascade));
entry.reset(new ValueFormat(m_options.m_format,m_options.m_cascade));
if (error.Fail())
{
result.AppendError(error.AsCString());
result.SetStatus(eReturnStatusFailed);
return false;
}
// now I have a valid format, let's add it to every type
for(int i = 1; i < argc; i++) {
for(int i = 0; i < argc; i++) {
const char* typeA = command.GetArgumentAtIndex(i);
ConstString typeCS(typeA);
if (typeCS)
@ -179,13 +165,13 @@ public:
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
}
};
OptionDefinition
CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "cascade", 'c', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."},
{ LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."},
{ LLDB_OPT_SET_ALL, false, "format", 'f', required_argument, NULL, 0, eArgTypeFormat, "The format to use to display this type."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};
@ -411,25 +397,31 @@ private:
switch (short_option)
{
case 'c':
case 'C':
m_cascade = Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("Invalid value for cascade: %s.\n", option_arg);
break;
case 'h':
m_no_children = !Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("Invalid value for nochildren: %s.\n", option_arg);
case 'e':
m_no_children = false;
break;
case 'v':
m_no_value = !Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("Invalid value for novalue: %s.\n", option_arg);
m_no_value = true;
break;
case 'o':
m_one_liner = Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("Invalid value for oneliner: %s.\n", option_arg);
case 'c':
m_one_liner = true;
break;
case 'f':
m_format_string = std::string(option_arg);
break;
case 'p':
m_skip_pointers = true;
break;
case 'r':
m_skip_references = true;
break;
case 'x':
m_regex = true;
break;
default:
error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option);
@ -446,6 +438,9 @@ private:
m_no_children = true;
m_no_value = false;
m_one_liner = false;
m_skip_references = false;
m_skip_pointers = false;
m_regex = false;
}
const OptionDefinition*
@ -464,6 +459,10 @@ private:
bool m_no_children;
bool m_no_value;
bool m_one_liner;
bool m_skip_references;
bool m_skip_pointers;
bool m_regex;
std::string m_format_string;
};
CommandOptions m_options;
@ -481,21 +480,14 @@ public:
"Add a new summary style for a type.",
NULL), m_options (interpreter)
{
CommandArgumentEntry format_arg;
CommandArgumentData format_style_arg;
CommandArgumentEntry type_arg;
CommandArgumentData type_style_arg;
format_style_arg.arg_type = eArgTypeFormat;
format_style_arg.arg_repetition = eArgRepeatPlain;
type_style_arg.arg_type = eArgTypeName;
type_style_arg.arg_repetition = eArgRepeatPlus;
format_arg.push_back (format_style_arg);
type_arg.push_back (type_style_arg);
m_arguments.push_back (format_arg);
m_arguments.push_back (type_arg);
}
@ -508,34 +500,29 @@ public:
{
const size_t argc = command.GetArgumentCount();
// we support just one custom syntax: type summary add -o yes typeName
// anything else, must take the usual route
// e.g. type summary add -o yes "" type1 type2 ... typeN
bool isValidShortcut = m_options.m_one_liner && (argc == 1);
bool isValid = (argc >= 2);
if (!isValidShortcut && !isValid)
if (argc < 1)
{
result.AppendErrorWithFormat ("%s takes two or more args.\n", m_cmd_name.c_str());
result.AppendErrorWithFormat ("%s takes one or more args.\n", m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
const char* format_cstr = (isValidShortcut ? "" : command.GetArgumentAtIndex(0));
if ( (!format_cstr || !format_cstr[0]) && !m_options.m_one_liner )
if(!m_options.m_one_liner && m_options.m_format_string.empty())
{
result.AppendError("empty summary strings not allowed");
result.SetStatus(eReturnStatusFailed);
return false;
}
const char* format_cstr = (m_options.m_one_liner ? "" : m_options.m_format_string.c_str());
Error error;
SummaryFormat::SharedPointer entry(new SummaryFormat(format_cstr,m_options.m_cascade,
m_options.m_no_children,m_options.m_no_value,
m_options.m_one_liner));
m_options.m_one_liner,
m_options.m_skip_pointers,
m_options.m_skip_references));
if (error.Fail())
{
@ -546,17 +533,31 @@ public:
// now I have a valid format, let's add it to every type
for(int i = (isValidShortcut ? 0 : 1); i < argc; i++) {
for(int i = 0; i < argc; i++) {
const char* typeA = command.GetArgumentAtIndex(i);
ConstString typeCS(typeA);
if (typeCS)
Debugger::SummaryFormats::Add(typeCS, entry);
else
if(!typeA || typeA[0] == '\0')
{
result.AppendError("empty typenames not allowed");
result.SetStatus(eReturnStatusFailed);
return false;
}
ConstString typeCS(typeA);
if(!m_options.m_regex)
{
Debugger::SummaryFormats::Add(typeCS, entry);
}
else
{
RegularExpressionSP typeRX(new RegularExpression());
if(!typeRX->Compile(typeA))
{
result.AppendError("regex format error (maybe this is not really a regex?)");
result.SetStatus(eReturnStatusFailed);
return false;
}
Debugger::RegexSummaryFormats::Delete(typeCS);
Debugger::RegexSummaryFormats::Add(typeRX, entry);
}
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
@ -567,10 +568,14 @@ public:
OptionDefinition
CommandObjectTypeSummaryAdd::CommandOptions::g_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "cascade", 'c', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."},
{ LLDB_OPT_SET_ALL, false, "show-children", 'h', required_argument, NULL, 0, eArgTypeBoolean, "If true, print children."},
{ LLDB_OPT_SET_ALL, false, "show-value", 'v', required_argument, NULL, 0, eArgTypeBoolean, "If true, print value."},
{ LLDB_OPT_SET_ALL, false, "one-liner", 'o', required_argument, NULL, 0, eArgTypeBoolean, "If true, just print a one-line preformatted summary."},
{ LLDB_OPT_SET_ALL, false, "cascade", 'C', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."},
{ LLDB_OPT_SET_ALL, false, "no-value", 'v', no_argument, NULL, 0, eArgTypeBoolean, "Don't show the value, just show the summary, for this type."},
{ LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', no_argument, NULL, 0, eArgTypeBoolean, "Don't use this format for pointers-to-type objects."},
{ LLDB_OPT_SET_ALL, false, "skip-references", 'r', no_argument, NULL, 0, eArgTypeBoolean, "Don't use this format for references-to-type objects."},
{ LLDB_OPT_SET_ALL, false, "regex", 'x', no_argument, NULL, 0, eArgTypeBoolean, "Type names are actually regular expressions."},
{ LLDB_OPT_SET_1 , true, "inline-children", 'c', no_argument, NULL, 0, eArgTypeBoolean, "If true, inline all child values into summary string."},
{ LLDB_OPT_SET_2 , true, "format-string", 'f', required_argument, NULL, 0, eArgTypeFormatString, "Format string used to display text and object contents."},
{ LLDB_OPT_SET_2, false, "expand", 'e', no_argument, NULL, 0, eArgTypeBoolean, "Expand aggregate data types to show children on separate lines."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};
@ -626,8 +631,9 @@ public:
return false;
}
if (Debugger::SummaryFormats::Delete(typeCS))
bool delete_summary = Debugger::SummaryFormats::Delete(typeCS);
bool delete_regex = Debugger::RegexSummaryFormats::Delete(typeCS);
if (delete_summary || delete_regex)
{
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
@ -666,6 +672,7 @@ public:
Execute (Args& command, CommandReturnObject &result)
{
Debugger::SummaryFormats::Clear();
Debugger::RegexSummaryFormats::Clear();
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
@ -677,6 +684,7 @@ public:
//-------------------------------------------------------------------------
bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, const char* type, const SummaryFormat::SharedPointer& entry);
bool CommandObjectTypeRXSummaryList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SummaryFormat::SharedPointer& entry);
class CommandObjectTypeSummaryList;
@ -688,6 +696,14 @@ struct CommandObjectTypeSummaryList_LoopCallbackParam {
RegularExpression* X = NULL) : self(S), result(R), regex(X) {}
};
struct CommandObjectTypeRXSummaryList_LoopCallbackParam {
CommandObjectTypeSummaryList* self;
CommandReturnObject* result;
RegularExpression* regex;
CommandObjectTypeRXSummaryList_LoopCallbackParam(CommandObjectTypeSummaryList* S, CommandReturnObject* R,
RegularExpression* X = NULL) : self(S), result(R), regex(X) {}
};
class CommandObjectTypeSummaryList : public CommandObject
{
public:
@ -718,6 +734,7 @@ public:
const size_t argc = command.GetArgumentCount();
CommandObjectTypeSummaryList_LoopCallbackParam *param;
CommandObjectTypeRXSummaryList_LoopCallbackParam *rxparam;
if (argc == 1) {
RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0));
@ -728,6 +745,24 @@ public:
param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result);
Debugger::SummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param);
delete param;
if(Debugger::RegexSummaryFormats::GetCount() == 0)
{
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
result.GetOutputStream().Printf("Regex-based summaries (slower):\n");
if (argc == 1) {
RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0));
regex->Compile(command.GetArgumentAtIndex(0));
rxparam = new CommandObjectTypeRXSummaryList_LoopCallbackParam(this,&result,regex);
}
else
rxparam = new CommandObjectTypeRXSummaryList_LoopCallbackParam(this,&result);
Debugger::RegexSummaryFormats::LoopThrough(CommandObjectTypeRXSummaryList_LoopCallback, rxparam);
delete rxparam;
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
@ -742,18 +777,21 @@ private:
{
if (regex == NULL || regex->Execute(type))
{
result->GetOutputStream().Printf ("%s: `%s`%s%s%s%s\n", type,
result->GetOutputStream().Printf ("%s: `%s`%s%s%s%s%s%s\n", type,
entry->m_format.c_str(),
entry->m_cascades ? "" : " (not cascading)",
entry->m_dont_show_children ? "" : " (show children)",
entry->m_dont_show_value ? "" : " (show value)",
entry->m_show_members_oneliner ? " (one-line printout)" : "");
entry->m_dont_show_value ? " (hide value)" : "",
entry->m_show_members_oneliner ? " (one-line printout)" : "",
entry->m_skip_pointers ? " (skip pointers)" : "",
entry->m_skip_references ? " (skip references)" : "");
}
return true;
}
friend bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, const char* type, const SummaryFormat::SharedPointer& entry);
friend bool CommandObjectTypeRXSummaryList_LoopCallback(void* pt2self, lldb::RegularExpressionSP regex, const SummaryFormat::SharedPointer& entry);
};
bool
@ -766,6 +804,15 @@ CommandObjectTypeSummaryList_LoopCallback (
return param->self->LoopCallback(type, entry, param->regex, param->result);
}
bool
CommandObjectTypeRXSummaryList_LoopCallback (
void* pt2self,
lldb::RegularExpressionSP regex,
const SummaryFormat::SharedPointer& entry)
{
CommandObjectTypeRXSummaryList_LoopCallbackParam* param = (CommandObjectTypeRXSummaryList_LoopCallbackParam*)pt2self;
return param->self->LoopCallback(regex->GetText(), entry, param->regex, param->result);
}
@ -796,7 +843,7 @@ class CommandObjectTypeSummary : public CommandObjectMultiword
public:
CommandObjectTypeSummary (CommandInterpreter &interpreter) :
CommandObjectMultiword (interpreter,
"type format",
"type summary",
"A set of commands for editing variable summary display options",
"type summary [<sub-command-options>] ")
{

View File

@ -791,15 +791,201 @@ Debugger::FormatPrompt
case '*':
{
if (!vobj) break;
var_name_begin++;
lldb::clang_type_t pointer_clang_type = vobj->GetClangType();
clang_type_t elem_or_pointee_clang_type;
const Flags type_flags (ClangASTContext::GetTypeInfo (pointer_clang_type,
vobj->GetClangAST(),
&elem_or_pointee_clang_type));
if (type_flags.Test (ClangASTContext::eTypeIsPointer))
bool is_pointer = type_flags.Test (ClangASTContext::eTypeIsPointer),
is_array = type_flags.Test (ClangASTContext::eTypeIsArray);
if ( is_array ||
( is_pointer && ::strchr(var_name_begin,'[') && ::strchr(var_name_begin,'[') < var_name_end )
)
{
if (ClangASTContext::IsCharType (elem_or_pointee_clang_type))
const char* var_name_final;
char* close_bracket_position = NULL;
const char* percent_position = NULL;
const char* targetvalue;
lldb::Format custom_format = eFormatInvalid;
int index_lower = -1;
int index_higher = -1;
ValueObject::ValueObjectRepresentationStyle val_obj_display = ValueObject::eDisplaySummary;
{
percent_position = ::strchr(var_name_begin,'%');
if(!percent_position || percent_position > var_name_end)
var_name_final = var_name_end;
else
{
var_name_final = percent_position;
char* format_name = new char[var_name_end-var_name_final]; format_name[var_name_end-var_name_final-1] = '\0';
memcpy(format_name, var_name_final+1, var_name_end-var_name_final-1);
if ( !FormatManager::GetFormatFromCString(format_name,
true,
custom_format) )
{
// if this is an @ sign, print ObjC description
if(*format_name == '@')
val_obj_display = ValueObject::eDisplayLanguageSpecific;
// if this is a V, print the value using the default format
if(*format_name == 'V')
val_obj_display = ValueObject::eDisplayValue;
}
// a good custom format tells us to print the value using it
else
val_obj_display = ValueObject::eDisplayValue;
}
}
{
const char* open_bracket_position = ::strchr(var_name_begin,'[');
if(open_bracket_position && open_bracket_position < var_name_final)
{
// TODO: pick a way to say "all entries". this will make more sense once
// regex typenames are in place. now, you need to be size-aware anyways
char* separator_position = ::strchr(open_bracket_position,'-'); // might be NULL if this is a simple var[N] bitfield
close_bracket_position = ::strchr(open_bracket_position,']');
// as usual, we assume that [] will come before %
//printf("trying to expand a []\n");
var_name_final = open_bracket_position;
if(close_bracket_position - open_bracket_position == 1)
{
if(is_array)
{
index_lower = 0;
index_higher = vobj->GetNumChildren() - 1;
}
else
break; // cannot auto-determine size for pointers
}
else if (separator_position == NULL || separator_position > var_name_end)
{
char *end = NULL;
index_lower = ::strtoul (open_bracket_position+1, &end, 0);
index_higher = index_lower;
//printf("got to read low=%d high same\n",bitfield_lower);
}
else if(close_bracket_position && close_bracket_position < var_name_end)
{
char *end = NULL;
index_lower = ::strtoul (open_bracket_position+1, &end, 0);
index_higher = ::strtoul (separator_position+1, &end, 0);
//printf("got to read low=%d high=%d\n",bitfield_lower,bitfield_higher);
}
else
break;
if (index_lower > index_higher)
{
int temp = index_lower;
index_lower = index_higher;
index_higher = temp;
}
//*((char*)open_bracket_position) = '\0';
//printf("variable name is %s\n",var_name_begin);
//*((char*)open_bracket_position) = '[';
}
}
// if you just type a range, lldb will do the "right thing" in picking
// a reasonable display for the array entries. you can override this by
// giving other input (e.g. ${*var[1-3].member1%uint8_t[]}) and they
// will be honored
char* special_directions = NULL;
if (close_bracket_position && (var_name_end-close_bracket_position > 1))
{
int base_len = var_name_end-close_bracket_position;
special_directions = new char[8+base_len];
special_directions[0] = '$';
special_directions[1] = '{';
special_directions[2] = 'v';
special_directions[3] = 'a';
special_directions[4] = 'r';
memcpy(special_directions+5, close_bracket_position+1, base_len);
special_directions[base_len+7] = '\0';
printf("%s\n",special_directions);
}
// let us display items index_lower thru index_higher of this array
s.PutChar('[');
var_success = true;
const char* expr_path = NULL;
const char* ptr_deref_format = "%s[%d]";
char* ptr_deref_buffer = new char[1024];
StreamString expr_path_string;
if(is_pointer)
{
vobj->GetExpressionPath(expr_path_string, true, ValueObject::eHonorPointers);
expr_path = expr_path_string.GetData();
}
for(;index_lower<=index_higher;index_lower++)
{
ValueObject* item;
if(is_array)
item = vobj->GetChildAtIndex(index_lower, true).get();
else
{
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("name to deref in phase 0: %s\n",expr_path);
#endif //VERBOSE_FORMATPROMPT_OUTPUT
::sprintf(ptr_deref_buffer, ptr_deref_format, expr_path, index_lower);
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("name to deref in phase 1: %s\n",ptr_deref_buffer);
#endif //VERBOSE_FORMATPROMPT_OUTPUT
lldb::VariableSP var_sp;
Error error;
item = exe_ctx->frame->GetValueForVariableExpressionPath (ptr_deref_buffer,
eNoDynamicValues,
0,
var_sp,
error).get();
if (error.Fail())
{
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("ERROR: %s\n",error.AsCString("unknown"));
#endif //VERBOSE_FORMATPROMPT_OUTPUT
break;
}
}
if (!special_directions)
{
targetvalue = item->GetPrintableRepresentation(val_obj_display, custom_format);
if(targetvalue)
s.PutCString(targetvalue);
var_success &= (targetvalue != NULL);
if(custom_format != eFormatInvalid)
item->SetFormat(eFormatDefault);
}
else
{
var_success &= FormatPrompt(special_directions, sc, exe_ctx, addr, s, NULL, item);
}
if(index_lower < index_higher)
s.PutChar(',');
}
s.PutChar(']');
break;
}
else if (is_pointer)
{
var_name_begin++;
uint32_t offset = 0;
DataExtractor read_for_null = vobj->GetDataExtractor();
if (read_for_null.GetPointer(&offset) == 0)
break;
if (ClangASTContext::IsAggregateType (elem_or_pointee_clang_type) )
{
Error error;
realvobj = vobj;
vobj = vobj->Dereference(error).get();
if(!vobj || error.Fail())
break;
}
else if (ClangASTContext::IsCharType (elem_or_pointee_clang_type))
{
StreamString sstr;
ExecutionContextScope *exe_scope = vobj->GetExecutionContextScope();
@ -849,7 +1035,7 @@ Debugger::FormatPrompt
break;
}
}
else /*if (ClangASTContext::IsAggregateType (elem_or_pointee_clang_type)) or this is some other pointer type*/
else /*some other pointer type*/
{
Error error;
realvobj = vobj;
@ -864,7 +1050,7 @@ Debugger::FormatPrompt
case 'v':
{
const char* targetvalue;
bool use_summary = false;
ValueObject::ValueObjectRepresentationStyle val_obj_display = ValueObject::eDisplaySummary;
ValueObject* target;
lldb::Format custom_format = eFormatInvalid;
int bitfield_lower = -1;
@ -872,12 +1058,16 @@ Debugger::FormatPrompt
if (!vobj) break;
// simplest case ${var}, just print vobj's value
if (::strncmp (var_name_begin, "var}", strlen("var}")) == 0)
{
target = vobj;
val_obj_display = ValueObject::eDisplayValue;
}
else if (::strncmp(var_name_begin,"var%",strlen("var%")) == 0)
{
// this is a variable with some custom format applied to it
const char* var_name_final;
target = vobj;
val_obj_display = ValueObject::eDisplayValue;
{
const char* percent_position = ::strchr(var_name_begin,'%'); // TODO: make this a constant
//if(!percent_position || percent_position > var_name_end)
@ -887,9 +1077,14 @@ Debugger::FormatPrompt
var_name_final = percent_position;
char* format_name = new char[var_name_end-var_name_final]; format_name[var_name_end-var_name_final-1] = '\0';
memcpy(format_name, var_name_final+1, var_name_end-var_name_final-1);
FormatManager::GetFormatFromCString(format_name,
if ( !FormatManager::GetFormatFromCString(format_name,
true,
custom_format); // if this fails, custom_format is reset to invalid
custom_format) )
{
// if this is an @ sign, print ObjC description
if(*format_name == '@')
val_obj_display = ValueObject::eDisplayLanguageSpecific;
}
delete format_name;
//}
}
@ -899,7 +1094,7 @@ Debugger::FormatPrompt
// this is a bitfield variable
const char *var_name_final;
target = vobj;
val_obj_display = ValueObject::eDisplayValue;
{
const char* percent_position = ::strchr(var_name_begin,'%');
if(!percent_position || percent_position > var_name_end)
@ -909,10 +1104,15 @@ Debugger::FormatPrompt
var_name_final = percent_position;
char* format_name = new char[var_name_end-var_name_final]; format_name[var_name_end-var_name_final-1] = '\0';
memcpy(format_name, var_name_final+1, var_name_end-var_name_final-1);
FormatManager::GetFormatFromCString(format_name,
true,
custom_format); // if this fails, custom_format is reset to invalid
delete format_name;
if ( !FormatManager::GetFormatFromCString(format_name,
true,
custom_format) )
{
delete format_name;
break;
}
else
delete format_name;
}
}
@ -943,7 +1143,11 @@ Debugger::FormatPrompt
else
break;
if(bitfield_lower > bitfield_higher)
break;
{
int temp = bitfield_lower;
bitfield_lower = bitfield_higher;
bitfield_higher = temp;
}
}
}
}
@ -966,10 +1170,20 @@ Debugger::FormatPrompt
var_name_final = percent_position;
char* format_name = new char[var_name_end-var_name_final]; format_name[var_name_end-var_name_final-1] = '\0';
memcpy(format_name, var_name_final+1, var_name_end-var_name_final-1);
FormatManager::GetFormatFromCString(format_name,
true,
custom_format); // if this fails, custom_format is reset to invalid
delete format_name;
if ( !FormatManager::GetFormatFromCString(format_name,
true,
custom_format) )
{
// if this is an @ sign, print ObjC description
if(*format_name == '@')
val_obj_display = ValueObject::eDisplayLanguageSpecific;
// if this is a V, print the value using the default format
if(*format_name == 'V')
val_obj_display = ValueObject::eDisplayValue;
}
// a good custom format tells us to print the value using it
else
val_obj_display = ValueObject::eDisplayValue;
}
}
@ -999,7 +1213,11 @@ Debugger::FormatPrompt
else
break;
if(bitfield_lower > bitfield_higher)
break;
{
int temp = bitfield_lower;
bitfield_lower = bitfield_higher;
bitfield_higher = temp;
}
//*((char*)open_bracket_position) = '\0';
//printf("variable name is %s\n",var_name_begin);
//*((char*)open_bracket_position) = '[';
@ -1010,9 +1228,13 @@ Debugger::FormatPrompt
lldb::VariableSP var_sp;
StreamString sstring;
vobj->GetExpressionPath(sstring, true, ValueObject::eHonorPointers);
//printf("name to expand in phase 0: %s\n",sstring.GetData());
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("name to expand in phase 0: %s\n",sstring.GetData());
#endif //VERBOSE_FORMATPROMPT_OUTPUT
sstring.PutRawBytes(var_name_begin+3, var_name_final-var_name_begin-3);
//printf("name to expand in phase 1: %s\n",sstring.GetData());
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("name to expand in phase 1: %s\n",sstring.GetData());
#endif //VERBOSE_FORMATPROMPT_OUTPUT
std::string name = std::string(sstring.GetData());
target = exe_ctx->frame->GetValueForVariableExpressionPath (name.c_str(),
eNoDynamicValues,
@ -1021,17 +1243,14 @@ Debugger::FormatPrompt
error).get();
if (error.Fail())
{
//printf("ERROR: %s\n",error.AsCString("unknown"));
#ifdef VERBOSE_FORMATPROMPT_OUTPUT
printf("ERROR: %s\n",error.AsCString("unknown"));
#endif //VERBOSE_FORMATPROMPT_OUTPUT
break;
}
}
else
break;
if(*(var_name_end+1)=='s')
{
use_summary = true;
var_name_end++;
}
if (bitfield_lower >= 0)
{
//printf("trying to print a []\n");
@ -1046,16 +1265,8 @@ Debugger::FormatPrompt
}
else
{
//printf("here I come 1\n");
// format this as usual
if(custom_format != eFormatInvalid)
target->SetFormat(custom_format);
//printf("here I come 2\n");
if(!use_summary)
targetvalue = target->GetValueAsCString();
else
targetvalue = target->GetSummaryAsCString();
//printf("here I come 3\n");
targetvalue = target->GetPrintableRepresentation(val_obj_display, custom_format);
if(targetvalue)
s.PutCString(targetvalue);
var_success = targetvalue;
@ -1634,7 +1845,7 @@ Debugger::ValueFormats::Clear()
}
void
Debugger::ValueFormats::LoopThrough(FormatManager::ValueCallback callback, void* callback_baton)
Debugger::ValueFormats::LoopThrough(ValueFormat::ValueCallback callback, void* callback_baton)
{
GetFormatManager().Value().LoopThrough(callback, callback_baton);
}
@ -1645,6 +1856,11 @@ Debugger::ValueFormats::GetCurrentRevision()
return GetFormatManager().GetCurrentRevision();
}
uint32_t
Debugger::ValueFormats::GetCount()
{
return GetFormatManager().Value().GetCount();
}
bool
Debugger::SummaryFormats::Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry)
@ -1671,7 +1887,7 @@ Debugger::SummaryFormats::Clear()
}
void
Debugger::SummaryFormats::LoopThrough(FormatManager::SummaryCallback callback, void* callback_baton)
Debugger::SummaryFormats::LoopThrough(SummaryFormat::SummaryCallback callback, void* callback_baton)
{
GetFormatManager().Summary().LoopThrough(callback, callback_baton);
}
@ -1682,6 +1898,54 @@ Debugger::SummaryFormats::GetCurrentRevision()
return GetFormatManager().GetCurrentRevision();
}
uint32_t
Debugger::SummaryFormats::GetCount()
{
return GetFormatManager().Summary().GetCount();
}
bool
Debugger::RegexSummaryFormats::Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry)
{
return GetFormatManager().RegexSummary().Get(vobj,entry);
}
void
Debugger::RegexSummaryFormats::Add(const lldb::RegularExpressionSP &type, const SummaryFormat::SharedPointer &entry)
{
GetFormatManager().RegexSummary().Add(type,entry);
}
bool
Debugger::RegexSummaryFormats::Delete(const ConstString &type)
{
return GetFormatManager().RegexSummary().Delete(type.AsCString());
}
void
Debugger::RegexSummaryFormats::Clear()
{
GetFormatManager().RegexSummary().Clear();
}
void
Debugger::RegexSummaryFormats::LoopThrough(SummaryFormat::RegexSummaryCallback callback, void* callback_baton)
{
GetFormatManager().RegexSummary().LoopThrough(callback, callback_baton);
}
uint32_t
Debugger::RegexSummaryFormats::GetCurrentRevision()
{
return GetFormatManager().GetCurrentRevision();
}
uint32_t
Debugger::RegexSummaryFormats::GetCount()
{
return GetFormatManager().RegexSummary().GetCount();
}
#pragma mark Debugger::SettingsController
//--------------------------------------------------

View File

@ -151,3 +151,40 @@ FormatManager::GetFormatAsCString (Format format)
return g_format_infos[format].format_name;
return NULL;
}
template<>
bool
FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Get(const char* key,
SummaryFormat::SharedPointer& value)
{
Mutex::Locker(m_map_mutex);
MapIterator pos, end = m_map.end();
for (pos = m_map.begin(); pos != end; pos++)
{
lldb::RegularExpressionSP regex = pos->first;
if (regex->Execute(key))
{
value = pos->second;
return true;
}
}
return false;
}
template<>
bool
FormatNavigator<std::map<lldb::RegularExpressionSP, SummaryFormat::SharedPointer>, SummaryFormat::RegexSummaryCallback>::Delete(const char* type)
{
Mutex::Locker(m_map_mutex);
MapIterator pos, end = m_map.end();
for (pos = m_map.begin(); pos != end; pos++)
{
lldb::RegularExpressionSP regex = pos->first;
if ( ::strcmp(type,regex->GetText()) == 0)
{
m_map.erase(pos);
return true;
}
}
return false;
}

View File

@ -176,4 +176,9 @@ RegularExpression::GetErrorAsCString (char *err_str, size_t err_str_max_len) con
return ::regerror (m_comp_err, &m_preg, err_str, err_str_max_len);
}
bool
RegularExpression::operator < (const RegularExpression& rhs) const
{
return (m_re < rhs.m_re);
}

View File

@ -73,6 +73,7 @@ ValueObject::ValueObject (ValueObject &parent) :
m_old_value_valid (false),
m_pointers_point_to_load_addrs (false),
m_is_deref_of_parent (false),
m_is_array_item_for_pointer(false),
m_last_format_mgr_revision(0),
m_last_summary_format(),
m_last_value_format()
@ -108,6 +109,7 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) :
m_old_value_valid (false),
m_pointers_point_to_load_addrs (false),
m_is_deref_of_parent (false),
m_is_array_item_for_pointer(false),
m_last_format_mgr_revision(0),
m_last_summary_format(),
m_last_value_format()
@ -124,10 +126,11 @@ ValueObject::~ValueObject ()
}
bool
ValueObject::UpdateValueIfNeeded ()
ValueObject::UpdateValueIfNeeded (bool update_format)
{
UpdateFormatsIfNeeded();
if (update_format)
UpdateFormatsIfNeeded();
// If this is a constant value, then our success is predicated on whether
// we have an error or not
@ -191,7 +194,8 @@ ValueObject::UpdateFormatsIfNeeded()
if (m_last_value_format.get())
m_last_value_format.reset((ValueFormat*)NULL);
Debugger::ValueFormats::Get(*this, m_last_value_format);
Debugger::SummaryFormats::Get(*this, m_last_summary_format);
if (!Debugger::SummaryFormats::Get(*this, m_last_summary_format))
Debugger::RegexSummaryFormats::Get(*this, m_last_summary_format);
m_last_format_mgr_revision = Debugger::ValueFormats::GetCurrentRevision();
m_value_str.clear();
m_summary_str.clear();
@ -493,12 +497,47 @@ ValueObject::GetSummaryAsCString ()
ExecutionContext exe_ctx;
this->GetExecutionContextScope()->CalculateExecutionContext(exe_ctx);
SymbolContext sc = exe_ctx.frame->GetSymbolContext(eSymbolContextEverything);
if (Debugger::FormatPrompt(m_last_summary_format->m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, NULL, this))
if (m_last_summary_format->m_show_members_oneliner)
{
m_summary_str.swap(s.GetString());
return m_summary_str.c_str();
const uint32_t num_children = GetNumChildren();
if (num_children)
{
s.PutChar('(');
for (uint32_t idx=0; idx<num_children; ++idx)
{
ValueObjectSP child_sp(GetChildAtIndex(idx, true));
if (child_sp.get())
{
if (idx)
s.PutCString(", ");
s.PutCString(child_sp.get()->GetName().AsCString());
s.PutChar('=');
s.PutCString(child_sp.get()->GetValueAsCString());
}
}
s.PutChar(')');
m_summary_str.swap(s.GetString());
return m_summary_str.c_str();
}
else
return "()";
}
else
{
if (Debugger::FormatPrompt(m_last_summary_format->m_format.c_str(), &sc, &exe_ctx, &sc.line_entry.range.GetBaseAddress(), s, NULL, this))
{
m_summary_str.swap(s.GetString());
return m_summary_str.c_str();
}
else
return NULL;
}
return NULL;
}
clang_type_t clang_type = GetClangType();
@ -655,12 +694,13 @@ ValueObject::GetSummaryAsCString ()
const char *
ValueObject::GetObjectDescription ()
{
if (!m_object_desc_str.empty())
return m_object_desc_str.c_str();
if (!UpdateValueIfNeeded ())
return NULL;
if (!m_object_desc_str.empty())
return m_object_desc_str.c_str();
ExecutionContextScope *exe_scope = GetExecutionContextScope();
if (exe_scope == NULL)
return NULL;
@ -782,6 +822,37 @@ ValueObject::GetValueAsCString ()
return m_value_str.c_str();
}
const char *
ValueObject::GetPrintableRepresentation(ValueObjectRepresentationStyle val_obj_display,
lldb::Format custom_format)
{
if(custom_format != lldb::eFormatInvalid)
SetFormat(custom_format);
const char * return_value;
switch(val_obj_display)
{
case eDisplayValue:
return_value = GetValueAsCString();
break;
case eDisplaySummary:
return_value = GetSummaryAsCString();
break;
case eDisplayLanguageSpecific:
return_value = GetObjectDescription();
break;
}
// try to use the value if the user's choice failed
if(!return_value && val_obj_display != eDisplayValue)
return_value = GetValueAsCString();
return return_value;
}
addr_t
ValueObject::GetAddressOf (AddressType &address_type, bool scalar_is_load_address)
{
@ -1049,6 +1120,8 @@ ValueObject::GetSyntheticArrayMemberFromPointer (int32_t index, bool can_create)
{
AddSyntheticChild(index_const_str, synthetic_child);
synthetic_child_sp = synthetic_child->GetSP();
synthetic_child_sp->SetName(index_str);
synthetic_child_sp->m_is_array_item_for_pointer = true;
}
}
}
@ -1165,6 +1238,12 @@ ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExp
if (parent)
parent->GetExpressionPath (s, qualify_cxx_base_classes, epformat);
// if we are a deref_of_parent just because we are synthetic array
// members made up to allow ptr[%d] syntax to work in variable
// printing, then add our name ([%d]) to the expression path
if(m_is_array_item_for_pointer && epformat == eHonorPointers)
s.PutCString(m_name.AsCString());
if (!IsBaseClass())
{
@ -1312,8 +1391,17 @@ ValueObject::DumpValueObject
if (val_cstr && (!entry || entry->DoesPrintValue() || !sum_cstr))
s.Printf(" %s", valobj->GetValueAsCString());
if (sum_cstr)
s.Printf(" %s", sum_cstr);
if(sum_cstr)
{
// for some reason, using %@ (ObjC description) in a summary string, makes
// us believe we need to reset ourselves, thus invalidating the content of
// sum_cstr. Thus, IF we had a valid sum_cstr before, but it is now empty
// let us recalculate it!
if (sum_cstr[0] == '\0')
s.Printf(" %s", valobj->GetSummaryAsCString());
else
s.Printf(" %s", sum_cstr);
}
if (use_objc)
{
@ -1323,7 +1411,7 @@ ValueObject::DumpValueObject
else
s.Printf (" [no Objective-C description available]\n");
return;
}
}
}
if (curr_depth < max_depth)
@ -1360,32 +1448,7 @@ ValueObject::DumpValueObject
print_children = false;
}
if (entry && entry->IsOneliner())
{
const uint32_t num_children = valobj->GetNumChildren();
if (num_children)
{
s.PutChar('(');
for (uint32_t idx=0; idx<num_children; ++idx)
{
ValueObjectSP child_sp(valobj->GetChildAtIndex(idx, true));
if (child_sp.get())
{
if (idx)
s.PutCString(", ");
s.PutCString(child_sp.get()->GetName().AsCString());
s.PutChar('=');
s.PutCString(child_sp.get()->GetValueAsCString());
}
}
s.PutChar(')');
s.EOL();
}
}
else if (print_children && (!entry || entry->DoesPrintChildren() || !sum_cstr))
if (print_children && (!entry || entry->DoesPrintChildren() || !sum_cstr))
{
const uint32_t num_children = valobj->GetNumChildren();
if (num_children)

View File

@ -80,7 +80,7 @@ ValueObjectDynamicValue::CalculateNumChildren()
clang::ASTContext *
ValueObjectDynamicValue::GetClangAST ()
{
const bool success = UpdateValueIfNeeded();
const bool success = UpdateValueIfNeeded(false);
if (success && m_type_sp)
return m_type_sp->GetClangAST();
else

View File

@ -627,6 +627,37 @@ BreakpointIDRangeHelpTextCallback ()
return "A 'breakpoint id list' is a manner of specifying multiple breakpoints. This can be done through several mechanisms. The easiest way is to just enter a space-separated list of breakpoint ids. To specify all the breakpoint locations under a major breakpoint, you can use the major breakpoint number followed by '.*', eg. '5.*' means all the locations under breakpoint 5. You can also indicate a range of breakpoints by using <start-bp-id> - <end-bp-id>. The start-bp-id and end-bp-id for a range can be any valid breakpoint ids. It is not legal, however, to specify a range using specific locations that cross major breakpoint numbers. I.e. 3.2 - 3.7 is legal; 2 - 5 is legal; but 3.2 - 4.4 is not legal.";
}
static const char *
FormatHelpTextCallback ()
{
StreamString sstr;
sstr << "One of the format names (or one-character names) that can be used to show a variable's value:\n";
for (Format f = eFormatDefault; f < kNumFormats; f = Format(f+1))
{
char format_char = FormatManager::GetFormatAsFormatChar(f);
if (format_char)
sstr.Printf("'%c' or ", format_char);
sstr.Printf ("\"%s\" ; ", FormatManager::GetFormatAsCString(f));
}
sstr.Flush();
std::string data = sstr.GetString();
char* help = new char[data.length()+1];
data.copy(help, data.length());
return help;
}
static const char *
FormatStringHelpTextCallback()
{
return "Ask me tomorrow";
}
const char *
CommandObject::GetArgumentTypeAsCString (const lldb::CommandArgumentType arg_type)
{
@ -662,7 +693,8 @@ CommandObject::g_arguments_data[] =
{ eArgTypeExpression, "expr", CommandCompletions::eNoCompletion, NULL, "Help text goes here." },
{ eArgTypeExprFormat, "expression-format", CommandCompletions::eNoCompletion, NULL, "[ [bool|b] | [bin] | [char|c] | [oct|o] | [dec|i|d|u] | [hex|x] | [float|f] | [cstr|s] ]" },
{ eArgTypeFilename, "filename", CommandCompletions::eDiskFileCompletion, NULL, "The name of a file (can include path)." },
{ eArgTypeFormat, "format", CommandCompletions::eNoCompletion, NULL, "Help text goes here." },
{ eArgTypeFormat, "format", CommandCompletions::eNoCompletion, FormatHelpTextCallback, NULL },
{ eArgTypeFormatString, "format-string", CommandCompletions::eNoCompletion, FormatStringHelpTextCallback, NULL },
{ eArgTypeFrameIndex, "frame-index", CommandCompletions::eNoCompletion, NULL, "Index into a thread's list of frames." },
{ eArgTypeFullName, "fullname", CommandCompletions::eNoCompletion, NULL, "Help text goes here." },
{ eArgTypeFunctionName, "function-name", CommandCompletions::eNoCompletion, NULL, "The name of a function." },

View File

@ -117,21 +117,26 @@ GetCompleteQualType (clang::ASTContext *ast, clang::QualType qual_type)
clang::ObjCInterfaceDecl *class_interface_decl = objc_class_type->getInterface();
// We currently can't complete objective C types through the newly added ASTContext
// because it only supports TagDecl objects right now...
bool is_forward_decl = class_interface_decl->isForwardDecl();
if (is_forward_decl && class_interface_decl->hasExternalLexicalStorage())
if(class_interface_decl)
{
if (ast)
bool is_forward_decl = class_interface_decl->isForwardDecl();
if (is_forward_decl && class_interface_decl->hasExternalLexicalStorage())
{
ExternalASTSource *external_ast_source = ast->getExternalSource();
if (external_ast_source)
if (ast)
{
external_ast_source->CompleteType (class_interface_decl);
is_forward_decl = class_interface_decl->isForwardDecl();
ExternalASTSource *external_ast_source = ast->getExternalSource();
if (external_ast_source)
{
external_ast_source->CompleteType (class_interface_decl);
is_forward_decl = class_interface_decl->isForwardDecl();
}
}
return is_forward_decl == false;
}
return is_forward_decl == false;
return true;
}
return true;
else
return false;
}
}
break;

View File

@ -1,5 +0,0 @@
LEVEL = ../../make
CXX_SOURCES := main.cpp
include $(LEVEL)/Makefile.rules

View File

@ -1,120 +0,0 @@
"""
Test lldb data formatter subsystem.
"""
import os, time
import unittest2
import lldb
from lldbtest import *
class DataFormatterTestCase(TestBase):
mydir = os.path.join("functionalities", "data-formatter")
@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'])
self.expect("frame variable",
substrs = ['(Speed) SPILookHex = 5.55' # Speed by default is 5.55.
]);
# 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)
# Execute the cleanup function during test case tear down.
self.addTearDownHook(cleanup)
self.runCmd("type format add -c yes x Speed BitField")
self.runCmd("type format add -c no c RealNumber")
self.runCmd("type format add -c no x Type2")
self.runCmd("type format add -c yes c Type1")
# The type format list should show our custom formats.
self.expect("type format list",
substrs = ['RealNumber',
'Speed',
'BitField',
'Type1',
'Type2'])
self.expect("frame variable",
patterns = ['\(Speed\) SPILookHex = 0x[0-9a-f]+' # Speed should look hex-ish now.
]);
# Now let's delete the 'Speed' custom format.
self.runCmd("type format delete Speed")
# The type format list should not show 'Speed' at this point.
self.expect("type format list", matching=False,
substrs = ['Speed'])
# Delete type format for 'Speed', we should expect an error message.
self.expect("type format delete Speed", error=True,
substrs = ['no custom format for Speed'])
# For some reason the type system is calling this "struct"
self.runCmd("type summary add -o yes \"\" Point")
self.expect("frame variable iAmSomewhere",
substrs = ['x=4',
'y=6'])
self.expect("type summary list",
substrs = ['Point',
'one-line'])
self.runCmd("type summary add \"y=${var.y%x}\" Point")
self.expect("frame variable iAmSomewhere",
substrs = ['y=0x'])
self.runCmd("type summary add \"hello\" Point -h yes")
self.expect("type summary list",
substrs = ['Point',
'show children'])
self.expect("frame variable iAmSomewhere",
substrs = ['hello',
'x = 4',
'}'])
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -1,70 +0,0 @@
//===-- 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>
#include <stdlib.h>
#include <stdint.h>
typedef float RealNumber; // should show as char
typedef RealNumber Temperature; // should show as float
typedef RealNumber Speed; // should show as hex
typedef int Counter; // should show as int
typedef int BitField; // should show as hex
typedef BitField SignalMask; // should show as hex
typedef BitField Modifiers; // should show as hex
typedef Counter Accumulator; // should show as int
typedef int Type1; // should show as char
typedef Type1 Type2; // should show as hex
typedef Type2 Type3; // should show as char
typedef Type3 Type4; // should show as char
typedef int ChildType; // should show as int
typedef int AnotherChildType; // should show as int
struct Point {
int x;
int y;
Point(int X = 3, int Y = 2) : x(X), y(Y) {}
};
int main (int argc, const char * argv[])
{
int iAmInt = 1;
const float& IAmFloat = float(2.45);
RealNumber RNILookChar = 3.14;
Temperature TMILookFloat = 4.97;
Speed SPILookHex = 5.55;
Counter CTILookInt = 6;
BitField BFILookHex = 7;
SignalMask SMILookHex = 8;
Modifiers MFILookHex = 9;
Accumulator* ACILookInt = new Accumulator(10);
const Type1& T1ILookChar = 11;
Type2 T2ILookHex = 12;
Type3 T3ILookChar = 13;
Type4 T4ILookChar = 14;
AnotherChildType AHILookInt = 15;
Speed* SPPtrILookHex = new Speed(16);
Point iAmSomewhere(4,6);
return 0; // Set break point at this line.
}