Debugger: fix pmr containers test

The initial version of the pmr containers dumper test
had a couple of shortcomings:
  * there was an inclusion of `<memory_resource>` missing
    in the test program body
  * some test setups use compilers that do not use C++17 by default
    so the correct profile must be specified for the test

Disabled a bunch of checks that did not work with libcxx
due to incorrect reported size of
`std::pmr::polymorphic_allocator<std::pair<const int, int>>`.

Fixed the libcxx dumper for `std::map` (the ordinary, non-pmr, one).
As turned out, it was broken due to an incorrect padding calculation
for `std::pair<const int, int>`.
This has been worked around by using pair's inner types explicitly.

Amends: cb23332ae5

Task number: QTCREATORBUG-30224

Change-Id: I937295f88083ee9521a35ac7dbe22303ef52bd53
Reviewed-by: hjk <hjk@qt.io>
This commit is contained in:
Andrii Semkiv
2024-11-21 13:40:48 +01:00
parent 017132a959
commit 6be39b39e6
2 changed files with 53 additions and 36 deletions

View File

@@ -1,6 +1,29 @@
# Copyright (C) 2022 The Qt Company Ltd.
# SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
# Disclaimers:
# 1. Looking up the allocator template type is potentially an expensive operation.
# A better alternative would be finding out the allocator type
# through accessing a class member.
# However due to different implementations doing things very differently
# it is deemed acceptable.
# Specifically:
# * GCC's `_Rb_tree_impl` basically inherits from the allocator, the comparator
# and the sentinel node
# * Clang packs the allocator and the sentinel node into a compressed pair,
# so depending on whether the allocator is sized or not,
# there may or may not be a member to access
# * MSVC goes even one step further and stores the allocator and the sentinel node together
# in a compressed pair, which in turn is stored together with the comparator inside
# another compressed pair
# 2. `size` on an empty type, which the majority of the allocators are of,
# for whatever reason reports 1. In theory there can be allocators whose type is truly 1 byte,
# in which case we will have issues, but in practice they should be rather rare.
# 3. Note that sometimes the size of `std::pmr::polymorphic_allocator` is bizarrely reported
# as exactly 0, for example this happens with
# `std::__1::pmr::polymorphic_allocator<std::__1::pair<const int, int>>` from `std::pmr::map`,
# so dumping pmr containers still may still have some breakages for libcxx
from stdtypes import qdump__std__array, qdump__std__complex, qdump__std__once_flag, qdump__std__unique_ptr, qdumpHelper__std__deque__libcxx, qdumpHelper__std__vector__libcxx, qdump__std__forward_list
from utils import DisplayFormat
from dumper import Children, DumperBase
@@ -53,24 +76,10 @@ def qdump__std____1__list(d, value):
def qdump__std____1__set(d, value):
# Looking up the allocator template type is potentially an expensive operation.
# A better alternative would be finding out the allocator type through accessing a class member.
# However due to different implementations doing things very differently
# it is deemed acceptable.
# Specifically:
# * GCC's `_Rb_tree_impl` basically inherits from the allocator, the comparator
# and the sentinel node
# * Clang packs the allocator and the sentinel node into a compressed pair,
# so depending on whether the allocator is sized or not,
# there may or may not be a member to access
# * MSVC goes even one step further and stores the allocator and the sentinel node together
# in a compressed pair, which in turn is stored together with the comparator inside
# another compressed pair
# see disclaimer #1
alloc_type = value.type[2]
alloc_size = alloc_type.size()
# size of empty allocators (which are the majority) is reported as 1
# in theory there can be allocators whose size is truly 1 byte,
# in which case this will cause issues, but in practice they should be rather rare
# see disclaimer #2
if alloc_size > 1:
(proxy, head, alloc, size) = value.split(f'pp{{{alloc_type.name}}}p')
else:
@@ -109,30 +118,30 @@ def qform__std____1__map():
def qdump__std____1__map(d, value):
try:
(proxy, head, size) = value.split("ppp")
d.check(0 <= size and size <= 100 * 1000 * 1000)
alloc_type = value.type[3] # see disclaimer #1
alloc_size = alloc_type.size()
# see disclaimers #2 and #3
if alloc_size > 1:
(begin_node_ptr, head, alloc, size) = value.split(f'pp{{{alloc_type.name}}}p')
else:
(begin_node_ptr, head, size) = value.split("ppp")
# Sometimes there is extra data at the front. Don't know why at the moment.
except RuntimeError:
(junk, proxy, head, size) = value.split("pppp")
d.check(0 <= size and size <= 100 * 1000 * 1000)
d.putItemCount(size)
if d.isExpanded():
keyType = value.type[0]
valueType = value.type[1]
pairType = value.type[3][0]
valType = value.type[1]
def in_order_traversal(node):
(left, right, parent, color, pad, pair) = d.split("pppB@{%s}" % (pairType.name), node)
(left, right, parent, color, _pad_1, key, _pad_2, val) = d.split(
f'pppB@{{{keyType.name}}}@{{{valType.name}}}', node)
if left:
for res in in_order_traversal(left):
yield res
yield pair.split("{%s}@{%s}" % (keyType.name, valueType.name))[::2]
yield key, val
if right:
for res in in_order_traversal(right):

