Add a new way to bind a format to a type: by enum type

The "type format add" command gets a new flag --type (-t). If you pass -t <sometype>, upon fetching the value for an object of your type,
LLDB will display it as-if it was of enumeration type <sometype>
This is useful in cases of non-contiguous enums where there are empty gaps of unspecified values, and as such one cannot type their variables as the enum type,
but users would still like to see them as-if they were of the enum type (e.g. DWARF field types with their user-reserved ranges)

The SB API has also been improved to handle both types of formats, and a test case is added

llvm-svn: 198105
This commit is contained in:
Enrico Granata 2013-12-28 08:44:02 +00:00
parent ffd810525d
commit 30f287fde5
11 changed files with 400 additions and 52 deletions
lldb
include/lldb
scripts/Python/interface
source
test/functionalities/data-formatter/data-formatter-enum-format

View File

@ -22,6 +22,9 @@ public:
SBTypeFormat (lldb::Format format,
uint32_t options = 0); // see lldb::eTypeOption values
SBTypeFormat (const char* type,
uint32_t options = 0); // see lldb::eTypeOption values
SBTypeFormat (const lldb::SBTypeFormat &rhs);
@ -33,12 +36,18 @@ public:
lldb::Format
GetFormat ();
const char*
GetTypeName ();
uint32_t
GetOptions();
void
SetFormat (lldb::Format);
void
SetTypeName (const char*);
void
SetOptions (uint32_t);
@ -73,8 +82,15 @@ protected:
SBTypeFormat (const lldb::TypeFormatImplSP &);
enum class Type
{
eTypeKeepSame,
eTypeFormat,
eTypeEnum
};
bool
CopyOnWrite_Impl();
CopyOnWrite_Impl(Type);
};

View File

@ -130,15 +130,12 @@ namespace lldb_private {
uint32_t m_flags;
};
TypeFormatImpl (lldb::Format f = lldb::eFormatInvalid,
const Flags& flags = Flags());
TypeFormatImpl (const Flags& flags = Flags());
typedef std::shared_ptr<TypeFormatImpl> SharedPointer;
typedef bool(*ValueCallback)(void*, ConstString, const lldb::TypeFormatImplSP&);
~TypeFormatImpl ()
{
}
virtual ~TypeFormatImpl () = default;
bool
Cascades () const
@ -173,19 +170,7 @@ namespace lldb_private {
{
m_flags.SetSkipReferences(value);
}
lldb::Format
GetFormat () const
{
return m_format;
}
void
SetFormat (lldb::Format fmt)
{
m_format = fmt;
}
uint32_t
GetOptions ()
{
@ -204,24 +189,123 @@ namespace lldb_private {
return m_my_revision;
}
enum class Type
{
eTypeUnknown,
eTypeFormat,
eTypeEnum
};
virtual Type
GetType ()
{
return Type::eTypeUnknown;
}
// we are using a ValueObject* instead of a ValueObjectSP because we do not need to hold on to this for
// extended periods of time and we trust the ValueObject to stay around for as long as it is required
// for us to generate its value
bool
virtual bool
FormatObject (ValueObject *valobj,
std::string& dest) const;
std::string& dest) const = 0;
std::string
GetDescription();
virtual std::string
GetDescription() = 0;
protected:
Flags m_flags;
lldb::Format m_format;
uint32_t m_my_revision;
private:
DISALLOW_COPY_AND_ASSIGN(TypeFormatImpl);
};
};
class TypeFormatImpl_Format : public TypeFormatImpl
{
public:
TypeFormatImpl_Format (lldb::Format f = lldb::eFormatInvalid,
const TypeFormatImpl::Flags& flags = Flags());
typedef std::shared_ptr<TypeFormatImpl_Format> SharedPointer;
typedef bool(*ValueCallback)(void*, ConstString, const TypeFormatImpl_Format::SharedPointer&);
virtual ~TypeFormatImpl_Format () = default;
lldb::Format
GetFormat () const
{
return m_format;
}
void
SetFormat (lldb::Format fmt)
{
m_format = fmt;
}
virtual TypeFormatImpl::Type
GetType ()
{
return TypeFormatImpl::Type::eTypeFormat;
}
virtual bool
FormatObject (ValueObject *valobj,
std::string& dest) const;
virtual std::string
GetDescription();
protected:
lldb::Format m_format;
private:
DISALLOW_COPY_AND_ASSIGN(TypeFormatImpl_Format);
};
class TypeFormatImpl_EnumType : public TypeFormatImpl
{
public:
TypeFormatImpl_EnumType (ConstString type_name = ConstString(""),
const TypeFormatImpl::Flags& flags = Flags());
typedef std::shared_ptr<TypeFormatImpl_EnumType> SharedPointer;
typedef bool(*ValueCallback)(void*, ConstString, const TypeFormatImpl_EnumType::SharedPointer&);
~TypeFormatImpl_EnumType () = default;
ConstString
GetTypeName ()
{
return m_enum_type;
}
void
SetTypeName (ConstString enum_type)
{
m_enum_type = enum_type;
}
virtual TypeFormatImpl::Type
GetType ()
{
return TypeFormatImpl::Type::eTypeEnum;
}
virtual bool
FormatObject (ValueObject *valobj,
std::string& dest) const;
virtual std::string
GetDescription();
protected:
ConstString m_enum_type;
mutable std::map<void*,ClangASTType> m_types;
private:
DISALLOW_COPY_AND_ASSIGN(TypeFormatImpl_EnumType);
};
} // namespace lldb_private
#endif // lldb_TypeFormat_h_

