Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/etc/lldb_batchmode/from_lldb.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,11 @@

HAS_FLOAT128: bool = getattr(lldb, "eBasicTypeFloat128", None) is not None


class FromLLDB(Exception):
pass


# We use the following lists to dynamically create the enums at run-time (they're used to print
# more meaningful error messages when basic_type and type_class don't match).
# It takes a few hundred microseconds at runtime to generate these lists, but it means we never have
Expand Down Expand Up @@ -178,7 +183,7 @@ def get_summary_or_value(valobj: lldb.SBValue) -> Optional[str]:

def field_from_lldb(field: lldb.SBTypeMember) -> Field:
if BLESS and not field.IsValid():
raise Exception("Cannot bless invalid SBTypeMember object")
raise FromLLDB("Cannot bless invalid SBTypeMember object")

return Field(field.GetName(), field.GetType().GetName(), field.GetOffsetInBytes())

Expand Down Expand Up @@ -222,7 +227,7 @@ def get_generics(ty: lldb.SBType, sbtarget: lldb.SBTarget) -> list[lldb.SBType]:

def type_from_lldb(ty: lldb.SBType, sbtarget: lldb.SBTarget) -> Type:
if BLESS and not ty.IsValid():
raise Exception("Cannot bless invalid SBType object")
raise FromLLDB("Cannot bless invalid SBType object")

generic_types = get_generics(ty, sbtarget)
generics = [g.GetName() for g in generic_types]
Expand All @@ -238,7 +243,7 @@ def type_from_lldb(ty: lldb.SBType, sbtarget: lldb.SBTarget) -> Type:

def child_from_lldb(child: lldb.SBValue) -> Child:
if BLESS and not child.IsValid():
raise Exception("Cannot bless invalid child")
raise FromLLDB("Cannot bless invalid child")

sbtype: lldb.SBType = child.GetType()

Expand All @@ -256,7 +261,7 @@ def child_from_lldb(child: lldb.SBValue) -> Child:

def variable_from_lldb(var: lldb.SBValue) -> Variable:
if BLESS and not var.IsValid():
raise Exception("Cannot bless invalid SBValue object")
raise FromLLDB("Cannot bless invalid SBValue object")

sbtype = var.GetType()
type_name = sbtype.GetName()
Expand Down Expand Up @@ -323,7 +328,7 @@ def bless_variable(
valobj = frame.FindVariable(var_name)
if not valobj.IsValid():
# FIXME (todo) error handling
raise Exception(f"<bless error: Cannot find variable {var_name}>")
raise FromLLDB(f"<bless error: Cannot find variable {var_name}>")

# HACK it's obviously not ideal to output empty breakpoints, but it will be somewhat rare for it
# to happen (you would need a breakpoint with repr -> breakpoint without repr -> breakpoint
Expand Down
38 changes: 32 additions & 6 deletions src/etc/lldb_providers.py
Original file line number Diff line number Diff line change
Expand Up @@ -196,18 +196,37 @@ def has_children(self) -> bool:
return False


MSVC_STR_NAMES: List[str] = [
"ref$<str$>",
"ref_mut$<str$>",
"ptr_const$<str$>",
"ptr_mut$<str$>",
]


def get_template_args(type_name: str) -> Generator[str, None, None]:
"""
Takes a type name `T<A, tuple$<B, C>, D>` and returns a list of its generic args
`["A", "tuple$<B, C>", "D"]`.

Always returns an empty generator for `&str`, `&mut str`, `*const str`, and `*mut str`

Strips off `enum2$<>` wrapper from enum types before checking for template args

String-based replacement for LLDB's `SBType.template_args`, as LLDB is currently unable to
populate this field for targets with PDB debug info. Also useful for manually altering the type
name of generics (e.g. `Vec<ref$<str$> >` -> `Vec<&str>`).

Each element of the returned list can be looked up for its `SBType` value via
`SBTarget.FindFirstType()`
"""
if type_name in MSVC_STR_NAMES:
return

if type_name.startswith(("enum2$<", "slice2$<")):
# remove the prefix and the trailing ">"
type_name = type_name.split("<", 1)[0][:-1].strip()

level = 0
start = 0
for i, c in enumerate(type_name):
Expand All @@ -224,7 +243,7 @@ def get_template_args(type_name: str) -> Generator[str, None, None]:
start = i + 1


MSVC_PTR_PREFIX: List[str] = ["ref$<", "ref_mut$<", "ptr_const$<", "ptr_mut$<"]
MSVC_PTR_PREFIX = ("ref$<", "ref_mut$<", "ptr_const$<", "ptr_mut$<")

PRIMITIVE_TYPES: Dict[str, int] = {
"u8": eBasicTypeUnsignedChar,
Expand Down Expand Up @@ -258,6 +277,14 @@ def resolve_msvc_template_arg(arg_name: str, target: SBTarget) -> SBType:
`base_type.GetArrayType()`, which bypass the PDB file and ask clang directly for the type node.
"""

result = target.FindFirstType(arg_name)

if result.IsValid():
return result

if arg_name in MSVC_STR_NAMES:
return target.FindFirstType(arg_name)

# As of LLDB 22, finding primitives based on `FindFirstType` with their rust name no longer
# works. Instead, we can look them up by their `eBasicType` equivalent. For usize and isize,
# we convert them to their bit-sized counterpart before the lookup
Expand All @@ -273,18 +300,17 @@ def resolve_msvc_template_arg(arg_name: str, target: SBTarget) -> SBType:

return target.GetBasicType(eBasicTypeFloat128)

result = target.FindFirstType(arg_name)

if result.IsValid():
return result

for prefix in MSVC_PTR_PREFIX:
if arg_name.startswith(prefix):
arg_name = arg_name[len(prefix) : -1].strip()

result = resolve_msvc_template_arg(arg_name, target)
return result.GetPointerType()

if arg_name.startswith("slice2$<"):
arg_name = arg_name[len("slice2$<") : -1].strip()
return resolve_msvc_template_arg(arg_name, target)

if arg_name.startswith("array$<"):
template_args = get_template_args(arg_name)

Expand Down
Loading
Loading