View File

@@ -8693,6 +8693,7 @@ void tst_Dumpers::dumper_data()
#include <forward_list>
#include <list>
#include <map>
#include <memory_resource>
#include <set>
#include <unordered_map>
#include <unordered_set>
@@ -8731,16 +8732,17 @@ void tst_Dumpers::dumper_data()
us.insert(i);
ums.insert(i);
m.emplace(i, i);
mm.emplace(i, i);
um.emplace(i, i);
umm.emplace(i, i);
m.emplace(i, count + i);
mm.emplace(i, count + i);
um.emplace(i, count + i);
umm.emplace(i, count + i);
v.push_back(i);
}
)",
"&d, &fl, &l, &s, &ms, &us, &ums, &m, &mm, &um, &umm, &v"
}
+ Cxx17Profile{}
+ Check{"d", "<10 items>", "std::pmr::deque<int>"} % NoGdbEngine
+ Check{"d", "<10 items>", "std::pmr::deque"} % GdbEngine
+ Check{"d.1", "[1]", "8", "int"}
@@ -8760,12 +8762,18 @@ void tst_Dumpers::dumper_data()
+ Check{"us", "<10 items>", "std::pmr::unordered_set"} % GdbEngine
+ Check{"ums", "<10 items>", "std::pmr::unordered_multiset<int>"} % NoGdbEngine
+ Check{"ums", "<10 items>", "std::pmr::unordered_multiset"} % GdbEngine
+ Check{"m", "<10 items>", "std::pmr::map<int, int>"} % NoGdbEngine
// There is a bizzare interaction of `DumperBase.Type.size` (see Python scripts) and libcxx
// that results in the size of
// `std::__1::pmr::polymorphic_allocator<std::__1::pair<const int, int>>`
// from `std::pmr::map` being reported as exactly 0 which breaks dumping
+ Check{"m", "<10 items>", "std::pmr::map<int, int>"} % CdbEngine
+ Check{"m", "<10 items>", "std::pmr::map"} % GdbEngine
+ Check{"m.5", "[5] 5", "5", ""}
+ Check{"mm", "<10 items>", "std::pmr::multimap<int, int>"} % NoGdbEngine
+ Check{"m.5", "[5] 5", "15", ""} % NoLldbEngine
+ Check{"mm", "<10 items>", "std::pmr::multimap<int, int>"} % CdbEngine
+ Check{"mm", "<10 items>", "std::pmr::multimap"} % GdbEngine
+ Check{"mm.5", "[5] 5", "5", ""}
+ Check{"mm.5", "[5] 5", "15", ""} % NoLldbEngine
+ Check{"um", "<10 items>", "std::pmr::unordered_map<int, int>"} % NoGdbEngine
+ Check{"um", "<10 items>", "std::pmr::unordered_map"} % GdbEngine
+ Check{"umm", "<10 items>", "std::pmr::unordered_multimap<int, int>"} % NoGdbEngine