This commit adds a new top subcommand "summary" to command type named "type". Currently this command

implements three commands:

type summary add <format> <typename1> [<typename2> ...]
type summary delete <typename1> [<typename2> ...]
type summary list [<typename1> [<typename2>] ...]
type summary clear

This allows you to specify the default format that will be used to display
summaries for variables, shown when you use "frame variable" or "expression", or the SBValue classes.

Examples:
type summary add "x = ${var.x}" Point

type summary list

type summary add --one-liner SimpleType

llvm-svn: 134108
This commit is contained in:
Enrico Granata 2011-06-29 22:27:15 +00:00
parent d1ef780bb3
commit 4becb37e34
15 changed files with 1383 additions and 227 deletions

View File

@ -415,7 +415,8 @@ public:
const ExecutionContext *exe_ctx,
const Address *addr,
Stream &s,
const char **end);
const char **end,
ValueObject* vobj = NULL);
void
@ -473,18 +474,50 @@ private:
public:
static bool
GetFormatForType (const ConstString &type, lldb::Format& format, bool& cascade);
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();
};
static void
AddFormatForType (const ConstString &type, lldb::Format format, bool cascade);
class SummaryFormats
{
public:
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 bool
DeleteFormatForType (const ConstString &type);
static void
LoopThroughFormatList (FormatManager::Callback callback,
void* callback_baton);
};
} // namespace lldb_private

View File

@ -40,42 +40,288 @@ namespace std
#include "lldb/Core/Communication.h"
#include "lldb/Core/InputReaderStack.h"
#include "lldb/Core/Listener.h"
#include "lldb/Core/Stream.h"
#include "lldb/Core/StreamFile.h"
#include "lldb/Core/SourceManager.h"
#include "lldb/Core/UserID.h"
#include "lldb/Core/UserSettingsController.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Target/ExecutionContext.h"
#include "lldb/Target/Platform.h"
#include "lldb/Target/TargetList.h"
namespace lldb_private {
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
namespace lldb_private {
class FormatManager
class IFormatChangeListener
{
public:
virtual void
Changed() = 0;
virtual
~IFormatChangeListener() {}
};
struct SummaryFormat
{
std::string m_format;
bool m_dont_show_children;
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) :
m_format(f),
m_dont_show_children(nochildren),
m_dont_show_value(novalue),
m_show_members_oneliner(oneliner),
m_cascades(c)
{
}
bool
DoesPrintChildren() const
{
return !m_dont_show_children;
}
bool
DoesPrintValue() const
{
return !m_dont_show_value;
}
bool
IsOneliner() const
{
return m_show_members_oneliner;
}
typedef lldb::SharedPtr<SummaryFormat>::Type SharedPointer;
};
struct ValueFormat
{
lldb::Format m_format;
bool m_cascades;
ValueFormat (lldb::Format f = lldb::eFormatInvalid, bool c = false) :
m_format (f),
m_cascades (c)
{
}
typedef lldb::SharedPtr<ValueFormat>::Type SharedPointer;
~ValueFormat()
{
}
};
template<typename MapType, typename CallbackType>
class FormatNavigator
{
public:
typedef bool(*Callback)(void*, const char*, lldb::Format, bool);
typedef typename MapType::iterator MapIterator;
typedef typename MapType::key_type MapKeyType;
typedef typename MapType::mapped_type MapValueType;
FormatNavigator(IFormatChangeListener* lst = NULL) :
m_map_mutex(Mutex::eMutexTypeRecursive),
m_map(MapType()),
listener(lst)
{
}
bool
Get(ValueObject& vobj, MapValueType& entry)
{
Mutex::Locker(m_map_mutex);
clang::QualType type = clang::QualType::getFromOpaquePtr(vobj.GetClangType());
bool ret = Get(vobj, type, entry);
if(ret)
entry = MapValueType(entry);
else
entry = MapValueType();
return ret;
}
void
Add(const MapKeyType &type, const MapValueType& entry)
{
Mutex::Locker(m_map_mutex);
m_map[type] = MapValueType(entry);
if(listener)
listener->Changed();
}
bool
Delete(const MapKeyType& type)
{
Mutex::Locker(m_map_mutex);
MapIterator iter = m_map.find(type);
if (iter == m_map.end())
return false;
m_map.erase(type);
if(listener)
listener->Changed();
return true;
}
void
Clear()
{
Mutex::Locker(m_map_mutex);
m_map.clear();
if(listener)
listener->Changed();
}
void
LoopThrough(CallbackType callback, void* param)
{
if (callback)
{
Mutex::Locker(m_map_mutex);
MapIterator pos, end = m_map.end();
for (pos = m_map.begin(); pos != end; pos++)
{
MapKeyType type = pos->first;
if(!callback(param, type, MapValueType(pos->second)))
break;
}
}
}
~FormatNavigator()
{
}
private:
Mutex m_map_mutex;
MapType m_map;
IFormatChangeListener* listener;
DISALLOW_COPY_AND_ASSIGN(FormatNavigator);
bool
Get(const MapKeyType &type, MapValueType& entry)
{
Mutex::Locker(m_map_mutex);
MapIterator iter = m_map.find(type);
if (iter == m_map.end())
return false;
entry = iter->second;
return true;
}
bool Get(ValueObject& vobj,
const clang::QualType& q_type,
MapValueType& entry)
{
if (q_type.isNull())
return false;
clang::QualType type = q_type.getUnqualifiedType();
type.removeLocalConst(); type.removeLocalVolatile(); type.removeLocalRestrict();
ConstString name(type.getAsString().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);
// for C++ classes, navigate up the hierarchy
if (typePtr->isRecordType())
{
clang::CXXRecordDecl* record = typePtr->getAsCXXRecordDecl();
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;
}
if (record->hasDefinition())
{
clang::CXXRecordDecl::base_class_iterator pos,end;
if( record->getNumBases() > 0)
{
end = record->bases_end();
for (pos = record->bases_begin(); pos != end; pos++)
{
if((Get(vobj, pos->getType(), entry)) && entry->m_cascades)
return true; // if it does not cascade, just move on to other base classes which might
}
}
if (record->getNumVBases() > 0)
{
end = record->vbases_end();
for (pos = record->vbases_begin(); pos != end; pos++)
{
if((Get(vobj, pos->getType(), entry)) && entry->m_cascades)
return true;
}
}
}
}
}
// try to strip typedef chains
const clang::TypedefType* type_tdef = type->getAs<clang::TypedefType>();
if (type_tdef)
if ((Get(vobj, type_tdef->getDecl()->getUnderlyingType(), entry)) && entry->m_cascades)
return true;
return false;
}
};
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 FormatNavigator<ValueMap, ValueCallback> ValueNavigator;
typedef FormatNavigator<SummaryMap, SummaryCallback> SummaryNavigator;
ValueNavigator m_value_nav;
SummaryNavigator m_summary_nav;
uint32_t m_last_revision;
public:
FormatManager() :
m_format_map(FormatMap()),
m_format_map_mutex(Mutex::eMutexTypeRecursive)
m_value_nav(this),
m_summary_nav(this),
m_last_revision(0)
{
}
bool
GetFormatForType (const ConstString &type, lldb::Format& format, bool& cascade);
void
AddFormatForType (const ConstString &type, lldb::Format format, bool cascade);
bool
DeleteFormatForType (const ConstString &type);
void
LoopThroughFormatList (Callback cback, void* param);
ValueNavigator& Value() { return m_value_nav; }
SummaryNavigator& Summary() { return m_summary_nav; }
static bool
GetFormatFromCString (const char *format_cstr,
bool partial_match_ok,
@ -86,24 +332,23 @@ public:
static const char *
GetFormatAsCString (lldb::Format format);
private:
struct Entry
{
lldb::Format format;
bool cascades;
Entry (lldb::Format f = lldb::eFormatInvalid, bool c = false) :
format (f),
cascades (c)
{
}
};
typedef std::map<const char*, Entry> FormatMap;
typedef FormatMap::iterator FormatIterator;
void
Changed()
{
__sync_add_and_fetch(&m_last_revision, +1);
}
uint32_t
GetCurrentRevision() const
{
return m_last_revision;
}
~FormatManager()
{
}
FormatMap m_format_map;
Mutex m_format_map_mutex;
};
} // namespace lldb_private

