Skip to content

Commit 1414a5b

Browse files
committed
[lldb][DataFormatter] Fix libcxx std::deque formatter for references and pointers
(Addresses GH#62153) The `SBType` APIs to retrieve details about template arguments, such as `GetTemplateArgumentType` or `GetTemplateArgumentKind` don't "desugar" LValueReferences/RValueReferences or pointers. So when we try to format a `std::deque&`, the python call to `GetTemplateArgumentType` fails to get a type, leading to an `element_size` of `0` and a division-by-zero python exception (which gets caught by the summary provider silently). This leads to the contents of such `std::deque&` to be printed incorrectly. This patch dereferences the reference/pointer before calling into the above SBAPIs. **Testing** * Add API test Differential Revision: https://reviews.llvm.org/D148531
1 parent 7d4824b commit 1414a5b

File tree

4 files changed

+115
-1
lines changed

4 files changed

+115
-1
lines changed

lldb/examples/synthetic/libcxx.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -649,7 +649,12 @@ def __init__(self, valobj, d):
649649
def find_block_size(self):
650650
# in order to use the deque we must have the block size, or else
651651
# it's impossible to know what memory addresses are valid
652-
self.element_type = self.valobj.GetType().GetTemplateArgumentType(0)
652+
obj_type = self.valobj.GetType()
653+
if obj_type.IsReferenceType():
654+
obj_type = obj_type.GetDereferencedType()
655+
elif obj_type.IsPointerType():
656+
obj_type = obj_type.GetPointeeType()
657+
self.element_type = obj_type.GetTemplateArgumentType(0)
653658
self.element_size = self.element_type.GetByteSize()
654659
# The code says this, but there must be a better way:
655660
# template <class _Tp, class _Allocator>
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
USE_LIBCPP := 1
2+
CXX_SOURCES := main.cpp
3+
4+
include Makefile.rules
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
"""
2+
Test LLDB's data formatter for libcxx's std::deque.
3+
"""
4+
5+
import lldb
6+
from lldbsuite.test.decorators import *
7+
from lldbsuite.test.lldbtest import *
8+
from lldbsuite.test import lldbutil
9+
10+
class LibcxxDequeDataFormatterTestCase(TestBase):
11+
12+
def check_numbers(self, var_name):
13+
self.expect("frame variable " + var_name,
14+
substrs=[var_name + ' = size=7',
15+
'[0] = 1',
16+
'[1] = 12',
17+
'[2] = 123',
18+
'[3] = 1234',
19+
'[4] = 12345',
20+
'[5] = 123456',
21+
'[6] = 1234567',
22+
'}'])
23+
24+
self.expect_expr(var_name, result_summary="size=7", result_children=[
25+
ValueCheck(value="1"),
26+
ValueCheck(value="12"),
27+
ValueCheck(value="123"),
28+
ValueCheck(value="1234"),
29+
ValueCheck(value="12345"),
30+
ValueCheck(value="123456"),
31+
ValueCheck(value="1234567"),
32+
])
33+
34+
@add_test_categories(["libc++"])
35+
def test_with_run_command(self):
36+
"""Test basic formatting of std::deque"""
37+
self.build()
38+
(self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
39+
self, "break here", lldb.SBFileSpec("main.cpp", False))
40+
41+
self.expect("frame variable numbers",
42+
substrs=['numbers = size=0'])
43+
44+
lldbutil.continue_to_breakpoint(process, bkpt)
45+
46+
# first value added
47+
self.expect("frame variable numbers",
48+
substrs=['numbers = size=1',
49+
'[0] = 1',
50+
'}'])
51+
52+
# add remaining values
53+
lldbutil.continue_to_breakpoint(process, bkpt)
54+
55+
self.check_numbers("numbers")
56+
57+
# clear out the deque
58+
lldbutil.continue_to_breakpoint(process, bkpt)
59+
60+
self.expect("frame variable numbers",
61+
substrs=['numbers = size=0'])
62+
63+
@add_test_categories(["libc++"])
64+
def test_ref_and_ptr(self):
65+
"""Test formatting of std::deque& and std::deque*"""
66+
self.build()
67+
(self.target, process, thread, bkpt) = lldbutil.run_to_source_breakpoint(
68+
self, "stop here", lldb.SBFileSpec("main.cpp", False))
69+
70+
# The reference should display the same was as the value did
71+
self.check_numbers("ref")
72+
73+
# The pointer should just show the right number of elements:
74+
self.expect("frame variable ptr", substrs=['ptr =', ' size=7'])
75+
self.expect("expression ptr", substrs=['$', 'size=7'])
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
#include <cstdio>
2+
#include <deque>
3+
typedef std::deque<int> int_deq;
4+
5+
void by_ref_and_ptr(std::deque<int> &ref, std::deque<int> *ptr) {
6+
printf("stop here");
7+
return;
8+
}
9+
10+
int main() {
11+
int_deq numbers;
12+
printf("break here");
13+
14+
(numbers.push_back(1));
15+
printf("break here");
16+
17+
(numbers.push_back(12));
18+
(numbers.push_back(123));
19+
(numbers.push_back(1234));
20+
(numbers.push_back(12345));
21+
(numbers.push_back(123456));
22+
(numbers.push_back(1234567));
23+
by_ref_and_ptr(numbers, &numbers);
24+
printf("break here");
25+
26+
numbers.clear();
27+
printf("break here");
28+
29+
return 0;
30+
}

0 commit comments

Comments
 (0)