View File

@ -21,6 +21,8 @@ namespace lldb {
SBTypeFormat (lldb::Format format, uint32_t options = 0);
SBTypeFormat (const char* type, uint32_t options = 0);
SBTypeFormat (const lldb::SBTypeFormat &rhs);
~SBTypeFormat ();
@ -34,12 +36,18 @@ namespace lldb {
lldb::Format
GetFormat ();
const char*
GetTypeName ();
uint32_t
GetOptions();
void
SetFormat (lldb::Format);
void
SetTypeName (const char*);
void
SetOptions (uint32_t);

View File

@ -25,7 +25,13 @@ m_opaque_sp()
SBTypeFormat::SBTypeFormat (lldb::Format format,
uint32_t options)
: m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl(format,options)))
: m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl_Format(format,options)))
{
}
SBTypeFormat::SBTypeFormat (const char* type,
uint32_t options)
: m_opaque_sp(TypeFormatImplSP(new TypeFormatImpl_EnumType(ConstString(type ? type : ""),options)))
{
}
@ -47,11 +53,19 @@ SBTypeFormat::IsValid() const
lldb::Format
SBTypeFormat::GetFormat ()
{
if (IsValid())
return m_opaque_sp->GetFormat();
if (IsValid() && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat)
return ((TypeFormatImpl_Format*)m_opaque_sp.get())->GetFormat();
return lldb::eFormatInvalid;
}
const char*
SBTypeFormat::GetTypeName ()
{
if (IsValid() && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeEnum)
return ((TypeFormatImpl_EnumType*)m_opaque_sp.get())->GetTypeName().AsCString("");
return "";
}
uint32_t
SBTypeFormat::GetOptions()
{
@ -63,14 +77,21 @@ SBTypeFormat::GetOptions()
void
SBTypeFormat::SetFormat (lldb::Format fmt)
{
if (CopyOnWrite_Impl())
m_opaque_sp->SetFormat(fmt);
if (CopyOnWrite_Impl(Type::eTypeFormat))
((TypeFormatImpl_Format*)m_opaque_sp.get())->SetFormat(fmt);
}
void
SBTypeFormat::SetTypeName (const char* type)
{
if (CopyOnWrite_Impl(Type::eTypeEnum))
((TypeFormatImpl_EnumType*)m_opaque_sp.get())->SetTypeName(ConstString(type ? type : ""));
}
void
SBTypeFormat::SetOptions (uint32_t value)
{
if (CopyOnWrite_Impl())
if (CopyOnWrite_Impl(Type::eTypeKeepSame))
m_opaque_sp->SetOptions(value);
}
@ -143,13 +164,30 @@ SBTypeFormat::SBTypeFormat (const lldb::TypeFormatImplSP &typeformat_impl_sp) :
}
bool
SBTypeFormat::CopyOnWrite_Impl()
SBTypeFormat::CopyOnWrite_Impl(Type type)
{
if (!IsValid())
return false;
if (m_opaque_sp.unique())
if (m_opaque_sp.unique() &&
((type == Type::eTypeKeepSame) ||
(type == Type::eTypeFormat && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat) ||
(type == Type::eTypeEnum && m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeEnum))
)
return true;
SetSP(TypeFormatImplSP(new TypeFormatImpl(GetFormat(),GetOptions())));
if (type == Type::eTypeKeepSame)
{
if (m_opaque_sp->GetType() == TypeFormatImpl::Type::eTypeFormat)
type = Type::eTypeFormat;
else
type = Type::eTypeEnum;
}
if (type == Type::eTypeFormat)
SetSP(TypeFormatImplSP(new TypeFormatImpl_Format(GetFormat(),GetOptions())));
else
SetSP(TypeFormatImplSP(new TypeFormatImpl_EnumType(ConstString(GetTypeName()),GetOptions())));
return true;
}