View File

@ -64,6 +64,12 @@ namespace lldb_private {
class ValueObject : public UserID
{
public:
enum GetExpressionPathFormat
{
eDereferencePointers = 1,
eHonorPointers,
};
class EvaluationPoint
{
@ -262,8 +268,8 @@ public:
GetBaseClassPath (Stream &s);
virtual void
GetExpressionPath (Stream &s, bool qualify_cxx_base_classes);
GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat = eDereferencePointers);
virtual bool
IsInScope ()
{
@ -347,6 +353,9 @@ public:
bool
UpdateValueIfNeeded ();
void
UpdateFormatsIfNeeded();
DataExtractor &
GetDataExtractor ();
@ -508,7 +517,9 @@ protected:
ValueObject *m_deref_valobj;
lldb::Format m_format;
lldb::Format m_last_format;
uint32_t m_last_format_mgr_revision;
lldb::SummaryFormatSP m_last_summary_format;
lldb::ValueFormatSP m_last_value_format;
bool m_value_is_valid:1,
m_value_did_change:1,
m_children_count_valid:1,

View File

@ -182,6 +182,13 @@ public:
static lldb::Format
GetFormat (lldb::clang_type_t opaque_clang_qual_type);
uint32_t
GetTypeByteSize();
static uint32_t
GetTypeByteSize(clang::ASTContext *ast_context,
lldb::clang_type_t opaque_clang_qual_type);
bool
GetValueAsScalar (const DataExtractor &data,

View File

@ -60,6 +60,7 @@ namespace lldb {
typedef SharedPtr<lldb_private::StopInfo>::Type StopInfoSP;
typedef SharedPtr<lldb_private::StoppointLocation>::Type StoppointLocationSP;
typedef SharedPtr<lldb_private::Stream>::Type StreamSP;
typedef SharedPtr<lldb_private::SummaryFormat>::Type SummaryFormatSP;
typedef SharedPtr<lldb_private::SymbolFile>::Type SymbolFileSP;
typedef SharedPtr<lldb_private::SymbolContextSpecifier>::Type SymbolContextSpecifierSP;
typedef SharedPtr<lldb_private::Target>::Type TargetSP;
@ -72,6 +73,7 @@ namespace lldb {
typedef SharedPtr<lldb_private::UnwindPlan>::Type UnwindPlanSP;
typedef SharedPtr<lldb_private::ValueObject>::Type ValueObjectSP;
typedef SharedPtr<lldb_private::Value>::Type ValueSP;
typedef SharedPtr<lldb_private::ValueFormat>::Type ValueFormatSP;
typedef SharedPtr<lldb_private::ValueList>::Type ValueListSP;
typedef SharedPtr<lldb_private::Variable>::Type VariableSP;
typedef SharedPtr<lldb_private::VariableList>::Type VariableListSP;

View File

@ -76,6 +76,7 @@ class ExecutionContextScope;
class FileSpec;
class FileSpecList;
class Flags;
class FormatManager;
class FuncUnwinders;
class Function;
class FunctionInfo;
@ -130,6 +131,7 @@ class Stream;
class StreamFile;
class StreamString;
class StringList;
class SummaryFormat;
class Symbol;
class SymbolContext;
class SymbolContextList;
@ -164,6 +166,7 @@ class UnwindTable;
class UserSettingsController;
class VMRange;
class Value;
class ValueFormat;
class ValueList;
class ValueObject;
class ValueObjectList;

View File

@ -924,7 +924,7 @@
26BC7E9610F1B85900F91463 /* Timer.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Timer.cpp; path = source/Core/Timer.cpp; sourceTree = "<group>"; };
26BC7E9810F1B85900F91463 /* UserID.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = UserID.cpp; path = source/Core/UserID.cpp; sourceTree = "<group>"; };
26BC7E9910F1B85900F91463 /* Value.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Value.cpp; path = source/Core/Value.cpp; sourceTree = "<group>"; };
26BC7E9A10F1B85900F91463 /* ValueObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObject.cpp; path = source/Core/ValueObject.cpp; sourceTree = "<group>"; };
26BC7E9A10F1B85900F91463 /* ValueObject.cpp */ = {isa = PBXFileReference; fileEncoding = 4; name = ValueObject.cpp; path = source/Core/ValueObject.cpp; sourceTree = "<group>"; };
26BC7E9B10F1B85900F91463 /* ValueObjectChild.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectChild.cpp; path = source/Core/ValueObjectChild.cpp; sourceTree = "<group>"; };
26BC7E9C10F1B85900F91463 /* ValueObjectList.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectList.cpp; path = source/Core/ValueObjectList.cpp; sourceTree = "<group>"; };
26BC7E9D10F1B85900F91463 /* ValueObjectVariable.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ValueObjectVariable.cpp; path = source/Core/ValueObjectVariable.cpp; sourceTree = "<group>"; };

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
version = "1.3">
version = "1.8">
<BuildAction
parallelizeBuildables = "NO"
buildImplicitDependencies = "YES">
@ -77,7 +77,7 @@
launchStyle = "0"
useCustomWorkingDirectory = "NO"
customWorkingDirectory = "/Volumes/work/gclayton/Documents/devb/attach"
buildConfiguration = "Release"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "YES"
enablesOpenGLESFrameCapture = "YES">
<BuildableProductRunnable>

View File

@ -25,10 +25,10 @@ using namespace lldb;
using namespace lldb_private;
//-------------------------------------------------------------------------
// CommandObjectTypeAdd
// CommandObjectTypeFormatAdd
//-------------------------------------------------------------------------
class CommandObjectTypeAdd : public CommandObject
class CommandObjectTypeFormatAdd : public CommandObject
{
private:
@ -97,7 +97,7 @@ private:
}
public:
CommandObjectTypeAdd (CommandInterpreter &interpreter) :
CommandObjectTypeFormatAdd (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type format add",
"Add a new formatting style for a type.",
@ -121,7 +121,7 @@ public:
m_arguments.push_back (type_arg);
}
~CommandObjectTypeAdd ()
~CommandObjectTypeFormatAdd ()
{
}
@ -138,9 +138,22 @@ public:
}
const char* format_cstr = command.GetArgumentAtIndex(0);
lldb::Format format;
Error error = Args::StringToFormat(format_cstr, format, NULL);
if (!format_cstr || !format_cstr[0])
{
result.AppendError("empty format strings not allowed");
result.SetStatus(eReturnStatusFailed);
return false;
}
lldb::Format format;
Error error;
error = Args::StringToFormat(format_cstr, format, NULL);
ValueFormat::SharedPointer entry;
entry.reset(new ValueFormat(format,m_options.m_cascade));
if (error.Fail())
{
result.AppendError(error.AsCString());
@ -153,7 +166,14 @@ public:
for(int i = 1; i < argc; i++) {
const char* typeA = command.GetArgumentAtIndex(i);
ConstString typeCS(typeA);
Debugger::AddFormatForType(typeCS, format, m_options.m_cascade);
if (typeCS)
Debugger::ValueFormats::Add(typeCS, entry);
else
{
result.AppendError("empty typenames not allowed");
result.SetStatus(eReturnStatusFailed);
return false;
}
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
@ -163,7 +183,7 @@ public:
};
OptionDefinition
CommandObjectTypeAdd::CommandOptions::g_option_table[] =
CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "cascade", 'c', required_argument, NULL, 0, eArgTypeBoolean, "If true, cascade to derived typedefs."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
@ -171,13 +191,13 @@ CommandObjectTypeAdd::CommandOptions::g_option_table[] =
//-------------------------------------------------------------------------
// CommandObjectTypeDelete
// CommandObjectTypeFormatDelete
//-------------------------------------------------------------------------
class CommandObjectTypeDelete : public CommandObject
class CommandObjectTypeFormatDelete : public CommandObject
{
public:
CommandObjectTypeDelete (CommandInterpreter &interpreter) :
CommandObjectTypeFormatDelete (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type format delete",
"Delete an existing formatting style for a type.",
@ -185,17 +205,17 @@ public:
{
CommandArgumentEntry type_arg;
CommandArgumentData type_style_arg;
type_style_arg.arg_type = eArgTypeName;
type_style_arg.arg_repetition = eArgRepeatPlain;
type_arg.push_back (type_style_arg);
m_arguments.push_back (type_arg);
}
~CommandObjectTypeDelete ()
~CommandObjectTypeFormatDelete ()
{
}
@ -214,7 +234,15 @@ public:
const char* typeA = command.GetArgumentAtIndex(0);
ConstString typeCS(typeA);
if (Debugger::DeleteFormatForType(typeCS))
if(!typeCS)
{
result.AppendError("empty typenames not allowed");
result.SetStatus(eReturnStatusFailed);
return false;
}
if (Debugger::ValueFormats::Delete(typeCS))
{
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
@ -225,31 +253,60 @@ public:
result.SetStatus(eReturnStatusFailed);
return false;
}
}
};
//-------------------------------------------------------------------------
// CommandObjectTypeList
// CommandObjectTypeFormatClear
//-------------------------------------------------------------------------
bool CommandObjectTypeList_LoopCallback(void* pt2self, const char* type, lldb::Format format, bool cascade);
class CommandObjectTypeFormatClear : public CommandObject
{
public:
CommandObjectTypeFormatClear (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type format clear",
"Delete all existing format styles.",
NULL)
{
}
~CommandObjectTypeFormatClear ()
{
}
bool
Execute (Args& command, CommandReturnObject &result)
{
Debugger::ValueFormats::Clear();
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
};
class CommandObjectTypeList;
//-------------------------------------------------------------------------
// CommandObjectTypeFormatList
//-------------------------------------------------------------------------
struct CommandObjectTypeList_LoopCallbackParam {
CommandObjectTypeList* self;
bool CommandObjectTypeFormatList_LoopCallback(void* pt2self, const char* type, const ValueFormat::SharedPointer& entry);
class CommandObjectTypeFormatList;
struct CommandObjectTypeFormatList_LoopCallbackParam {
CommandObjectTypeFormatList* self;
CommandReturnObject* result;
RegularExpression* regex;
CommandObjectTypeList_LoopCallbackParam(CommandObjectTypeList* S, CommandReturnObject* R,
CommandObjectTypeFormatList_LoopCallbackParam(CommandObjectTypeFormatList* S, CommandReturnObject* R,
RegularExpression* X = NULL) : self(S), result(R), regex(X) {}
};
class CommandObjectTypeList : public CommandObject
class CommandObjectTypeFormatList : public CommandObject
{
public:
CommandObjectTypeList (CommandInterpreter &interpreter) :
CommandObjectTypeFormatList (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type format list",
"Show a list of current formatting styles.",
@ -266,7 +323,7 @@ public:
m_arguments.push_back (type_arg);
}
~CommandObjectTypeList ()
~CommandObjectTypeFormatList ()
{
}
@ -275,16 +332,16 @@ public:
{
const size_t argc = command.GetArgumentCount();
CommandObjectTypeList_LoopCallbackParam *param;
CommandObjectTypeFormatList_LoopCallbackParam *param;
if (argc == 1) {
RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0));
regex->Compile(command.GetArgumentAtIndex(0));
param = new CommandObjectTypeList_LoopCallbackParam(this,&result,regex);
param = new CommandObjectTypeFormatList_LoopCallbackParam(this,&result,regex);
}
else
param = new CommandObjectTypeList_LoopCallbackParam(this,&result);
Debugger::LoopThroughFormatList(CommandObjectTypeList_LoopCallback, param);
param = new CommandObjectTypeFormatList_LoopCallbackParam(this,&result);
Debugger::ValueFormats::LoopThrough(CommandObjectTypeFormatList_LoopCallback, param);
delete param;
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
@ -294,45 +351,438 @@ private:
bool
LoopCallback (const char* type,
lldb::Format format,
bool cascade,
const ValueFormat::SharedPointer& entry,
RegularExpression* regex,
CommandReturnObject *result)
{
if (regex == NULL || regex->Execute(type))
{
result->GetOutputStream().Printf ("%s: %s\n", type, FormatManager::GetFormatAsCString (format));
result->GetOutputStream().Printf ("%s: %s%s\n", type,
FormatManager::GetFormatAsCString (entry->m_format),
entry->m_cascades ? "" : " (not cascading)");
}
return true;
}
friend bool CommandObjectTypeList_LoopCallback(void* pt2self, const char* type, lldb::Format format, bool cascade);
friend bool CommandObjectTypeFormatList_LoopCallback(void* pt2self, const char* type, const ValueFormat::SharedPointer& entry);
};
bool
CommandObjectTypeList_LoopCallback (
CommandObjectTypeFormatList_LoopCallback (
void* pt2self,
const char* type,
lldb::Format format,
bool cascade)
const ValueFormat::SharedPointer& entry)
{
CommandObjectTypeList_LoopCallbackParam* param = (CommandObjectTypeList_LoopCallbackParam*)pt2self;
return param->self->LoopCallback(type, format, cascade, param->regex, param->result);
CommandObjectTypeFormatList_LoopCallbackParam* param = (CommandObjectTypeFormatList_LoopCallbackParam*)pt2self;
return param->self->LoopCallback(type, entry, param->regex, param->result);
}
//-------------------------------------------------------------------------
// CommandObjectTypeSummaryAdd
//-------------------------------------------------------------------------
class CommandObjectTypeSummaryAdd : public CommandObject
{
private:
class CommandOptions : public Options
{
public:
CommandOptions (CommandInterpreter &interpreter) :
Options (interpreter)
{
}
virtual
~CommandOptions (){}
virtual Error
SetOptionValue (uint32_t option_idx, const char *option_arg)
{
Error error;
char short_option = (char) m_getopt_table[option_idx].val;
bool success;
switch (short_option)
{
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);
break;
case 'v':
m_no_value = !Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("Invalid value for novalue: %s.\n", option_arg);
break;
case 'o':
m_one_liner = Args::StringToBoolean(option_arg, true, &success);
if (!success)
error.SetErrorStringWithFormat("Invalid value for oneliner: %s.\n", option_arg);
break;
default:
error.SetErrorStringWithFormat ("Unrecognized option '%c'.\n", short_option);
break;
}
return error;
}
void
OptionParsingStarting ()
{
m_cascade = true;
m_no_children = true;
m_no_value = false;
m_one_liner = false;
}
const OptionDefinition*
GetDefinitions ()
{
return g_option_table;
}
// Options table: Required for subclasses of Options.
static OptionDefinition g_option_table[];
// Instance variables to hold the values for command options.
bool m_cascade;
bool m_no_children;
bool m_no_value;
bool m_one_liner;
};
CommandOptions m_options;
virtual Options *
GetOptions ()
{
return &m_options;
}
public:
CommandObjectTypeSummaryAdd (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type summary add",
"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);
}
~CommandObjectTypeSummaryAdd ()
{
}
bool
Execute (Args& command, CommandReturnObject &result)
{
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)
{
result.AppendErrorWithFormat ("%s takes two 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 )
{
result.AppendError("empty summary strings not allowed");
result.SetStatus(eReturnStatusFailed);
return false;
}
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));
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 = (isValidShortcut ? 0 : 1); i < argc; i++) {
const char* typeA = command.GetArgumentAtIndex(i);
ConstString typeCS(typeA);
if (typeCS)
Debugger::SummaryFormats::Add(typeCS, entry);
else
{
result.AppendError("empty typenames not allowed");
result.SetStatus(eReturnStatusFailed);
return false;
}
}
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
}
};
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."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};
//-------------------------------------------------------------------------
// CommandObjectTypeSummaryDelete
//-------------------------------------------------------------------------
class CommandObjectTypeSummaryDelete : public CommandObject
{
public:
CommandObjectTypeSummaryDelete (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type summary delete",
"Delete an existing summary style for a type.",
NULL)
{
CommandArgumentEntry type_arg;
CommandArgumentData type_style_arg;
type_style_arg.arg_type = eArgTypeName;
type_style_arg.arg_repetition = eArgRepeatPlain;
type_arg.push_back (type_style_arg);
m_arguments.push_back (type_arg);
}
~CommandObjectTypeSummaryDelete ()
{
}
bool
Execute (Args& command, CommandReturnObject &result)
{
const size_t argc = command.GetArgumentCount();
if (argc != 1)
{
result.AppendErrorWithFormat ("%s takes 1 arg.\n", m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
return false;
}
const char* typeA = command.GetArgumentAtIndex(0);
ConstString typeCS(typeA);
if(!typeCS)
{
result.AppendError("empty typenames not allowed");
result.SetStatus(eReturnStatusFailed);
return false;
}
if (Debugger::SummaryFormats::Delete(typeCS))
{
result.SetStatus(eReturnStatusSuccessFinishNoResult);
return result.Succeeded();
}
else
{
result.AppendErrorWithFormat ("no custom summary for %s.\n", typeA);
result.SetStatus(eReturnStatusFailed);
return false;
}
}
};
//-------------------------------------------------------------------------
// CommandObjectTypeSummaryClear
//-------------------------------------------------------------------------
class CommandObjectTypeSummaryClear : public CommandObject
{
public:
CommandObjectTypeSummaryClear (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type summary clear",
"Delete all existing summary styles.",
NULL)
{
}
~CommandObjectTypeSummaryClear ()
{
}
bool
Execute (Args& command, CommandReturnObject &result)
{
Debugger::SummaryFormats::Clear();
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
};
//-------------------------------------------------------------------------
// CommandObjectTypeSummaryList
//-------------------------------------------------------------------------
bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, const char* type, const SummaryFormat::SharedPointer& entry);
class CommandObjectTypeSummaryList;
struct CommandObjectTypeSummaryList_LoopCallbackParam {
CommandObjectTypeSummaryList* self;
CommandReturnObject* result;
RegularExpression* regex;
CommandObjectTypeSummaryList_LoopCallbackParam(CommandObjectTypeSummaryList* S, CommandReturnObject* R,
RegularExpression* X = NULL) : self(S), result(R), regex(X) {}
};
class CommandObjectTypeSummaryList : public CommandObject
{
public:
CommandObjectTypeSummaryList (CommandInterpreter &interpreter) :
CommandObject (interpreter,
"type summary list",
"Show a list of current summary styles.",
NULL)
{
CommandArgumentEntry type_arg;
CommandArgumentData type_style_arg;
type_style_arg.arg_type = eArgTypeName;
type_style_arg.arg_repetition = eArgRepeatOptional;
type_arg.push_back (type_style_arg);
m_arguments.push_back (type_arg);
}
~CommandObjectTypeSummaryList ()
{
}
bool
Execute (Args& command, CommandReturnObject &result)
{
const size_t argc = command.GetArgumentCount();
CommandObjectTypeSummaryList_LoopCallbackParam *param;
if (argc == 1) {
RegularExpression* regex = new RegularExpression(command.GetArgumentAtIndex(0));
regex->Compile(command.GetArgumentAtIndex(0));
param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result,regex);
}
else
param = new CommandObjectTypeSummaryList_LoopCallbackParam(this,&result);
Debugger::SummaryFormats::LoopThrough(CommandObjectTypeSummaryList_LoopCallback, param);
delete param;
result.SetStatus(eReturnStatusSuccessFinishResult);
return result.Succeeded();
}
private:
bool
LoopCallback (const char* type,
const SummaryFormat::SharedPointer& entry,
RegularExpression* regex,
CommandReturnObject *result)
{
if (regex == NULL || regex->Execute(type))
{
result->GetOutputStream().Printf ("%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)" : "");
}
return true;
}
friend bool CommandObjectTypeSummaryList_LoopCallback(void* pt2self, const char* type, const SummaryFormat::SharedPointer& entry);
};
bool
CommandObjectTypeSummaryList_LoopCallback (
void* pt2self,
const char* type,
const SummaryFormat::SharedPointer& entry)
{
CommandObjectTypeSummaryList_LoopCallbackParam* param = (CommandObjectTypeSummaryList_LoopCallbackParam*)pt2self;
return param->self->LoopCallback(type, entry, param->regex, param->result);
}
class CommandObjectTypeFormat : public CommandObjectMultiword
{
public:
CommandObjectTypeFormat (CommandInterpreter &interpreter) :
CommandObjectMultiword (interpreter,
"type format",
"A set of commands for editing variable display options",
"A set of commands for editing variable value display options",
"type format [<sub-command-options>] ")
{
LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeAdd (interpreter)));
LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeDelete (interpreter)));
LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeList (interpreter)));
LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeFormatAdd (interpreter)));
LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeFormatClear (interpreter)));
LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeFormatDelete (interpreter)));
LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeFormatList (interpreter)));
}
@ -341,6 +791,27 @@ public:
}
};
class CommandObjectTypeSummary : public CommandObjectMultiword
{
public:
CommandObjectTypeSummary (CommandInterpreter &interpreter) :
CommandObjectMultiword (interpreter,
"type format",
"A set of commands for editing variable summary display options",
"type summary [<sub-command-options>] ")
{
LoadSubCommand ("add", CommandObjectSP (new CommandObjectTypeSummaryAdd (interpreter)));
LoadSubCommand ("clear", CommandObjectSP (new CommandObjectTypeSummaryClear (interpreter)));
LoadSubCommand ("delete", CommandObjectSP (new CommandObjectTypeSummaryDelete (interpreter)));
LoadSubCommand ("list", CommandObjectSP (new CommandObjectTypeSummaryList (interpreter)));
}
~CommandObjectTypeSummary ()
{
}
};
//-------------------------------------------------------------------------
// CommandObjectType
//-------------------------------------------------------------------------
@ -352,6 +823,7 @@ CommandObjectType::CommandObjectType (CommandInterpreter &interpreter) :
"type [<sub-command-options>]")
{
LoadSubCommand ("format", CommandObjectSP (new CommandObjectTypeFormat (interpreter)));
LoadSubCommand ("summary", CommandObjectSP (new CommandObjectTypeSummary (interpreter)));
}

View File

@ -11,6 +11,9 @@
#include <map>
#include "clang/AST/DeclCXX.h"
#include "clang/AST/Type.h"
#include "lldb/lldb-private.h"
#include "lldb/Core/ConnectionFileDescriptor.h"
#include "lldb/Core/FormatManager.h"
@ -20,6 +23,7 @@
#include "lldb/Core/StreamAsynchronousIO.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Core/Timer.h"
#include "lldb/Core/ValueObject.h"
#include "lldb/Host/Terminal.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Target/TargetList.h"
@ -698,13 +702,20 @@ Debugger::FormatPrompt
const ExecutionContext *exe_ctx,
const Address *addr,
Stream &s,
const char **end
const char **end,
ValueObject* vobj
)
{
ValueObject* realvobj = NULL; // makes it super-easy to parse pointers
bool success = true;
const char *p;
for (p = format; *p != '\0'; ++p)
{
if(realvobj)
{
vobj = realvobj;
realvobj = NULL;
}
size_t non_special_chars = ::strcspn (p, "${}\\");
if (non_special_chars > 0)
{
@ -732,7 +743,7 @@ Debugger::FormatPrompt
++p; // Skip the '{'
if (FormatPrompt (p, sc, exe_ctx, addr, sub_strm, &p))
if (FormatPrompt (p, sc, exe_ctx, addr, sub_strm, &p, vobj))
{
// The stream had all it needed
s.Write(sub_strm.GetData(), sub_strm.GetSize());
@ -777,6 +788,284 @@ Debugger::FormatPrompt
bool var_success = false;
switch (var_name_begin[0])
{
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))
{
if (ClangASTContext::IsCharType (elem_or_pointee_clang_type))
{
StreamString sstr;
ExecutionContextScope *exe_scope = vobj->GetExecutionContextScope();
Process *process = exe_scope->CalculateProcess();
if(!process) break;
lldb::addr_t cstr_address = LLDB_INVALID_ADDRESS;
AddressType cstr_address_type = eAddressTypeInvalid;
DataExtractor data;
size_t bytes_read = 0;
std::vector<char> data_buffer;
Error error;
cstr_address = vobj->GetPointerValue (cstr_address_type, true);
{
const size_t k_max_buf_size = 256;
data_buffer.resize (k_max_buf_size + 1);
// NULL terminate in case we don't get the entire C string
data_buffer.back() = '\0';
sstr << '"';
data.SetData (&data_buffer.front(), data_buffer.size(), endian::InlHostByteOrder());
while ((bytes_read = process->ReadMemory (cstr_address, &data_buffer.front(), k_max_buf_size, error)) > 0)
{
size_t len = strlen(&data_buffer.front());
if (len == 0)
break;
if (len > bytes_read)
len = bytes_read;
data.Dump (&sstr,
0, // Start offset in "data"
eFormatCharArray, // Print as characters
1, // Size of item (1 byte for a char!)
len, // How many bytes to print?
UINT32_MAX, // num per line
LLDB_INVALID_ADDRESS,// base address
0, // bitfield bit size
0); // bitfield bit offset
if (len < k_max_buf_size)
break;
cstr_address += k_max_buf_size;
}
sstr << '"';
s.PutCString(sstr.GetData());
var_success = true;
break;
}
}
else /*if (ClangASTContext::IsAggregateType (elem_or_pointee_clang_type)) or this is some other pointer type*/
{
Error error;
realvobj = vobj;
vobj = vobj->Dereference(error).get();
if(!vobj || error.Fail())
break;
}
}
else
break;
}
case 'v':
{
const char* targetvalue;
bool use_summary = false;
ValueObject* target;
lldb::Format custom_format = eFormatInvalid;
int bitfield_lower = -1;
int bitfield_higher = -1;
if (!vobj) break;
// simplest case ${var}, just print vobj's value
if (::strncmp (var_name_begin, "var}", strlen("var}")) == 0)
target = vobj;
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;
{
const char* percent_position = ::strchr(var_name_begin,'%'); // TODO: make this a constant
//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);
FormatManager::GetFormatFromCString(format_name,
true,
custom_format); // if this fails, custom_format is reset to invalid
delete format_name;
//}
}
}
else if (::strncmp(var_name_begin,"var[",strlen("var[")) == 0)
{
// this is a bitfield variable
const char *var_name_final;
target = vobj;
{
const char* 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);
FormatManager::GetFormatFromCString(format_name,
true,
custom_format); // if this fails, custom_format is reset to invalid
delete format_name;
}
}
{
// code here might be simpler than in the case below
const char* open_bracket_position = ::strchr(var_name_begin,'[');
if(open_bracket_position && open_bracket_position < var_name_final)
{
char* separator_position = ::strchr(open_bracket_position,'-'); // might be NULL if this is a simple var[N] bitfield
char* 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 (separator_position == NULL || separator_position > var_name_end)
{
char *end = NULL;
bitfield_lower = ::strtoul (open_bracket_position+1, &end, 0);
bitfield_higher = bitfield_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;
bitfield_lower = ::strtoul (open_bracket_position+1, &end, 0);
bitfield_higher = ::strtoul (separator_position+1, &end, 0);
//printf("got to read low=%d high=%d\n",bitfield_lower,bitfield_higher);
}
else
break;
if(bitfield_lower > bitfield_higher)
break;
}
}
}
// this is ${var.something} or multiple .something nested
else if (::strncmp (var_name_begin, "var", strlen("var")) == 0)
{
// check for custom format string
// we need this because we might have ${var.something%format}. in this case var_name_end
// still points to the closing }, but we must extract the variable name only up to
// before the %. var_name_final will point to that % sign position
const char* var_name_final;
{
const char* 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);
FormatManager::GetFormatFromCString(format_name,
true,
custom_format); // if this fails, custom_format is reset to invalid
delete format_name;
}
}
{
const char* open_bracket_position = ::strchr(var_name_begin,'[');
if(open_bracket_position && open_bracket_position < var_name_final)
{
char* separator_position = ::strchr(open_bracket_position,'-'); // might be NULL if this is a simple var[N] bitfield
char* 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 (separator_position == NULL || separator_position > var_name_end)
{
char *end = NULL;
bitfield_lower = ::strtoul (open_bracket_position+1, &end, 0);
bitfield_higher = bitfield_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;
bitfield_lower = ::strtoul (open_bracket_position+1, &end, 0);
bitfield_higher = ::strtoul (separator_position+1, &end, 0);
//printf("got to read low=%d high=%d\n",bitfield_lower,bitfield_higher);
}
else
break;
if(bitfield_lower > bitfield_higher)
break;
//*((char*)open_bracket_position) = '\0';
//printf("variable name is %s\n",var_name_begin);
//*((char*)open_bracket_position) = '[';
}
}
Error error;
lldb::VariableSP var_sp;
StreamString sstring;
vobj->GetExpressionPath(sstring, true, ValueObject::eHonorPointers);
//printf("name to expand in phase 0: %s\n",sstring.GetData());
sstring.PutRawBytes(var_name_begin+3, var_name_final-var_name_begin-3);
//printf("name to expand in phase 1: %s\n",sstring.GetData());
std::string name = std::string(sstring.GetData());
target = exe_ctx->frame->GetValueForVariableExpressionPath (name.c_str(),
eNoDynamicValues,
0,
var_sp,
error).get();
if (error.Fail())
{
//printf("ERROR: %s\n",error.AsCString("unknown"));
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");
// format this as a bitfield
DataExtractor extractor = target->GetDataExtractor();
uint32_t item_byte_size = ClangASTType::GetTypeByteSize(target->GetClangAST(), target->GetClangType());
if(custom_format == eFormatInvalid)
custom_format = eFormatHex;
var_success =
extractor.Dump(&s, 0, custom_format, item_byte_size, 1, 1, LLDB_INVALID_ADDRESS, bitfield_higher-bitfield_lower+1, bitfield_lower) > 0;
//printf("var_success = %s\n",var_success ? "true" : "false");
}
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");
if(targetvalue)
s.PutCString(targetvalue);
var_success = targetvalue;
//printf("here I come 4 : %s\n",var_success ? "good" : "bad");
if(custom_format != eFormatInvalid)
target->SetFormat(eFormatDefault);
//printf("here I come 5\n");
}
break;
}
case 'a':
if (::strncmp (var_name_begin, "addr}", strlen("addr}")) == 0)
{
@ -1321,27 +1610,76 @@ GetFormatManager() {
}
bool
Debugger::GetFormatForType (const ConstString &type, lldb::Format& format, bool& cascade)
Debugger::ValueFormats::Get(ValueObject& vobj, ValueFormat::SharedPointer &entry)
{
return GetFormatManager().GetFormatForType(type, format, cascade);
return GetFormatManager().Value().Get(vobj,entry);
}
void
Debugger::AddFormatForType (const ConstString &type, lldb::Format format, bool cascade)
Debugger::ValueFormats::Add(const ConstString &type, const ValueFormat::SharedPointer &entry)
{
GetFormatManager().AddFormatForType(type,format, cascade);
GetFormatManager().Value().Add(type.AsCString(),entry);
}
bool
Debugger::DeleteFormatForType (const ConstString &type)
Debugger::ValueFormats::Delete(const ConstString &type)
{
return GetFormatManager().DeleteFormatForType(type);
return GetFormatManager().Value().Delete(type.AsCString());
}
void
Debugger::LoopThroughFormatList (FormatManager::Callback callback, void* callback_baton)
Debugger::ValueFormats::Clear()
{
return GetFormatManager().LoopThroughFormatList(callback, callback_baton);
GetFormatManager().Value().Clear();
}
void
Debugger::ValueFormats::LoopThrough(FormatManager::ValueCallback callback, void* callback_baton)
{
GetFormatManager().Value().LoopThrough(callback, callback_baton);
}
uint32_t
Debugger::ValueFormats::GetCurrentRevision()
{
return GetFormatManager().GetCurrentRevision();
}
bool
Debugger::SummaryFormats::Get(ValueObject& vobj, SummaryFormat::SharedPointer &entry)
{
return GetFormatManager().Summary().Get(vobj,entry);
}
void
Debugger::SummaryFormats::Add(const ConstString &type, const SummaryFormat::SharedPointer &entry)
{
GetFormatManager().Summary().Add(type.AsCString(),entry);
}
bool
Debugger::SummaryFormats::Delete(const ConstString &type)
{
return GetFormatManager().Summary().Delete(type.AsCString());
}
void
Debugger::SummaryFormats::Clear()
{
GetFormatManager().Summary().Clear();
}
void
Debugger::SummaryFormats::LoopThrough(FormatManager::SummaryCallback callback, void* callback_baton)
{
GetFormatManager().Summary().LoopThrough(callback, callback_baton);
}
uint32_t
Debugger::SummaryFormats::GetCurrentRevision()
{
return GetFormatManager().GetCurrentRevision();
}
#pragma mark Debugger::SettingsController

View File

@ -151,61 +151,3 @@ FormatManager::GetFormatAsCString (Format format)
return g_format_infos[format].format_name;
return NULL;
}
bool
FormatManager::GetFormatForType (const ConstString &type_name, lldb::Format& format, bool& cascade)
{
Mutex::Locker locker (m_format_map_mutex);
FormatMap& fmtmap = m_format_map;
FormatMap::iterator iter = fmtmap.find(type_name.GetCString());
if(iter == fmtmap.end())
return false;
else {
format = iter->second.format;
cascade = iter->second.cascades;
return true;
}
}
void
FormatManager::AddFormatForType (const ConstString &type_name, lldb::Format format, bool cascade)
{
Entry entry(format, cascade);
Mutex::Locker locker (m_format_map_mutex);
FormatMap& fmtmap = m_format_map;
fmtmap[type_name.GetCString()] = entry;
}
bool
FormatManager::DeleteFormatForType (const ConstString &type_name)
{
Mutex::Locker locker (m_format_map_mutex);
FormatMap& fmtmap = m_format_map;
const char* typeCS = type_name.GetCString();
FormatMap::iterator iter = fmtmap.find(typeCS);
if (iter != fmtmap.end())
{
fmtmap.erase(typeCS);
return true;
}
return false;
}
void
FormatManager::LoopThroughFormatList (Callback callback, void* param)
{
if (callback)
{
Mutex::Locker locker (m_format_map_mutex);
FormatIterator pos, end = m_format_map.end();
for (pos = m_format_map.begin(); pos != end; ++pos)
{
const char* type = pos->first;
lldb::Format format = pos->second.format;
bool cascade = pos->second.cascades;
if (!callback(param, type, format, cascade))
break;
}
}
}

