Skip to content

Commit a2831f7

Browse files
committed
add msvc enum providers
1 parent 1e3b45d commit a2831f7

File tree

2 files changed

+179
-2
lines changed

2 files changed

+179
-2
lines changed

src/etc/lldb_commands

+4-2
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,10 @@ type synthetic add -l lldb_lookup.synthetic_lookup -x "^(core::([a-z_]+::)+)NonZ
1818
type synthetic add -l lldb_lookup.synthetic_lookup -x "^core::num::([a-z_]+::)*NonZero.+$" --category Rust
1919
type synthetic add -l lldb_lookup.synthetic_lookup -x "^(std::([a-z_]+::)+)PathBuf$" --category Rust
2020
type synthetic add -l lldb_lookup.synthetic_lookup -x "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust
21+
type synthetic add -l lldb_lookup.MSVCEnumSyntheticProvider -x "^enum2\$<.+>$" --category Rust
22+
type synthetic add -l lldb_lookup.synthetic_lookup -x "^enum2\$<.+>::.*$" --category Rust
2123
type synthetic add -l lldb_lookup.MSVCStdSliceSyntheticProvider -x "^ref\$<slice2\$<.+> >" --category Rust
2224
type synthetic add -l lldb_lookup.MSVCTupleSyntheticProvider -x "^tuple\$<.+>$" --category Rust
23-
type summary add -F lldb_lookup.TupleSummaryProvider -e -x -h "^tuple\$<.+>$" --category Rust
2425
type synthetic add -l lldb_lookup.synthetic_lookup -x "^\(.*\)$" --category Rust
2526
type summary add -F _ -e -x -h "^.*$" --category Rust
2627
type summary add -F lldb_lookup.summary_lookup -e -x -h "^(alloc::([a-z_]+::)+)String$" --category Rust
@@ -45,5 +46,6 @@ type summary add -F lldb_lookup.summary_lookup -e -x -h "^(std::([a-z_]+::)+)Pa
4546
type summary add -F lldb_lookup.summary_lookup -e -x -h "^&(mut )?(std::([a-z_]+::)+)Path$" --category Rust
4647
type summary add -F lldb_lookup.TupleSummaryProvider -e -x -h "^tuple\$<.+>$" --category Rust
4748
type summary add -F lldb_lookup.StdSliceSummaryProvider -e -x -h "^ref(_mut)?\$<slice2\$<.+> >" --category Rust
48-
type synthetic add -l lldb_lookup.MSVCStdSliceSyntheticProvider -x "^ref(_mut)?\$<slice2\$<.+> >" --category Rust
49+
type summary add -F lldb_lookup.MSVCEnumSummaryProvider -e -x -h "^enum2\$<.+>$" --category Rust
50+
type summary add -F lldb_lookup.summary_lookup -e -x -h "^enum2\$<.+>::.*$" --category Rust
4951
type category enable Rust

src/etc/lldb_providers.py

