[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:
Danil Stefaniuc 2021-11-22 12:54:28 -08:00 committed by Walter Erquinigo
parent 2e6a0a8b81
commit fcd288b52a
7 changed files with 174 additions and 94 deletions

View File

@ -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):
'''

View File

@ -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(

View File

@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp
include Makefile.rules

View File

@ -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)

View File

@ -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;
}

View File

@ -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

View File

@ -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;
}