View File

@ -19,6 +19,7 @@
// Project includes
#include "lldb/Core/DataBufferHeap.h"
#include "lldb/Core/Debugger.h"
#include "lldb/Core/StreamString.h"
#include "lldb/Core/ValueObjectChild.h"
#include "lldb/Core/ValueObjectConstResult.h"
@ -71,7 +72,10 @@ ValueObject::ValueObject (ValueObject &parent) :
m_children_count_valid (false),
m_old_value_valid (false),
m_pointers_point_to_load_addrs (false),
m_is_deref_of_parent (false)
m_is_deref_of_parent (false),
m_last_format_mgr_revision(0),
m_last_summary_format(),
m_last_value_format()
{
m_manager->ManageObject(this);
}
@ -103,7 +107,10 @@ ValueObject::ValueObject (ExecutionContextScope *exe_scope) :
m_children_count_valid (false),
m_old_value_valid (false),
m_pointers_point_to_load_addrs (false),
m_is_deref_of_parent (false)
m_is_deref_of_parent (false),
m_last_format_mgr_revision(0),
m_last_summary_format(),
m_last_value_format()
{
m_manager = new ValueObjectManager();
m_manager->ManageObject (this);
@ -119,6 +126,9 @@ ValueObject::~ValueObject ()
bool
ValueObject::UpdateValueIfNeeded ()
{
UpdateFormatsIfNeeded();
// If this is a constant value, then our success is predicated on whether
// we have an error or not
if (GetIsConstant())
@ -168,6 +178,26 @@ ValueObject::UpdateValueIfNeeded ()
return m_error.Success();
}
void
ValueObject::UpdateFormatsIfNeeded()
{
/*printf("CHECKING FOR UPDATES. I am at revision %d, while the format manager is at revision %d\n",
m_last_format_mgr_revision,
Debugger::ValueFormats::GetCurrentRevision());*/
if (m_last_format_mgr_revision != Debugger::ValueFormats::GetCurrentRevision())
{
if (m_last_summary_format.get())
m_last_summary_format.reset((SummaryFormat*)NULL);
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);
m_last_format_mgr_revision = Debugger::ValueFormats::GetCurrentRevision();
m_value_str.clear();
m_summary_str.clear();
}
}
DataExtractor &
ValueObject::GetDataExtractor ()
{
@ -454,9 +484,23 @@ const char *
ValueObject::GetSummaryAsCString ()
{
if (UpdateValueIfNeeded ())
{
{
if (m_summary_str.empty())
{
if (m_last_summary_format.get())
{
StreamString s;
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))
{
m_summary_str.swap(s.GetString());
return m_summary_str.c_str();
}
return NULL;
}
clang_type_t clang_type = GetClangType();
// See if this is a pointer to a C string?
@ -664,28 +708,6 @@ ValueObject::GetValueAsCString ()
{
if (UpdateValueIfNeeded())
{
/*
this is a quick fix for the case in which we display a variable, then change its format with
type format add and the old display string keeps showing until one steps through the code
*/
{
const Value::ContextType context_type = m_value.GetContextType();
switch (context_type)
{
case Value::eContextTypeClangType:
case Value::eContextTypeLLDBType:
case Value::eContextTypeVariable:
{
Format format = GetFormat();
if (format != m_last_format)
m_value_str.clear();
}
break;
default:
break;
}
}
if (m_value_str.empty())
{
const Value::ContextType context_type = m_value.GetContextType();
@ -701,13 +723,18 @@ ValueObject::GetValueAsCString ()
{
StreamString sstr;
Format format = GetFormat();
if (format == eFormatDefault)
format = ClangASTType::GetFormat(clang_type);
if (format == eFormatDefault)
{
if (m_last_value_format)
format = m_last_value_format->m_format;
else
format = ClangASTType::GetFormat(clang_type);
}
if (ClangASTType::DumpTypeValue (GetClangAST(), // The clang AST
clang_type, // The clang type to display
&sstr,
m_last_format = format, // Format to display this type with
format, // Format to display this type with
m_data, // Data to extract from
0, // Byte offset into "m_data"
GetByteSize(), // Byte size of item in "m_data"
@ -1123,16 +1150,22 @@ ValueObject::GetNonBaseClassParent()
}
void
ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes)
ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes, GetExpressionPathFormat epformat)
{
const bool is_deref_of_parent = IsDereferenceOfParent ();
if (is_deref_of_parent)
s.PutCString("*(");
if (GetParent())
GetParent()->GetExpressionPath (s, qualify_cxx_base_classes);
if(is_deref_of_parent && epformat == eDereferencePointers) {
// this is the original format of GetExpressionPath() producing code like *(a_ptr).memberName, which is entirely
// fine, until you put this into StackFrame::GetValueForVariableExpressionPath() which prefers to see a_ptr->memberName.
// the eHonorPointers mode is meant to produce strings in this latter format
s.PutCString("*(");
}
ValueObject* parent = GetParent();
if (parent)
parent->GetExpressionPath (s, qualify_cxx_base_classes, epformat);
if (!IsBaseClass())
{
if (!is_deref_of_parent)
@ -1145,14 +1178,21 @@ ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes)
{
const uint32_t non_base_class_parent_type_info = ClangASTContext::GetTypeInfo (non_base_class_parent_clang_type, NULL, NULL);
if (non_base_class_parent_type_info & ClangASTContext::eTypeIsPointer)
if(parent && parent->IsDereferenceOfParent() && epformat == eHonorPointers)
{
s.PutCString("->");
}
else if ((non_base_class_parent_type_info & ClangASTContext::eTypeHasChildren) &&
!(non_base_class_parent_type_info & ClangASTContext::eTypeIsArray))
{
s.PutChar('.');
else
{
if (non_base_class_parent_type_info & ClangASTContext::eTypeIsPointer)
{
s.PutCString("->");
}
else if ((non_base_class_parent_type_info & ClangASTContext::eTypeHasChildren) &&
!(non_base_class_parent_type_info & ClangASTContext::eTypeIsArray))
{
s.PutChar('.');
}
}
}
}
@ -1170,8 +1210,9 @@ ValueObject::GetExpressionPath (Stream &s, bool qualify_cxx_base_classes)
}
}
if (is_deref_of_parent)
if (is_deref_of_parent && epformat == eDereferencePointers) {
s.PutChar(')');
}
}
void
@ -1245,6 +1286,8 @@ ValueObject::DumpValueObject
}
const char *val_cstr = NULL;
const char *sum_cstr = NULL;
SummaryFormat* entry = valobj->m_last_summary_format.get();
if (err_cstr == NULL)
{
@ -1261,10 +1304,13 @@ ValueObject::DumpValueObject
const bool is_ref = type_flags.Test (ClangASTContext::eTypeIsReference);
if (print_valobj)
{
const char *sum_cstr = valobj->GetSummaryAsCString();
sum_cstr = valobj->GetSummaryAsCString();
if (val_cstr)
s.Printf(" %s", val_cstr);
// We must calculate this value in realtime because entry might alter this variable's value
// (e.g. by saying ${var%fmt}) and render precached values useless
if (val_cstr && (!entry || entry->DoesPrintValue() || !sum_cstr))
s.Printf(" %s", valobj->GetValueAsCString());
if (sum_cstr)
s.Printf(" %s", sum_cstr);
@ -1314,7 +1360,32 @@ ValueObject::DumpValueObject
print_children = false;
}
if (print_children)
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))
{
const uint32_t num_children = valobj->GetNumChildren();
if (num_children)

View File

@ -247,25 +247,8 @@ ClangASTType::GetFormat ()
lldb::Format
ClangASTType::GetFormat (clang_type_t clang_type)
{
// first of all, check for a valid format for this type itself
clang::QualType qual_type(clang::QualType::getFromOpaquePtr(clang_type));
lldb::Format format;
bool cascade;
if(Debugger::GetFormatForType(GetClangTypeName(qual_type), format, cascade))
return format; // return it if found
// here, I know this type does not have a direct format. two things can happen:
// 1) this is a typedef - I expand this to its parent type and look there
// 2) this is not a typedef - I use the default formatting options
const clang::TypedefType *typedef_type = qual_type->getAs<clang::TypedefType>();
while (typedef_type) {
qual_type = typedef_type->getDecl()->getUnderlyingType();
std::string name = qual_type.getAsString();
if(Debugger::GetFormatForType(GetClangTypeName(qual_type), format, cascade) && cascade) // if I have a cascading format...
return format; // ...use it
typedef_type = qual_type->getAs<clang::TypedefType>(); // try to expand another level
}
switch (qual_type->getTypeClass())
{
case clang::Type::FunctionNoProto:
@ -1347,6 +1330,23 @@ ClangASTType::ReadFromMemory
data);
}
uint32_t
ClangASTType::GetTypeByteSize()
{
return GetTypeByteSize(m_ast,
m_type);
}
uint32_t
ClangASTType::GetTypeByteSize(
clang::ASTContext *ast_context,
lldb::clang_type_t opaque_clang_qual_type)
{
clang::QualType qual_type(clang::QualType::getFromOpaquePtr(opaque_clang_qual_type));
return (ast_context->getTypeSize (qual_type) + 7) / 8;
}
bool
ClangASTType::ReadFromMemory

View File

@ -51,11 +51,8 @@ class DataFormatterTestCase(TestBase):
# 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 delete Speed', check=False)
self.runCmd('type format delete Bitfield', check=False)
self.runCmd('type format delete RealNumber', check=False)
self.runCmd('type format delete Type2', check=False)
self.runCmd('type format delete Type1', check=False)
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)
@ -87,6 +84,33 @@ class DataFormatterTestCase(TestBase):
# 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__':

View File

@ -31,6 +31,12 @@ 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[])
{
@ -57,6 +63,8 @@ int main (int argc, const char * argv[])
Speed* SPPtrILookHex = new Speed(16);
Point iAmSomewhere(4,6);
return 0; // Set break point at this line.
}