+175
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@
33
from lldb import (
44
SBData,
55
SBError,
6+
SBType,
7+
SBTypeStaticField,
68
SBValue,
79
eBasicTypeLong,
810
eBasicTypeUnsignedLong,
@@ -325,6 +327,179 @@ def _getCurrentVariantIndex(self, all_variants: SBValue) -> int:
325327
default_index = i
326328
return default_index
327329

330+
class MSVCEnumSyntheticProvider:
331+
"""
332+
Synthetic provider for sum-type enums on MSVC. For a detailed explanation of the internals,
333+
see:
334+
335+
https://github.com/rust-lang/rust/blob/master/compiler/rustc_codegen_llvm/src/debuginfo/metadata/enums/cpp_like.rs
336+
"""
337+
338+
__slots__ = ["valobj", "variant", "value"]
339+
def __init__(self, valobj: SBValue, _dict: LLDBOpaque):
340+
self.valobj = valobj
341+
self.variant: SBValue
342+
self.value: SBValue
343+
self.update()
344+
345+
def update(self):
346+
tag: SBValue = self.valobj.GetChildMemberWithName("tag")
347+
348+
if tag.IsValid():
349+
tag: int = tag.GetValueAsUnsigned()
350+
for child in self.valobj.GetNonSyntheticValue().children:
351+
if not child.name.startswith("variant"):
352+
continue
353+
354+
variant_type: SBType = child.GetType()
355+
exact: SBTypeStaticField = variant_type.GetStaticFieldWithName(
356+
"DISCR_EXACT"
357+
)
358+
359+
if exact.IsValid():
360+
discr: int = exact.GetConstantValue(
361+
self.valobj.target
362+
).GetValueAsUnsigned()
363+
if tag == discr:
364+
self.variant = child
365+
self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
366+
return
367+
else: # if invalid, DISCR must be a range
368+
begin: int = variant_type.GetStaticFieldWithName(
369+
"DISCR_BEGIN"
370+
).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
371+
end: int = variant_type.GetStaticFieldWithName(
372+
"DISCR_END"
373+
).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
374+
375+
# begin isn't necessarily smaller than end, so we must test for both cases
376+
if begin < end:
377+
if begin <= tag <= end:
378+
self.variant = child
379+
self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
380+
return
381+
else:
382+
if tag >= begin or tag <= end:
383+
self.variant = child
384+
self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
385+
return
386+
else: # if invalid, tag is a 128 bit value
387+
tag_lo: int = self.valobj.GetChildMemberWithName("tag128_lo").GetValueAsUnsigned()
388+
tag_hi: int = self.valobj.GetChildMemberWithName("tag128_hi").GetValueAsUnsigned()
389+
390+
tag: int = (tag_hi << 64) | tag_lo
391+
392+
for child in self.valobj.GetNonSyntheticValue().children:
393+
if not child.name.startswith("variant"):
394+
continue
395+
396+
variant_type: SBType = child.GetType()
397+
exact_lo: SBTypeStaticField = variant_type.GetStaticFieldWithName(
398+
"DISCR128_EXACT_LO"
399+
)
400+
401+
if exact_lo.IsValid():
402+
exact_lo: int = exact_lo.GetConstantValue(self.valobj.target).GetValueAsUnsigned()
403+
exact_hi: int = variant_type.GetStaticFieldWithName(
404+
"DISCR128_EXACT_HI"
405+
).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
406+
407+
discr: int = (exact_hi << 64) | exact_lo
408+
if tag == discr:
409+
self.variant = child
410+
self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
411+
return
412+
else: # if invalid, DISCR must be a range
413+
begin_lo: int = variant_type.GetStaticFieldWithName(
414+
"DISCR128_BEGIN_LO"
415+
).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
416+
begin_hi: int = variant_type.GetStaticFieldWithName(
417+
"DISCR128_BEGIN_HI"
418+
).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
419+
420+
end_lo: int = variant_type.GetStaticFieldWithName(
421+
"DISCR128_END_LO"
422+
).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
423+
end_hi: int = variant_type.GetStaticFieldWithName(
424+
"DISCR128_END_HI"
425+
).GetConstantValue(self.valobj.target).GetValueAsUnsigned()
426+
427+
begin = (begin_hi << 64) | begin_lo
428+
end = (end_hi << 64) | end_lo
429+
430+
# begin isn't necessarily smaller than end, so we must test for both cases
431+
if begin < end:
432+
if begin <= tag <= end:
433+
self.variant = child
434+
self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
435+
return
436+
else:
437+
if tag >= begin or tag <= end:
438+
self.variant = child
439+
self.value = child.GetChildMemberWithName("value").GetSyntheticValue()
440+
return
441+
442+
def num_children(self) -> int:
443+
return self.value.GetNumChildren()
444+
445+
def get_child_index(self, name: str) -> int:
446+
return self.value.GetIndexOfChildWithName(name)
447+
448+
def get_child_at_index(self, index: int) -> SBValue:
449+
return self.value.GetChildAtIndex(index)
450+
451+
def has_children(self) -> bool:
452+
return self.value.MightHaveChildren()
453+
454+
def get_type_name(self) -> str:
455+
name = self.valobj.GetTypeName()
456+
# remove "enum2$<", str.removeprefix() is python 3.9+
457+
name = name[7:]
458+
459+
# MSVC misinterprets ">>" as a shift operator, so spaces are inserted by rust to
460+
# avoid that
461+
if name.endswith(" >"):
462+
name = name[:-2]
463+
elif name.endswith(">"):
464+
name = name[:-1]
465+
466+
return name
467+
468+
469+
def MSVCEnumSummaryProvider(valobj: SBValue, _dict: LLDBOpaque) -> str:
470+
enum_synth = MSVCEnumSyntheticProvider(valobj.GetNonSyntheticValue(), _dict)
471+
variant_names: SBType = valobj.target.FindFirstType(
472+
f"{enum_synth.valobj.GetTypeName()}::VariantNames"
473+
)
474+
name_idx = (
475+
enum_synth.variant.GetType()
476+
.GetStaticFieldWithName("NAME")
477+
.GetConstantValue(valobj.target)
478+
.GetValueAsUnsigned()
479+
)
480+
481+
name: str = variant_names.enum_members[name_idx].name
482+
483+
if enum_synth.num_children() == 0:
484+
return name
485+
486+
child_name: str = enum_synth.value.GetChildAtIndex(0).name
487+
if child_name == "0" or child_name == "__0":
488+
# enum variant is a tuple struct
489+
return name + TupleSummaryProvider(enum_synth.value, _dict)
490+
else:
491+
# enum variant is a regular struct
492+
var_list = (
493+
str(enum_synth.value.GetNonSyntheticValue()).split("= ", 1)[1].splitlines()
494+
)
495+
vars = [x.strip() for x in var_list if x not in ("{", "}")]
496+
if vars[0][0] == "(":
497+
vars[0] = vars[0][1:]
498+
if vars[-1][-1] == ")":
499+
vars[-1] = vars[-1][:-1]
500+
501+
return f'{name}{{{", ".join(vars)}}}'
502+
328503

329504
class TupleSyntheticProvider:
330505
"""Pretty-printer for tuples and tuple enum variants"""

0 commit comments

Comments
 (0)