[formatters] Add a libstdcpp formatter for bitset and unify tests across stdlibs
This diff adds a data formatter for libstdcpp's bitset. Besides, it unifies the tests for bitset for libcxx and libstdcpp for maintainability. Reviewed By: wallace Differential Revision: https://reviews.llvm.org/D112180
This commit is contained in:
parent
acabad9ff6
commit
566bfbb740
|
@ -3,9 +3,9 @@ add_lldb_library(lldbPluginCPlusPlusLanguage PLUGIN
|
|||
CPlusPlusLanguage.cpp
|
||||
CPlusPlusNameParser.cpp
|
||||
CxxStringTypes.cpp
|
||||
GenericBitset.cpp
|
||||
LibCxx.cpp
|
||||
LibCxxAtomic.cpp
|
||||
LibCxxBitset.cpp
|
||||
LibCxxInitializerList.cpp
|
||||
LibCxxList.cpp
|
||||
LibCxxMap.cpp
|
||||
|
|
|
@ -895,6 +895,8 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
|
|||
SyntheticChildren::Flags stl_synth_flags;
|
||||
stl_synth_flags.SetCascades(true).SetSkipPointers(false).SetSkipReferences(
|
||||
false);
|
||||
SyntheticChildren::Flags stl_deref_flags = stl_synth_flags;
|
||||
stl_deref_flags.SetFrontEndWantsDereference();
|
||||
|
||||
cpp_category_sp->GetRegexTypeSyntheticsContainer()->Add(
|
||||
RegularExpression("^std::vector<.+>(( )?&)?$"),
|
||||
|
@ -913,6 +915,10 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
|
|||
"lldb.formatters.cpp.gnu_libstdcpp.StdListSynthProvider")));
|
||||
stl_summary_flags.SetDontShowChildren(false);
|
||||
stl_summary_flags.SetSkipPointers(true);
|
||||
cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
|
||||
RegularExpression("^std::bitset<.+>(( )?&)?$"),
|
||||
TypeSummaryImplSP(
|
||||
new StringSummaryFormat(stl_summary_flags, "size=${svar%#}")));
|
||||
cpp_category_sp->GetRegexTypeSummariesContainer()->Add(
|
||||
RegularExpression("^std::vector<.+>(( )?&)?$"),
|
||||
TypeSummaryImplSP(
|
||||
|
@ -959,6 +965,12 @@ static void LoadLibStdcppFormatters(lldb::TypeCategoryImplSP cpp_category_sp) {
|
|||
"std::tuple synthetic children", ConstString("^std::tuple<.+>(( )?&)?$"),
|
||||
stl_synth_flags, true);
|
||||
|
||||
AddCXXSynthetic(
|
||||
cpp_category_sp,
|
||||
lldb_private::formatters::LibStdcppBitsetSyntheticFrontEndCreator,
|
||||
"std::bitset synthetic child", ConstString("^std::bitset<.+>(( )?&)?$"),
|
||||
stl_deref_flags, true);
|
||||
|
||||
AddCXXSummary(cpp_category_sp,
|
||||
lldb_private::formatters::LibStdcppUniquePointerSummaryProvider,
|
||||
"libstdc++ std::unique_ptr summary provider",
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
//===-- LibCxxBitset.cpp --------------------------------------------------===//
|
||||
//===-- GenericBitset.cpp //-----------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
|
@ -7,6 +7,7 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
|
||||
#include "LibCxx.h"
|
||||
#include "LibStdcpp.h"
|
||||
#include "Plugins/TypeSystem/Clang/TypeSystemClang.h"
|
||||
#include "lldb/DataFormatters/FormattersHelpers.h"
|
||||
#include "lldb/Target/Target.h"
|
||||
|
@ -16,9 +17,15 @@ using namespace lldb_private;
|
|||
|
||||
namespace {
|
||||
|
||||
class BitsetFrontEnd : public SyntheticChildrenFrontEnd {
|
||||
/// This class can be used for handling bitsets from both libcxx and libstdcpp.
|
||||
class GenericBitsetFrontEnd : public SyntheticChildrenFrontEnd {
|
||||
public:
|
||||
BitsetFrontEnd(ValueObject &valobj);
|
||||
enum class StdLib {
|
||||
LibCxx,
|
||||
LibStdcpp,
|
||||
};
|
||||
|
||||
GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib);
|
||||
|
||||
size_t GetIndexOfChildWithName(ConstString name) override {
|
||||
return formatters::ExtractIndexFromString(name.GetCString());
|
||||
|
@ -30,6 +37,8 @@ public:
|
|||
ValueObjectSP GetChildAtIndex(size_t idx) override;
|
||||
|
||||
private:
|
||||
ConstString GetDataContainerMemberName();
|
||||
|
||||
// The lifetime of a ValueObject and all its derivative ValueObjects
|
||||
// (children, clones, etc.) is managed by a ClusterManager. These
|
||||
// objects are only destroyed when every shared pointer to any of them
|
||||
|
@ -38,15 +47,16 @@ private:
|
|||
// Value objects created from raw data (i.e. in a different cluster) must
|
||||
// be referenced via shared pointer to keep them alive, however.
|
||||
std::vector<ValueObjectSP> m_elements;
|
||||
ValueObject* m_first = nullptr;
|
||||
ValueObject *m_first = nullptr;
|
||||
CompilerType m_bool_type;
|
||||
ByteOrder m_byte_order = eByteOrderInvalid;
|
||||
uint8_t m_byte_size = 0;
|
||||
StdLib m_stdlib;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
BitsetFrontEnd::BitsetFrontEnd(ValueObject &valobj)
|
||||
: SyntheticChildrenFrontEnd(valobj) {
|
||||
GenericBitsetFrontEnd::GenericBitsetFrontEnd(ValueObject &valobj, StdLib stdlib)
|
||||
: SyntheticChildrenFrontEnd(valobj), m_stdlib(stdlib) {
|
||||
m_bool_type = valobj.GetCompilerType().GetBasicTypeFromAST(eBasicTypeBool);
|
||||
if (auto target_sp = m_backend.GetTargetSP()) {
|
||||
m_byte_order = target_sp->GetArchitecture().GetByteOrder();
|
||||
|
@ -55,7 +65,16 @@ BitsetFrontEnd::BitsetFrontEnd(ValueObject &valobj)
|
|||
}
|
||||
}
|
||||
|
||||
bool BitsetFrontEnd::Update() {
|
||||
ConstString GenericBitsetFrontEnd::GetDataContainerMemberName() {
|
||||
switch (m_stdlib) {
|
||||
case StdLib::LibCxx:
|
||||
return ConstString("__first_");
|
||||
case StdLib::LibStdcpp:
|
||||
return ConstString("_M_w");
|
||||
}
|
||||
}
|
||||
|
||||
bool GenericBitsetFrontEnd::Update() {
|
||||
m_elements.clear();
|
||||
m_first = nullptr;
|
||||
|
||||
|
@ -65,16 +84,17 @@ bool BitsetFrontEnd::Update() {
|
|||
size_t capping_size = target_sp->GetMaximumNumberOfChildrenToDisplay();
|
||||
|
||||
size_t size = 0;
|
||||
|
||||
if (auto arg = m_backend.GetCompilerType().GetIntegralTemplateArgument(0))
|
||||
size = arg->value.getLimitedValue(capping_size);
|
||||
|
||||
m_elements.assign(size, ValueObjectSP());
|
||||
|
||||
m_first = m_backend.GetChildMemberWithName(ConstString("__first_"), true).get();
|
||||
m_first = m_backend.GetChildMemberWithName(GetDataContainerMemberName(), true)
|
||||
.get();
|
||||
return false;
|
||||
}
|
||||
|
||||
ValueObjectSP BitsetFrontEnd::GetChildAtIndex(size_t idx) {
|
||||
ValueObjectSP GenericBitsetFrontEnd::GetChildAtIndex(size_t idx) {
|
||||
if (idx >= m_elements.size() || !m_first)
|
||||
return ValueObjectSP();
|
||||
|
||||
|
@ -112,9 +132,18 @@ ValueObjectSP BitsetFrontEnd::GetChildAtIndex(size_t idx) {
|
|||
return m_elements[idx];
|
||||
}
|
||||
|
||||
SyntheticChildrenFrontEnd *formatters::LibStdcppBitsetSyntheticFrontEndCreator(
|
||||
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
|
||||
if (valobj_sp)
|
||||
return new GenericBitsetFrontEnd(*valobj_sp,
|
||||
GenericBitsetFrontEnd::StdLib::LibStdcpp);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
SyntheticChildrenFrontEnd *formatters::LibcxxBitsetSyntheticFrontEndCreator(
|
||||
CXXSyntheticChildren *, lldb::ValueObjectSP valobj_sp) {
|
||||
if (valobj_sp)
|
||||
return new BitsetFrontEnd(*valobj_sp);
|
||||
return new GenericBitsetFrontEnd(*valobj_sp,
|
||||
GenericBitsetFrontEnd::StdLib::LibCxx);
|
||||
return nullptr;
|
||||
}
|
|
@ -41,6 +41,10 @@ SyntheticChildrenFrontEnd *
|
|||
LibStdcppTupleSyntheticFrontEndCreator(CXXSyntheticChildren *,
|
||||
lldb::ValueObjectSP);
|
||||
|
||||
SyntheticChildrenFrontEnd *
|
||||
LibStdcppBitsetSyntheticFrontEndCreator(CXXSyntheticChildren *,
|
||||
lldb::ValueObjectSP);
|
||||
|
||||
SyntheticChildrenFrontEnd *
|
||||
LibStdcppVectorIteratorSyntheticFrontEndCreator(CXXSyntheticChildren *,
|
||||
lldb::ValueObjectSP);
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
CXX_SOURCES := main.cpp
|
||||
|
||||
USE_LIBCPP := 1
|
||||
include Makefile.rules
|
|
@ -0,0 +1,93 @@
|
|||
"""
|
||||
Test lldb data formatter subsystem for bitset for libcxx and libstdcpp.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
USE_LIBSTDCPP = "USE_LIBSTDCPP"
|
||||
USE_LIBCPP = "USE_LIBCPP"
|
||||
VALUE = "VALUE"
|
||||
REFERENCE = "REFERENCE"
|
||||
POINTER = "POINTER"
|
||||
|
||||
class GenericBitsetDataFormatterTestCase(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
def setUp(self):
|
||||
TestBase.setUp(self)
|
||||
primes = [1]*300
|
||||
primes[0] = primes[1] = 0
|
||||
for i in range(2, len(primes)):
|
||||
for j in range(2*i, len(primes), i):
|
||||
primes[j] = 0
|
||||
self.primes = primes
|
||||
|
||||
def getBitsetVariant(self, size, variant):
|
||||
if variant == VALUE:
|
||||
return "std::bitset<" + str(size) + ">"
|
||||
elif variant == REFERENCE:
|
||||
return "std::bitset<" + str(size) + "> &"
|
||||
elif variant == POINTER:
|
||||
return "std::bitset<" + str(size) + "> *"
|
||||
return ""
|
||||
|
||||
def check(self, name, size, variant):
|
||||
var = self.frame().FindVariable(name)
|
||||
self.assertTrue(var.IsValid())
|
||||
self.assertEqual(var.GetNumChildren(), size)
|
||||
children = []
|
||||
for i in range(size):
|
||||
child = var.GetChildAtIndex(i)
|
||||
children.append(ValueCheck(value=str(bool(child.GetValueAsUnsigned())).lower()))
|
||||
self.assertEqual(child.GetValueAsUnsigned(), self.primes[i],
|
||||
"variable: %s, index: %d"%(name, size))
|
||||
self.expect_var_path(name,type=self.getBitsetVariant(size,variant),children=children)
|
||||
|
||||
def do_test_value(self, stdlib_type):
|
||||
"""Test that std::bitset is displayed correctly"""
|
||||
self.build(dictionary={stdlib_type: "1"})
|
||||
|
||||
lldbutil.run_to_source_breakpoint(self, '// break here',
|
||||
lldb.SBFileSpec("main.cpp", False))
|
||||
|
||||
self.check("empty", 0, VALUE)
|
||||
self.check("small", 13, VALUE)
|
||||
self.check("large", 70, VALUE)
|
||||
|
||||
@add_test_categories(["libstdcxx"])
|
||||
def test_value_libstdcpp(self):
|
||||
self.do_test_value(USE_LIBSTDCPP)
|
||||
|
||||
@add_test_categories(["libc++"])
|
||||
def test_value_libcpp(self):
|
||||
self.do_test_value(USE_LIBCPP)
|
||||
|
||||
def do_test_ptr_and_ref(self, stdlib_type):
|
||||
"""Test that ref and ptr to std::bitset is displayed correctly"""
|
||||
self.build(dictionary={stdlib_type: "1"})
|
||||
|
||||
(_, process, _, bkpt) = lldbutil.run_to_source_breakpoint(self,
|
||||
'Check ref and ptr',
|
||||
lldb.SBFileSpec("main.cpp", False))
|
||||
|
||||
self.check("ref", 13, REFERENCE)
|
||||
self.check("ptr", 13, POINTER)
|
||||
|
||||
lldbutil.continue_to_breakpoint(process, bkpt)
|
||||
|
||||
self.check("ref", 70, REFERENCE)
|
||||
self.check("ptr", 70, POINTER)
|
||||
|
||||
@add_test_categories(["libstdcxx"])
|
||||
def test_ptr_and_ref_libstdcpp(self):
|
||||
self.do_test_ptr_and_ref(USE_LIBSTDCPP)
|
||||
|
||||
@add_test_categories(["libc++"])
|
||||
def test_ptr_and_ref_libcpp(self):
|
||||
self.do_test_ptr_and_ref(USE_LIBCPP)
|
|
@ -1,27 +1,26 @@
|
|||
#include <bitset>
|
||||
#include <stdio.h>
|
||||
|
||||
template<std::size_t N>
|
||||
void fill(std::bitset<N> &b) {
|
||||
template <std::size_t N> void fill(std::bitset<N> &b) {
|
||||
b.set();
|
||||
b[0] = b[1] = false;
|
||||
for (std::size_t i = 2; i < N; ++i) {
|
||||
for (std::size_t j = 2*i; j < N; j+=i)
|
||||
for (std::size_t j = 2 * i; j < N; j += i)
|
||||
b[j] = false;
|
||||
}
|
||||
}
|
||||
|
||||
template<std::size_t N>
|
||||
template <std::size_t N>
|
||||
void by_ref_and_ptr(std::bitset<N> &ref, std::bitset<N> *ptr) {
|
||||
// Check ref and ptr
|
||||
return;
|
||||
// Check ref and ptr
|
||||
return;
|
||||
}
|
||||
|
||||
int main() {
|
||||
std::bitset<0> empty;
|
||||
std::bitset<13> small;
|
||||
fill(small);
|
||||
std::bitset<200> large;
|
||||
std::bitset<70> large;
|
||||
fill(large);
|
||||
by_ref_and_ptr(small, &small); // break here
|
||||
by_ref_and_ptr(large, &large);
|
|
@ -1,61 +0,0 @@
|
|||
"""
|
||||
Test lldb data formatter subsystem.
|
||||
"""
|
||||
|
||||
|
||||
|
||||
import lldb
|
||||
from lldbsuite.test.decorators import *
|
||||
from lldbsuite.test.lldbtest import *
|
||||
from lldbsuite.test import lldbutil
|
||||
|
||||
|
||||
class TestDataFormatterLibcxxBitset(TestBase):
|
||||
|
||||
mydir = TestBase.compute_mydir(__file__)
|
||||
|
||||
def setUp(self):
|
||||
TestBase.setUp(self)
|
||||
|
||||
primes = [1]*300
|
||||
primes[0] = primes[1] = 0
|
||||
for i in range(2, len(primes)):
|
||||
for j in range(2*i, len(primes), i):
|
||||
primes[j] = 0
|
||||
self.primes = primes
|
||||
|
||||
def check(self, name, size):
|
||||
var = self.frame().FindVariable(name)
|
||||
self.assertTrue(var.IsValid())
|
||||
self.assertEqual(var.GetNumChildren(), size)
|
||||
for i in range(size):
|
||||
child = var.GetChildAtIndex(i)
|
||||
self.assertEqual(child.GetValueAsUnsigned(), self.primes[i],
|
||||
"variable: %s, index: %d"%(name, size))
|
||||
|
||||
@add_test_categories(["libc++"])
|
||||
def test_value(self):
|
||||
"""Test that std::bitset is displayed correctly"""
|
||||
self.build()
|
||||
lldbutil.run_to_source_breakpoint(self, '// break here',
|
||||
lldb.SBFileSpec("main.cpp", False))
|
||||
|
||||
self.check("empty", 0)
|
||||
self.check("small", 13)
|
||||
self.check("large", 200)
|
||||
|
||||
@add_test_categories(["libc++"])
|
||||
def test_ptr_and_ref(self):
|
||||
"""Test that ref and ptr to std::bitset is displayed correctly"""
|
||||
self.build()
|
||||
(_, process, _, bkpt) = lldbutil.run_to_source_breakpoint(self,
|
||||
'Check ref and ptr',
|
||||
lldb.SBFileSpec("main.cpp", False))
|
||||
|
||||
self.check("ref", 13)
|
||||
self.check("ptr", 13)
|
||||
|
||||
lldbutil.continue_to_breakpoint(process, bkpt)
|
||||
|
||||
self.check("ref", 200)
|
||||
self.check("ptr", 200)
|
Loading…
Reference in New Issue