View File

@ -371,6 +371,7 @@ private:
m_skip_references = false;
m_regex = false;
m_category.assign("default");
m_custom_type_name.clear();
}
virtual Error
SetOptionValue (CommandInterpreter &interpreter,
@ -400,6 +401,9 @@ private:
case 'x':
m_regex = true;
break;
case 't':
m_custom_type_name.assign(option_value);
break;
default:
error.SetErrorStringWithFormat ("unrecognized option '%c'", short_option);
break;
@ -419,6 +423,7 @@ private:
bool m_skip_pointers;
bool m_regex;
std::string m_category;
std::string m_custom_type_name;
};
OptionGroupOptions m_option_group;
@ -480,7 +485,7 @@ public:
);
// Add the "--format" to all options groups
m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_ALL);
m_option_group.Append (&m_format_options, OptionGroupFormat::OPTION_GROUP_FORMAT, LLDB_OPT_SET_1);
m_option_group.Append (&m_command_options);
m_option_group.Finalize();
@ -504,7 +509,7 @@ protected:
}
const Format format = m_format_options.GetFormat();
if (format == eFormatInvalid)
if (format == eFormatInvalid && m_command_options.m_custom_type_name.empty())
{
result.AppendErrorWithFormat ("%s needs a valid format.\n", m_cmd_name.c_str());
result.SetStatus(eReturnStatusFailed);
@ -513,10 +518,16 @@ protected:
TypeFormatImplSP entry;
entry.reset(new TypeFormatImpl(format,
TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade).
SetSkipPointers(m_command_options.m_skip_pointers).
SetSkipReferences(m_command_options.m_skip_references)));
if (m_command_options.m_custom_type_name.empty())
entry.reset(new TypeFormatImpl_Format(format,
TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade).
SetSkipPointers(m_command_options.m_skip_pointers).
SetSkipReferences(m_command_options.m_skip_references)));
else
entry.reset(new TypeFormatImpl_EnumType(ConstString(m_command_options.m_custom_type_name.c_str()),
TypeFormatImpl::Flags().SetCascades(m_command_options.m_cascade).
SetSkipPointers(m_command_options.m_skip_pointers).
SetSkipReferences(m_command_options.m_skip_references)));
// now I have a valid format, let's add it to every type
@ -562,11 +573,12 @@ protected:
OptionDefinition
CommandObjectTypeFormatAdd::CommandOptions::g_option_table[] =
{
{ LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."},
{ LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."},
{ LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."},
{ LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."},
{ LLDB_OPT_SET_ALL, false, "category", 'w', OptionParser::eRequiredArgument, NULL, 0, eArgTypeName, "Add this to the given category instead of the default one."},
{ LLDB_OPT_SET_ALL, false, "cascade", 'C', OptionParser::eRequiredArgument, NULL, 0, eArgTypeBoolean, "If true, cascade through typedef chains."},
{ LLDB_OPT_SET_ALL, false, "skip-pointers", 'p', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for pointers-to-type objects."},
{ LLDB_OPT_SET_ALL, false, "skip-references", 'r', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Don't use this format for references-to-type objects."},
{ LLDB_OPT_SET_ALL, false, "regex", 'x', OptionParser::eNoArgument, NULL, 0, eArgTypeNone, "Type names are actually regular expressions."},
{ LLDB_OPT_SET_2, false, "type", 't', OptionParser::eRequiredArgument, NULL, 0, eArgTypeName, "Format variables as if they were of this type."},
{ 0, false, NULL, 0, 0, NULL, 0, eArgTypeNone, NULL }
};

View File

@ -1402,7 +1402,7 @@ bool
ValueObject::GetValueAsCString (lldb::Format format,
std::string& destination)
{
return GetValueAsCString(TypeFormatImpl(format),destination);
return GetValueAsCString(TypeFormatImpl_Format(format),destination);
}
const char *
@ -1439,7 +1439,7 @@ ValueObject::GetValueAsCString ()
{
m_last_format = my_format;
if (!format_sp)
format_sp.reset(new TypeFormatImpl(my_format));
format_sp.reset(new TypeFormatImpl_Format(my_format));
if (GetValueAsCString(*format_sp.get(), m_value_str))
{
if (!m_value_did_change && m_old_value_valid)

View File

@ -741,7 +741,7 @@ AddFormat (TypeCategoryImpl::SharedPointer category_sp,
TypeFormatImpl::Flags flags,
bool regex = false)
{
lldb::TypeFormatImplSP format_sp(new TypeFormatImpl(format, flags));
lldb::TypeFormatImplSP format_sp(new TypeFormatImpl_Format(format, flags));
if (regex)
category_sp->GetRegexTypeFormatsContainer()->Add(RegularExpressionSP(new RegularExpression(type_name.AsCString())),format_sp);

View File

@ -25,22 +25,30 @@
#include "lldb/DataFormatters/TypeFormat.h"
#include "lldb/Interpreter/CommandInterpreter.h"
#include "lldb/Symbol/ClangASTType.h"
#include "lldb/Symbol/TypeList.h"
#include "lldb/Target/StackFrame.h"
#include "lldb/Target/Target.h"
using namespace lldb;
using namespace lldb_private;
TypeFormatImpl::TypeFormatImpl (lldb::Format f,
const Flags& flags) :
TypeFormatImpl::TypeFormatImpl (const Flags& flags) :
m_flags(flags),
m_my_revision(0)
{
}
TypeFormatImpl_Format::TypeFormatImpl_Format (lldb::Format f,
const TypeFormatImpl::Flags& flags) :
TypeFormatImpl(flags),
m_format (f)
{
}
bool
TypeFormatImpl::FormatObject (ValueObject *valobj,
std::string& dest) const
TypeFormatImpl_Format::FormatObject (ValueObject *valobj,
std::string& dest) const
{
if (!valobj)
return false;
@ -127,7 +135,7 @@ TypeFormatImpl::FormatObject (ValueObject *valobj,
}
std::string
TypeFormatImpl::GetDescription()
TypeFormatImpl_Format::GetDescription()
{
StreamString sstr;
sstr.Printf ("%s%s%s%s",
@ -138,3 +146,87 @@ TypeFormatImpl::GetDescription()
return sstr.GetString();
}
TypeFormatImpl_EnumType::TypeFormatImpl_EnumType (ConstString type_name,
const TypeFormatImpl::Flags& flags) :
TypeFormatImpl(flags),
m_enum_type(type_name),
m_types()
{
}
bool
TypeFormatImpl_EnumType::FormatObject (ValueObject *valobj,
std::string& dest) const
{
dest.clear();
if (!valobj)
return false;
if (valobj->GetClangType().IsAggregateType ())
return false;
ProcessSP process_sp;
TargetSP target_sp;
void* valobj_key = (process_sp = valobj->GetProcessSP()).get();
if (!valobj_key)
valobj_key = (target_sp = valobj->GetTargetSP()).get();
else
target_sp = process_sp->GetTarget().shared_from_this();
if (!valobj_key)
return false;
auto iter = m_types.find(valobj_key),
end = m_types.end();
ClangASTType valobj_enum_type;
if (iter == end)
{
// probably a redundant check
if (!target_sp)
return false;
const ModuleList& images(target_sp->GetImages());
SymbolContext sc;
TypeList types;
images.FindTypes(sc, m_enum_type, false, UINT32_MAX, types);
if (types.GetSize() == 0)
return false;
for (lldb::TypeSP type_sp : types.Types())
{
if (!type_sp)
continue;
if ( (type_sp->GetClangForwardType().GetTypeInfo() & ClangASTType::eTypeIsEnumeration) == ClangASTType::eTypeIsEnumeration)
{
valobj_enum_type = type_sp->GetClangFullType();
m_types.emplace(valobj_key,valobj_enum_type);
break;
}
}
}
else
valobj_enum_type = iter->second;
if (valobj_enum_type.IsValid() == false)
return false;
DataExtractor data;
valobj->GetData(data);
ExecutionContext exe_ctx (valobj->GetExecutionContextRef());
StreamString sstr;
valobj_enum_type.DumpTypeValue(&sstr,
lldb::eFormatEnum,
data,
0,
data.GetByteSize(),
0,
0,
exe_ctx.GetBestExecutionContextScope());
if (!sstr.GetString().empty())
dest.swap(sstr.GetString());
return !dest.empty();
}
std::string
TypeFormatImpl_EnumType::GetDescription()
{
StreamString sstr;
sstr.Printf ("as type %s%s%s%s",
m_enum_type.AsCString("<invalid type>"),
Cascades() ? "" : " (not cascading)",
SkipsPointers() ? " (skip pointers)" : "",
SkipsReferences() ? " (skip references)" : "");
return sstr.GetString();
}

View File

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

View File

@ -0,0 +1,80 @@
"""
Test lldb data formatter subsystem.
"""
import os, time
import unittest2
import lldb
from lldbtest import *
import lldbutil
class EnumFormatTestCase(TestBase):
mydir = TestBase.compute_mydir(__file__)
@unittest2.skipUnless(sys.platform.startswith("darwin"), "requires Darwin")
@dsym_test
def test_with_dsym_and_run_command(self):
"""Test data formatter commands."""
self.buildDsym()
self.data_formatter_commands()
@dwarf_test
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)
lldbutil.run_break_set_by_file_and_line (self, "main.cpp", self.line, num_expected_locations=1, loc_exact=True)
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 = ['(Foo) f = Case45',
'(int) x = 1',
'(int) y = 45',
'(int) z = 43'
]);
# 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 --type Foo int")
# The type format list should show our custom formats.
self.expect("type format list -w default",
substrs = ['int: as type Foo'])
self.expect("frame variable",
substrs = ['(Foo) f = Case45',
'(int) x = Case1',
'(int) y = Case45',
'(int) z = 43'
]);
if __name__ == '__main__':
import atexit
lldb.SBDebugger.Initialize()
atexit.register(lambda: lldb.SBDebugger.Terminate())
unittest2.main()

View File

@ -0,0 +1,13 @@
enum Foo {
Case1 = 1,
Case2 = 2,
Case45 = 45
};
int main() {
Foo f = Case45;
int x = 1;
int y = 45;
int z = 43;
return 1; // Set break point at this line.
}