Skip to content
14 changes: 14 additions & 0 deletions cross-project-tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,26 @@ add_llvm_executable(check-gdb-llvm-support
)
target_link_libraries(check-gdb-llvm-support PRIVATE LLVMSupport)

add_executable(check-lldb-llvm-support-arrayref
debuginfo-tests/llvm-prettyprinters/lldb/arrayref.cpp
)
target_link_libraries(check-lldb-llvm-support-arrayref PRIVATE LLVMSupport)
target_compile_options(check-lldb-llvm-support-arrayref PRIVATE -g -O0)

add_executable(check-lldb-llvm-support-pointer-int-pair
debuginfo-tests/llvm-prettyprinters/lldb/pointer-int-pair.cpp
)
target_link_libraries(check-lldb-llvm-support-pointer-int-pair PRIVATE LLVMSupport)
target_compile_options(check-lldb-llvm-support-pointer-int-pair PRIVATE -g -O0)

set(CROSS_PROJECT_TESTS_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
set(CROSS_PROJECT_TESTS_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR})

set(CROSS_PROJECT_TEST_DEPS
FileCheck
check-gdb-llvm-support
check-lldb-llvm-support-arrayref
check-lldb-llvm-support-pointer-int-pair
count
llvm-ar
llvm-config
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#include "llvm/ADT/ArrayRef.h"

int Array[] = {1, 2, 3};

llvm::ArrayRef<int> ArrayRef(Array);
llvm::MutableArrayRef<int> MutableArrayRef(Array);

int main() { return 0; }
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# RUN: split-file %s %t
# RUN: lldb -b -x -o 'command script import %llvm_src_root/utils/lldbDataFormatters.py' -s %t/commands.input %llvm_tools_dir/check-lldb-llvm-support-arrayref | FileCheck %t/checks

#--- commands.input
b main
run
p ArrayRef
p MutableArrayRef

#--- checks
# CHECK: (lldb) p ArrayRef
# CHECK-NEXT: (llvm::ArrayRef<int>) size=3 {
# CHECK-NEXT: [0] = 1
# CHECK-NEXT: [1] = 2
# CHECK-NEXT: [2] = 3
# CHECK-NEXT: }

# CHECK: (lldb) p MutableArrayRef
# CHECK-NEXT: (llvm::MutableArrayRef<int>) {
# CHECK-NEXT: llvm::ArrayRef<int> = size=3 {
# CHECK-NEXT: [0] = 1
# CHECK-NEXT: [1] = 2
# CHECK-NEXT: [2] = 3
# CHECK-NEXT: }
# CHECK-NEXT: }
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import lit.util

if "native" not in config.available_features or lit.util.which("lldb") is None:
config.unsupported = True

config.suffixes = [".test"]
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
#include "llvm/ADT/PointerIntPair.h"

int main() {
float a = 5;
llvm::PointerIntPair<float *, 1, bool> float_pair(&a, true);
llvm::PointerIntPair<void *, 1, bool> void_pair(&a, false);
llvm::PointerIntPair<llvm::PointerIntPair<void *, 1, bool>, 1, bool> nested(
void_pair, true);

struct S {
int i;
};
S s;

enum class E : unsigned {
Case1,
Case2,
Case3,
Case4,
};
llvm::PointerIntPair<S *, 2, E> enum_pair(&s, E::Case2);

S s2;

__builtin_debugtrap();

enum_pair.setPointerAndInt(&s2, E::Case3);

__builtin_debugtrap();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# RUN: split-file %s %t
# RUN: lldb -x \
# RUN: -o 'command script import %llvm_src_root/utils/lldbDataFormatters.py' \
# RUN: -s %t/commands.input %llvm_tools_dir/check-lldb-llvm-support-pointer-int-pair \
# RUN: -o quit \
# RUN: | FileCheck %t/checks

#--- commands.input
run

p &a
v -T float_pair
v -T nested
p &s
v -T enum_pair

continue

p &s2
p enum_pair
p enum_pair.Pointer
p enum_pair.Int

#--- checks
# CHECK: (lldb) p &a
# CHECK-NEXT: (float *) [[PTR_A:0x[0-9a-zA-Z]+]]

# CHECK: (lldb) v -T float_pair
# CHECK-NEXT: (llvm::PointerIntPair<float *, 1, bool>) float_pair = {
# CHECK-NEXT: (float *) Pointer = [[PTR_A]]
# CHECK-NEXT: (bool) Int = true
# CHECK-NEXT: }

# CHECK: (lldb) v -T nested
# CHECK-NEXT: (llvm::PointerIntPair<llvm::PointerIntPair<void *, 1, bool>, 1, bool>) nested = {
# CHECK-NEXT: (llvm::PointerIntPair<void *, 1, bool>) Pointer = {
# CHECK-NEXT: (void *) Pointer = [[PTR_A]]
# CHECK-NEXT: (bool) Int = false
# CHECK-NEXT: }
# CHECK-NEXT: Int = true
# CHECK-NEXT: }

# CHECK: (lldb) p &s
# CHECK-NEXT: (S *) [[PTR_S:0x[0-9a-zA-Z]+]]

# CHECK: (lldb) v -T enum_pair
# CHECK-NEXT: (llvm::PointerIntPair<S *, 2, E>) enum_pair = {
# CHECK-NEXT: (S *) Pointer = [[PTR_S]]
# CHECK-NEXT: (E) Int = Case2
# CHECK-NEXT: }

# CHECK: (lldb) continue

# CHECK: (lldb) p &s2
# CHECK-NEXT: (S *) [[PTR_S2:0x[0-9a-zA-Z]+]]

# CHECK: (lldb) p enum_pair
# CHECK-NEXT: (llvm::PointerIntPair<S *, 2, E>) {
# CHECK-NEXT: Pointer = [[PTR_S2]]
# CHECK-NEXT: Int = Case3
# CHECK-NEXT: }

# CHECK: (lldb) p enum_pair.Pointer
# CHECK-NEXT: (S *) [[PTR_S2]]

# CHECK: (lldb) p enum_pair.Int
# CHECK-NEXT: (E) Case3
155 changes: 71 additions & 84 deletions llvm/utils/lldbDataFormatters.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,21 +57,11 @@ def __lldb_init_module(debugger, internal_dict):
f"-F {__name__}.ConstStringSummaryProvider "
"lldb_private::ConstString"
)

# The synthetic providers for PointerIntPair and PointerUnion are disabled
# because of a few issues. One example is template arguments that are
# non-pointer types that instead specialize PointerLikeTypeTraits.
# debugger.HandleCommand(
# "type synthetic add -w llvm "
# f"-l {__name__}.PointerIntPairSynthProvider "
# '-x "^llvm::PointerIntPair<.+>$"'
# )
# debugger.HandleCommand(
# "type synthetic add -w llvm "
# f"-l {__name__}.PointerUnionSynthProvider "
# '-x "^llvm::PointerUnion<.+>$"'
# )

debugger.HandleCommand(
"type synthetic add -w llvm "
f"-l {__name__}.PointerIntPairSynthProvider "
'-x "^llvm::PointerIntPair<.+>$"'
)
debugger.HandleCommand(
"type summary add -w llvm "
f"-e -F {__name__}.DenseMapSummary "
Expand Down Expand Up @@ -217,13 +207,6 @@ def ConstStringSummaryProvider(valobj, internal_dict):
return ""


def get_expression_path(val):
stream = lldb.SBStream()
if not val.GetExpressionPath(stream):
return None
return stream.GetData()


class PointerIntPairSynthProvider:
def __init__(self, valobj, internal_dict):
self.valobj = valobj
Expand All @@ -239,81 +222,85 @@ def get_child_index(self, name):
return 1
return None

def get_child_at_index(self, index):
expr_path = get_expression_path(self.valobj)
if index == 0:
return self.valobj.CreateValueFromExpression(
"Pointer", f"({self.pointer_ty.name}){expr_path}.getPointer()"
)
if index == 1:
return self.valobj.CreateValueFromExpression(
"Int", f"({self.int_ty.name}){expr_path}.getInt()"
)
return None

def update(self):
self.pointer_ty = self.valobj.GetType().GetTemplateArgumentType(0)
self.int_ty = self.valobj.GetType().GetTemplateArgumentType(2)

def _is_valid(self) -> bool:
return (
self.value
and self.pointer_ty
and self.int_ty
and self.pointer_bit_mask
and self.int_shift
and self.int_mask
)

def parse_template_parameters(typename):
"""
LLDB doesn't support template parameter packs, so let's parse them manually.
"""
result = []
start = typename.find("<")
end = typename.rfind(">")
if start < 1 or end < 2 or end - start < 2:
return result
def _get_pointer(self):
data: SBData = self.value.GetData()
error = lldb.SBError()
raw_bytes = data.ReadRawData(error, 0, self.ptr_size)
if error.Fail():
return None

nesting_level = 0
current_parameter_start = start + 1
unmasked_pointer = int.from_bytes(raw_bytes, self.byteorder)
return unmasked_pointer & self.pointer_bit_mask.GetValueAsUnsigned()

for i in range(start + 1, end + 1):
c = typename[i]
if c == "<":
nesting_level += 1
elif c == ">":
nesting_level -= 1
elif c == "," and nesting_level == 0:
result.append(typename[current_parameter_start:i].strip())
current_parameter_start = i + 1
def _get_int(self):
data: SBData = self.value.GetData()
error = lldb.SBError()
raw_bytes = data.ReadRawData(error, 0, self.ptr_size)
if error.Fail():
return None

result.append(typename[current_parameter_start:i].strip())
unmasked_pointer = int.from_bytes(raw_bytes, self.byteorder)
return (
unmasked_pointer >> self.int_shift.GetValueAsUnsigned()
) & self.int_mask.GetValueAsUnsigned()

return result
def get_child_at_index(self, index):
if not self._is_valid():
return None

if index == 0:
pointer_value = self._get_pointer()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason why call to _get_pointer() is not cached, where the cached value would be invalidated by update()? Maybe even call _get_pointer() and _get_int() from update() preemptively.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yea can move most of that logic out

if pointer_value is None:
return None

class PointerUnionSynthProvider:
def __init__(self, valobj, internal_dict):
self.valobj = valobj
self.update()
data = lldb.SBData()
data.SetDataFromUInt64Array([pointer_value])
return self.valobj.CreateValueFromData("Pointer", data, self.pointer_ty)
if index == 1:
int_value = self._get_int()
if int_value is None:
return None

def num_children(self):
return 1
data = lldb.SBData()
data.SetDataFromUInt64Array([int_value])
return self.valobj.CreateValueFromData("Int", data, self.int_ty)

def get_child_index(self, name):
if name == "Ptr":
return 0
return None

def get_child_at_index(self, index):
if index != 0:
return None
ptr_type_name = self.template_args[self.active_type_tag]
return self.valobj.CreateValueFromExpression(
"Ptr", f"({ptr_type_name}){self.val_expr_path}.getPointer()"
)

def update(self):
self.pointer_int_pair = self.valobj.GetChildMemberWithName("Val")
self.val_expr_path = get_expression_path(
self.valobj.GetChildMemberWithName("Val")
self.byteorder = (
"big"
if self.valobj.target.GetByteOrder() == lldb.eByteOrderBig
else "little"
)
self.ptr_size = self.valobj.target.GetAddressByteSize()
self.value: SBValue = self.valobj.GetChildMemberWithName("Value")
self.pointer_ty: SBType = self.valobj.GetType().GetTemplateArgumentType(0)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we assert that template argument 0 has a particular name (PointerType in this case)? Here and below every time we use index to get something.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't do that for the template parameters because we're getting the type not a structured template parameter object. So in this case pointer_ty.name would be something like int *. I think the test coverage should give us signal when this breaks.

But I'll add the asserts to the enum retrieval

self.int_ty: SBType = self.valobj.GetType().GetTemplateArgumentType(2)

pointer_info = self.valobj.GetType().GetTemplateArgumentType(4)
mask_and_shift_constants = pointer_info.FindDirectNestedType(
"MaskAndShiftConstants"
).GetEnumMembers()
self.pointer_bit_mask: SBTypeEnumMember = (
mask_and_shift_constants.GetTypeEnumMemberAtIndex(0)
)
self.int_shift: SBTypeEnumMember = (
mask_and_shift_constants.GetTypeEnumMemberAtIndex(1)
)
self.int_mask: SBTypeEnumMember = (
mask_and_shift_constants.GetTypeEnumMemberAtIndex(2)
)
self.active_type_tag = self.valobj.CreateValueFromExpression(
"", f"(int){self.val_expr_path}.getInt()"
).GetValueAsSigned()
self.template_args = parse_template_parameters(self.valobj.GetType().name)


def DenseMapSummary(valobj: lldb.SBValue, _) -> str:
Expand Down