forked from OSchip/llvm-project
[formatters] Add a libstdcpp formatter for for unordered_map, unordered_set, unordered_multimap, unordered_multiset
This diff adds a data formatter and tests for libstdcpp's unordered_map, unordered_set, unordered_multimap, unordered_multiset Reviewed By: wallace Differential Revision: https://reviews.llvm.org/D113760
This commit is contained in:
parent
2e6a0a8b81
commit
fcd288b52a
|
@ -9,6 +9,86 @@ import lldb.formatters.Logger
|
|||
# before relying on these formatters to do the right thing for your setup
|
||||
|
||||
|
||||
"""
|
||||
This formatter can be applied to all
|
||||
unordered map-like structures (unordered_map, unordered_multimap, unordered_set, unordered_multiset)
|
||||
"""
|
||||
class StdUnorderedMapSynthProvider:
|
||||
def __init__(self, valobj, dict):
|
||||
self.valobj = valobj
|
||||
self.count = None
|
||||
self.kind = self.get_object_kind(valobj)
|
||||
|
||||
def get_object_kind(self, valobj):
|
||||
type_name = valobj.GetTypeName()
|
||||
return "set" if "set" in type_name else "map"
|
||||
|
||||
def extract_type(self):
|
||||
type = self.valobj.GetType()
|
||||
# type of std::pair<key, value> is the first template
|
||||
# argument type of the 4th template argument to std::map and
|
||||
# 3rd template argument for std::set. That's why
|
||||
# we need to know kind of the object
|
||||
template_arg_num = 4 if self.kind == "map" else 3
|
||||
allocator_type = type.GetTemplateArgumentType(template_arg_num)
|
||||
data_type = allocator_type.GetTemplateArgumentType(0)
|
||||
return data_type
|
||||
|
||||
def update(self):
|
||||
# preemptively setting this to None - we might end up changing our mind
|
||||
# later
|
||||
self.count = None
|
||||
try:
|
||||
self.head = self.valobj.GetChildMemberWithName('_M_h')
|
||||
self.before_begin = self.head.GetChildMemberWithName('_M_before_begin')
|
||||
self.next = self.before_begin.GetChildMemberWithName('_M_nxt')
|
||||
self.data_type = self.extract_type()
|
||||
self.skip_size = self.next.GetType().GetByteSize()
|
||||
self.data_size = self.data_type.GetByteSize()
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
|
||||
def get_child_index(self, name):
|
||||
try:
|
||||
return int(name.lstrip('[').rstrip(']'))
|
||||
except:
|
||||
return -1
|
||||
|
||||
def get_child_at_index(self, index):
|
||||
logger = lldb.formatters.Logger.Logger()
|
||||
logger >> "Being asked to fetch child[" + str(index) + "]"
|
||||
if index < 0:
|
||||
return None
|
||||
if index >= self.num_children():
|
||||
return None
|
||||
try:
|
||||
offset = index
|
||||
current = self.next
|
||||
while offset > 0:
|
||||
current = current.GetChildMemberWithName('_M_nxt')
|
||||
offset = offset - 1
|
||||
return current.CreateChildAtOffset( '[' + str(index) + ']', self.skip_size, self.data_type)
|
||||
|
||||
except:
|
||||
logger >> "Cannot get child"
|
||||
return None
|
||||
|
||||
def num_children(self):
|
||||
if self.count is None:
|
||||
self.count = self.num_children_impl()
|
||||
return self.count
|
||||
|
||||
def num_children_impl(self):
|
||||
logger = lldb.formatters.Logger.Logger()
|
||||
try:
|
||||
count = self.head.GetChildMemberWithName('_M_element_count').GetValueAsUnsigned(0)
|
||||
return count
|
||||
except:
|
||||
logger >> "Could not determine the size"
|
||||
return 0
|
||||
|
||||
|
||||
class AbstractListSynthProvider:
|
||||
def __init__(self, valobj, dict, has_prev):
|
||||
'''
|
||||
|
|
|
@ -918,6 +918,11 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
|
|||
SyntheticChildrenSP(new ScriptedSyntheticChildren(
|
||||
stl_deref_flags,
|
||||
"lldb.formatters.cpp.gnu_libstdcpp.StdMapLikeSynthProvider")));
|
||||
cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
|
||||
RegularExpression("^std::unordered_(multi)?(map|set)<.+> >$"),
|
||||
SyntheticChildrenSP(new ScriptedSyntheticChildren(
|
||||
stl_deref_flags,
|
||||
"lldb.formatters.cpp.gnu_libstdcpp.StdUnorderedMapSynthProvider")));
|
||||
cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
|
||||
RegularExpression("^std::(__cxx11::)?list<.+>(( )?&)?$"),
|
||||
SyntheticChildrenSP(new ScriptedSyntheticChildren(
|
||||
|
@ -954,6 +959,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
|
|||
RegularExpression("^std::multiset<.+> >(( )?&)?$"),
|
||||
TypeSummaryImplSP(
|
||||
new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
|
||||
cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
|
||||
RegularExpression("^std::unordered_(multi)?(map|set)<.+> >$"),
|
||||
TypeSummaryImplSP(
|
||||
new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
|
||||
cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
|
||||
RegularExpression("^std::(__cxx11::)?list<.+>(( )?&)?$"),
|
||||
TypeSummaryImplSP(
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
CXX_SOURCES := main.cpp
|
||||
|
||||
include Makefile.rules
|
|
@ -9,18 +9,19 @@ from lldbsuite.test.decorators import *
|
|||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
USE_LIBSTDCPP = "USE_LIBSTDCPP"
|
||||
USE_LIBCPP = "USE_LIBCPP"
|
||||
|
||||
class LibcxxUnorderedDataFormatterTestCase(TestBase):
|
||||
|
||||
class GenericUnorderedDataFormatterTestCase(TestBase):
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
def setUp(self):
|
||||
TestBase.setUp(self)
|
||||
self.namespace = 'std'
|
||||
|
||||
@add_test_categories(["libc++"])
|
||||
def test_with_run_command(self):
|
||||
self.build()
|
||||
|
||||
def do_test_with_run_command(self, stdlib_type):
|
||||
self.build(dictionary={stdlib_type: "1"})
|
||||
self.runCmd("file " + self.getBuildArtifact("a.out"), CURRENT_EXECUTABLE_SET)
|
||||
|
||||
lldbutil.run_break_set_by_source_regexp(
|
||||
|
@ -76,3 +77,11 @@ class LibcxxUnorderedDataFormatterTestCase(TestBase):
|
|||
self.expect(("frame variable %s" % var_name), patterns=patterns)
|
||||
self.expect(("frame variable %s" % var_name), patterns=patterns)
|
||||
self.runCmd("continue")
|
||||
|
||||
@add_test_categories(["libstdcxx"])
|
||||
def test_with_run_command_libstdcpp(self):
|
||||
self.do_test_with_run_command(USE_LIBSTDCPP)
|
||||
|
||||
@add_test_categories(["libc++"])
|
||||
def test_with_run_command_libcpp(self):
|
||||
self.do_test_with_run_command(USE_LIBCPP)
|
|
@ -0,0 +1,68 @@
|
|||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
int g_the_foo = 0;
|
||||
|
||||
int thefoo_rw(int arg = 1) {
|
||||
if (arg < 0)
|
||||
arg = 0;
|
||||
if (!arg)
|
||||
arg = 1;
|
||||
g_the_foo += arg;
|
||||
return g_the_foo;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::unordered_map<int, std::string> map;
|
||||
map.emplace(1, "hello");
|
||||
map.emplace(2, "world");
|
||||
map.emplace(3, "this");
|
||||
map.emplace(4, "is");
|
||||
map.emplace(5, "me");
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
std::unordered_multimap<int, std::string> mmap;
|
||||
mmap.emplace(1, "hello");
|
||||
mmap.emplace(2, "hello");
|
||||
mmap.emplace(2, "world");
|
||||
mmap.emplace(3, "this");
|
||||
mmap.emplace(3, "this");
|
||||
mmap.emplace(3, "this");
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
std::unordered_set<int> iset;
|
||||
iset.emplace(1);
|
||||
iset.emplace(2);
|
||||
iset.emplace(3);
|
||||
iset.emplace(4);
|
||||
iset.emplace(5);
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
std::unordered_set<std::string> sset;
|
||||
sset.emplace("hello");
|
||||
sset.emplace("world");
|
||||
sset.emplace("this");
|
||||
sset.emplace("is");
|
||||
sset.emplace("me");
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
std::unordered_multiset<int> imset;
|
||||
imset.emplace(1);
|
||||
imset.emplace(2);
|
||||
imset.emplace(2);
|
||||
imset.emplace(3);
|
||||
imset.emplace(3);
|
||||
imset.emplace(3);
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
std::unordered_multiset<std::string> smset;
|
||||
smset.emplace("hello");
|
||||
smset.emplace("world");
|
||||
smset.emplace("world");
|
||||
smset.emplace("is");
|
||||
smset.emplace("is");
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -1,9 +0,0 @@
|
|||
CXX_SOURCES := main.cpp
|
||||
|
||||
# Work around "exception specification in declaration does not match previous
|
||||
# declaration" errors present in older libc++ releases. This error was fixed in
|
||||
# the 3.8 release.
|
||||
CFLAGS_EXTRAS := -fno-exceptions
|
||||
|
||||
USE_LIBCPP := 1
|
||||
include Makefile.rules
|
|
@ -1,80 +0,0 @@
|
|||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
|
||||
using std::string;
|
||||
|
||||
#define intstr_map std::unordered_map<int, string>
|
||||
#define intstr_mmap std::unordered_multimap<int, string>
|
||||
|
||||
#define int_set std::unordered_set<int>
|
||||
#define str_set std::unordered_set<string>
|
||||
#define int_mset std::unordered_multiset<int>
|
||||
#define str_mset std::unordered_multiset<string>
|
||||
|
||||
int g_the_foo = 0;
|
||||
|
||||
int thefoo_rw(int arg = 1)
|
||||
{
|
||||
if (arg < 0)
|
||||
arg = 0;
|
||||
if (!arg)
|
||||
arg = 1;
|
||||
g_the_foo += arg;
|
||||
return g_the_foo;
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
intstr_map map;
|
||||
map.emplace(1,"hello");
|
||||
map.emplace(2,"world");
|
||||
map.emplace(3,"this");
|
||||
map.emplace(4,"is");
|
||||
map.emplace(5,"me");
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
intstr_mmap mmap;
|
||||
mmap.emplace(1,"hello");
|
||||
mmap.emplace(2,"hello");
|
||||
mmap.emplace(2,"world");
|
||||
mmap.emplace(3,"this");
|
||||
mmap.emplace(3,"this");
|
||||
mmap.emplace(3,"this");
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
int_set iset;
|
||||
iset.emplace(1);
|
||||
iset.emplace(2);
|
||||
iset.emplace(3);
|
||||
iset.emplace(4);
|
||||
iset.emplace(5);
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
str_set sset;
|
||||
sset.emplace("hello");
|
||||
sset.emplace("world");
|
||||
sset.emplace("this");
|
||||
sset.emplace("is");
|
||||
sset.emplace("me");
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
int_mset imset;
|
||||
imset.emplace(1);
|
||||
imset.emplace(2);
|
||||
imset.emplace(2);
|
||||
imset.emplace(3);
|
||||
imset.emplace(3);
|
||||
imset.emplace(3);
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
str_mset smset;
|
||||
smset.emplace("hello");
|
||||
smset.emplace("world");
|
||||
smset.emplace("world");
|
||||
smset.emplace("is");
|
||||
smset.emplace("is");
|
||||
thefoo_rw(); // Set break point at this line.
|
||||
|
||||
return 0;
|
||||
}
|
Loading…
Reference in New Issue