-
Notifications
You must be signed in to change notification settings - Fork 18.2k
[llvm][formatters] Add LLDB data-formatter for llvm::PointerIntPair #173261
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
95dc640
32fb68b
841e2e5
dd56bd7
2f46d57
b710c51
14c99e1
9d3804d
d777b46
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| 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 |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 " | ||
|
|
@@ -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 | ||
|
|
@@ -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() | ||
| 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) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can we assert that template argument 0 has a particular name (
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 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: | ||
|
|
||
There was a problem hiding this comment.
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 byupdate()? Maybe even call_get_pointer()and_get_int()fromupdate()preemptively.There was a problem hiding this comment.
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