Skip to content

Commit

Permalink
[formatters] Add a libstdcpp formatter for for unordered_map, unorder…
Browse files Browse the repository at this point in the history
…ed_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
  • Loading branch information
danilashtefan authored and walter-erquinigo committed Nov 22, 2021
1 parent 2e6a0a8 commit fcd288b
Show file tree
Hide file tree
Showing 7 changed files with 174 additions and 94 deletions.
80 changes: 80 additions & 0 deletions lldb/examples/synthetic/gnu_libstdcpp.py
Expand Up @@ -9,6 +9,86 @@
# 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):
'''
Expand Down
9 changes: 9 additions & 0 deletions lldb/source/Plugins/Language/CPlusPlus/CPlusPlusLanguage.cpp
Expand Up @@ -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(
Expand Down Expand Up @@ -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(
Expand Down
@@ -0,0 +1,3 @@
CXX_SOURCES := main.cpp

include Makefile.rules
Expand Up @@ -9,18 +9,19 @@
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(
Expand Down Expand Up @@ -76,3 +77,11 @@ def look_for_content_and_continue(self, var_name, patterns):
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;
}

This file was deleted.

This file was deleted.

0 comments on commit fcd288b

Please sign in to comment.