import lldb, signal, sys, time import lldb.formatters.Logger import codelldb.value ############################################################ # # Store pointers to some important types and globals. # ############################################################ _FNameEntryType = None _FNameEntryStride = None _FUObjectItemType = None _GNameBlocksDebug = None _GObjectArrayForDebugVisualizers = None def _init_globals(target): global _FNameEntryType, _FNameEntryStride, _FUObjectItemType global _GNameBlocksDebug, _GObjectArrayForDebugVisualizers if _FNameEntryType is None: t = target.FindFirstType('FNameEntry') if t.IsValid(): _FNameEntryType = t _FNameEntryStride = t.GetByteAlign() if _FUObjectItemType is None: t = target.FindFirstType('FUObjectItem') if t.IsValid(): _FUObjectItemType = t if _GNameBlocksDebug is None: v = target.FindFirstGlobalVariable('GNameBlocksDebug') if v.IsValid(): _GNameBlocksDebug = v if _GObjectArrayForDebugVisualizers is None: v = target.FindFirstGlobalVariable('GObjectArrayForDebugVisualizers') if v.IsValid(): _GObjectArrayForDebugVisualizers = v class _GlobalsInitStopHook: def __init__(self, target, extra_args, internal_dict): _init_globals(target) def handle_stop(self, exe_ctx, stream): _init_globals(exe_ctx.GetTarget()) return True ############################################################ # # A forwarding synth provider is a provider that # forwards all child lookups to a different SBValue. # ############################################################ def TryGetSyntheticValue(v): s = v.GetSyntheticValue() return s if s.IsValid() else v class ForwardingSynthProvider: def __init__(self, valobj, dict): self.valobj = valobj self.update() def num_children(self): return self.forward_to.GetNumChildren() def get_child_index(self, name): return self.forward_to.GetIndexOfChildWithName(name) def get_child_at_index(self, index): return self.forward_to.GetChildAtIndex(index) def update(self): self.forward_to = lldb.SBValue() def has_children(self): return self.forward_to.IsValid() and self.forward_to.MightHaveChildren() ############################################################ # # A Stored synth provider is an synth provider that # just stores an array of children. # ############################################################ class StoredSynthProvider: def __init__(self, valobj, dict): self.valobj = valobj self.update() def num_children(self): return len(self.children) def get_child_index(self, name): for i, child in enumerate(self.children): if child.GetName() == name: return i return -1 def get_child_at_index(self, index): if 0 <= index < len(self.children): return self.children[index] return lldb.SBValue() def update(self): self.children = [] def has_children(self): return len(self.children) > 0 ############################################################ # # An Indexed synth provider exposes N integer-indexed # children [0]..[N-1]. Subclasses set self.count in update() # and override get_child_at_index(index) to return the SBValue # for a given index (with their own range check). # ############################################################ MAX_ARRAY_CHILDREN = 50 class IndexedSynthProvider: def __init__(self, valobj, dict): self.valobj = valobj self.update() def num_children(self): return min(self.count, MAX_ARRAY_CHILDREN) def get_child_index(self, name): try: return int(name.lstrip('[').rstrip(']')) except: return -1 def has_children(self): return self.count > 0 def update(self): self.count = 0 ############################################################ # # Provider for FString # ############################################################ def UEFStringSummaryProvider(valobj, dict): target = valobj.GetTarget() sbtype = valobj.GetType().GetUnqualifiedType() if sbtype.IsPointerType(): if valobj.GetValueAsUnsigned(0) == 0: return 'nullptr' valobj = valobj.Dereference() elif sbtype.IsReferenceType(): valobj = valobj.Dereference() tarray = valobj.GetChildMemberWithName('Data').GetNonSyntheticValue() array_num = tarray.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) if array_num < 0: return 'corrupted string' if array_num == 0: return 'empty string' data_ptr = tarray.GetChildMemberWithName('AllocatorInstance').GetChildMemberWithName('Data') buffer_addr = data_ptr.GetValueAsUnsigned(0) if buffer_addr == 0: return 'corrupted string' tchar_type = tarray.GetType().GetTemplateArgumentType(0) array_type = tchar_type.GetArrayType(array_num) sbaddr = lldb.SBAddress(buffer_addr, target) summary = target.CreateValueFromAddress('string', sbaddr, array_type).GetSummary() if summary and summary.startswith('u"'): return summary[1:] return summary ############################################################ # # Provider for FName # ############################################################ def UEFNameSummaryProvider(valobj, dict): valobj = valobj.GetNonSyntheticValue() target = valobj.GetTarget() process = target.GetProcess() entry_id = valobj.GetChildMemberWithName('DisplayIndex') index = entry_id.GetChildMemberWithName('Value').GetValueAsUnsigned(0) number = valobj.GetChildMemberWithName('Number').GetValueAsUnsigned(0) block_idx = index >> 16 entry_offset = (index & 0xFFFF) * _FNameEntryStride gname_ptr = _GNameBlocksDebug.GetValueAsUnsigned(0) error = lldb.SBError() block_addr = process.ReadPointerFromMemory(gname_ptr + block_idx * target.GetAddressByteSize(), error) if error.Fail(): return '' entry_addr = block_addr + entry_offset entry = target.CreateValueFromAddress('entry', lldb.SBAddress(entry_addr, target), _FNameEntryType) header = entry.GetChildMemberWithName('Header') length = header.GetChildMemberWithName('Len').GetValueAsUnsigned(0) is_wide = header.GetChildMemberWithName('bIsWide').GetValueAsUnsigned(0) if is_wide: name_addr = entry.GetChildMemberWithName('WideName').GetLoadAddress() raw = process.ReadMemory(name_addr, length * 2, error) if error.Fail(): return '' name = raw.decode('utf-16-le') else: name_addr = entry.GetChildMemberWithName('AnsiName').GetLoadAddress() raw = process.ReadMemory(name_addr, length, error) if error.Fail(): return '' name = raw.decode('utf-8', errors='replace') if number > 0: return '%s_%d' % (name, number - 1) return name ############################################################ # # Providers for TObjectPtr # ############################################################ class TObjectPtrSynthProvider(ForwardingSynthProvider): def update(self): debug_ptr = self.valobj.GetChildMemberWithName('DebugPtr') addr = debug_ptr.GetValueAsUnsigned(0) if addr == 0 or addr & 1: # null or unresolved handle (odd address) self.forward_to = lldb.SBValue() else: self.forward_to = TryGetSyntheticValue(debug_ptr.Dereference()) def TObjectPtrSummaryProvider(valobj, dict): debug_ptr = valobj.GetNonSyntheticValue().GetChildMemberWithName('DebugPtr') addr = debug_ptr.GetValueAsUnsigned(0) if addr == 0: return 'nullptr' if addr & 1: return 'unresolved' return '{...}' ############################################################ # # Providers for TStrongObjectPtr # ############################################################ class TStrongObjectPtrSynthProvider(ForwardingSynthProvider): def update(self): obj = self.valobj.GetChildMemberWithName('Object') addr = obj.GetValueAsUnsigned(0) if addr == 0: self.forward_to = lldb.SBValue() else: self.forward_to = TryGetSyntheticValue(obj.Dereference()) def TStrongObjectPtrSummaryProvider(valobj, dict): obj = valobj.GetNonSyntheticValue().GetChildMemberWithName('Object') addr = obj.GetValueAsUnsigned(0) if addr == 0: return 'nullptr' return '{...}' ############################################################ # # Providers for TWeakObjectPtr # ############################################################ def _resolve_weak_object_ptr(valobj): obj_index = valobj.GetChildMemberWithName('ObjectIndex').GetValueAsSigned(0) serial_num = valobj.GetChildMemberWithName('ObjectSerialNumber').GetValueAsSigned(0) if serial_num == 0 or obj_index < 0: return None chunk_idx = obj_index >> 16 # NumElementsPerChunk = 65536 item_idx = obj_index & 0xFFFF objects = _GObjectArrayForDebugVisualizers.Dereference().GetChildMemberWithName('Objects') # FUObjectItem** chunk = objects.GetChildAtIndex(chunk_idx, lldb.eDynamicDontRunTarget, True) # FUObjectItem* item = chunk.GetChildAtIndex(item_idx, lldb.eDynamicDontRunTarget, True) # FUObjectItem actual_serial = item.GetChildMemberWithName('SerialNumber').GetValueAsSigned(0) if actual_serial != serial_num: return None return item.GetChildMemberWithName('Object') class TWeakObjectPtrSynthProvider(ForwardingSynthProvider): def update(self): obj_ptr = _resolve_weak_object_ptr(self.valobj) if obj_ptr is None or obj_ptr.GetValueAsUnsigned(0) == 0: self.forward_to = lldb.SBValue() else: self.forward_to = TryGetSyntheticValue(obj_ptr.Dereference()) def TWeakObjectPtrSummaryProvider(valobj, dict): raw = valobj.GetNonSyntheticValue() serial_num = raw.GetChildMemberWithName('ObjectSerialNumber').GetValueAsSigned(0) if serial_num == 0: return 'nullptr' if _resolve_weak_object_ptr(raw) is None: return 'expired' return '{...}' ############################################################ # # Providers for TSharedPtr # ############################################################ class TSharedPtrSynthProvider(ForwardingSynthProvider): def update(self): obj = self.valobj.GetChildMemberWithName('Object') addr = obj.GetValueAsUnsigned(0) if addr == 0: self.forward_to = lldb.SBValue() else: self.forward_to = TryGetSyntheticValue(obj.Dereference()) def TSharedPtrSummaryProvider(valobj, dict): obj = valobj.GetNonSyntheticValue().GetChildMemberWithName('Object') addr = obj.GetValueAsUnsigned(0) if addr == 0: return 'nullptr' return '{...}' ############################################################ # # Providers for TWeakPtr # ############################################################ def _resolve_weak_ptr(valobj): """Returns (object SBValue, is_expired). valobj must be the raw (non-synthetic) TWeakPtr.""" weak_ref = valobj.GetChildMemberWithName('WeakReferenceCount') ref_ctrl_ptr_val = weak_ref.GetChildMemberWithName('ReferenceController') if ref_ctrl_ptr_val.GetValueAsUnsigned(0) == 0: return None, False shared_count_field = ref_ctrl_ptr_val.Dereference().GetChildMemberWithName('SharedReferenceCount') if shared_count_field.GetValueAsSigned(0) <= 0: return None, True obj = valobj.GetChildMemberWithName('Object') return obj, False class TWeakPtrSynthProvider(ForwardingSynthProvider): def update(self): obj, expired = _resolve_weak_ptr(self.valobj) if obj is None or obj.GetValueAsUnsigned(0) == 0: self.forward_to = lldb.SBValue() else: self.forward_to = TryGetSyntheticValue(obj.Dereference()) def TWeakPtrSummaryProvider(valobj, dict): obj, expired = _resolve_weak_ptr(valobj.GetNonSyntheticValue()) if expired: return 'expired' if obj is None: return 'nullptr' return '{...}' ############################################################ # # Providers for TUniquePtr # ############################################################ class TUniquePtrSynthProvider(ForwardingSynthProvider): def update(self): ptr = self.valobj.GetChildMemberWithName('Ptr') addr = ptr.GetValueAsUnsigned(0) if addr == 0: self.forward_to = lldb.SBValue() else: self.forward_to = TryGetSyntheticValue(ptr.Dereference()) def TUniquePtrSummaryProvider(valobj, dict): ptr = valobj.GetNonSyntheticValue().GetChildMemberWithName('Ptr') addr = ptr.GetValueAsUnsigned(0) if addr == 0: return 'nullptr' return '{...}' ############################################################ # # Providers for UObject # ############################################################ class UObjectSynthProvider(StoredSynthProvider): def update(self): self.children = [] raw = self.valobj target = raw.GetTarget() debug_ptr = raw.GetChildMemberWithName('ClassPrivate') \ .GetChildMemberWithName('ObjectPtr') \ .GetChildMemberWithName('DebugPtr') class_val = target.CreateValueFromAddress( 'Class', lldb.SBAddress(debug_ptr.GetLoadAddress(), target), debug_ptr.GetType()) self.children.append(class_val) name_field = raw.GetChildMemberWithName('NamePrivate') name_val = target.CreateValueFromAddress( 'Name', lldb.SBAddress(name_field.GetLoadAddress(), target), name_field.GetType()) self.children.append(name_val) def UObjectSummaryProvider(valobj, dict): raw = valobj.GetNonSyntheticValue() class_debug_ptr = raw.GetChildMemberWithName('ClassPrivate') \ .GetChildMemberWithName('ObjectPtr') \ .GetChildMemberWithName('DebugPtr') class_addr = class_debug_ptr.GetValueAsUnsigned(0) if not class_addr: return 'null' class_name = UEFNameSummaryProvider( class_debug_ptr.Dereference().GetChildMemberWithName('NamePrivate'), dict) instance_name = UEFNameSummaryProvider( raw.GetChildMemberWithName('NamePrivate'), dict) return 'Class=%s Name=%s' % (class_name, instance_name) ############################################################ # # Providers for TArray # ############################################################ def _resolve_tarray_data_addr(allocator): """Returns the base address of the element data for any TArray allocator.""" # Heap allocator (TSizedHeapAllocator): has a Data pointer directly data = allocator.GetChildMemberWithName('Data') if data.IsValid(): return data.GetValueAsUnsigned(0) # Inline allocator (TSizedInlineAllocator): prefer SecondaryData.Data, fall back to InlineData secondary = allocator.GetChildMemberWithName('SecondaryData') if secondary.IsValid(): sec_data = secondary.GetChildMemberWithName('Data') if sec_data.IsValid() and sec_data.GetValueAsUnsigned(0) != 0: return sec_data.GetValueAsUnsigned(0) # Fixed allocator or inline allocator using inline storage inline = allocator.GetChildMemberWithName('InlineData') if inline.IsValid(): return inline.GetLoadAddress() return 0 def _resolve_tarray(tarray_val): """Given an SBValue of a TArray, return (array_num, data_sbvalue) where data_sbvalue is a typed array of the TArray's element type. Returns (0, None) if empty or unresolvable.""" tarray_val = tarray_val.GetNonSyntheticValue() arr_type = tarray_val.GetType().GetUnqualifiedType() if arr_type.IsReferenceType(): arr_type = arr_type.GetDereferencedType() elif arr_type.IsPointerType(): arr_type = arr_type.GetPointeeType() if arr_type.GetNumberOfTemplateArguments() == 0: return 0, None array_num = tarray_val.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) if array_num <= 0: return 0, None data_addr = _resolve_tarray_data_addr(tarray_val.GetChildMemberWithName('AllocatorInstance')) if data_addr == 0: return 0, None elem_type = arr_type.GetTemplateArgumentType(0) if elem_type.GetByteSize() == 0: return 0, None target = tarray_val.GetTarget() data = target.CreateValueFromAddress( 'data', lldb.SBAddress(data_addr, target), elem_type.GetArrayType(array_num)) return array_num, data class TArraySynthProvider(IndexedSynthProvider): def get_child_at_index(self, index): if index < 0 or index >= self.count: return lldb.SBValue() return self.data_array.GetChildAtIndex(index) def update(self): self.count, self.data_array = _resolve_tarray(self.valobj) def TArraySummaryProvider(valobj, dict): raw = valobj.GetNonSyntheticValue() array_num = raw.GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) if array_num < 0: return 'corrupted' return 'size=%d' % array_num ############################################################ # # Provider for TBitArray # ############################################################ def _bitarray_words(bit_array_val): """Given an SBValue of a TBitArray, return its backing uint32[] as an SBValue (or None if the array is empty/unallocated).""" bit_array_val = bit_array_val.GetNonSyntheticValue() num_bits = bit_array_val.GetChildMemberWithName('NumBits').GetValueAsSigned(0) if num_bits <= 0: return None allocator = bit_array_val.GetChildMemberWithName('AllocatorInstance') data_addr = _resolve_tarray_data_addr(allocator) if data_addr == 0: return None target = bit_array_val.GetTarget() uint32_type = target.GetBasicType(lldb.eBasicTypeUnsignedInt) num_words = (num_bits + 31) // 32 return target.CreateValueFromAddress( 'words', lldb.SBAddress(data_addr, target), uint32_type.GetArrayType(num_words)) class TBitArraySynthProvider(IndexedSynthProvider): def get_child_at_index(self, index): if index < 0 or index >= self.count: return lldb.SBValue() word = self.words.GetChildAtIndex(index) target = self.valobj.GetTarget() child = target.CreateValueFromAddress( '[%d]' % index, lldb.SBAddress(word.GetLoadAddress(), target), target.GetBasicType(lldb.eBasicTypeUnsignedInt)) child.SetFormat(lldb.eFormatBinary) return child def update(self): self.count = 0 self.words = _bitarray_words(self.valobj) if self.words is None: return self.count = self.words.GetNumChildren() def TBitArraySummaryProvider(valobj, dict): num_bits = valobj.GetNonSyntheticValue().GetChildMemberWithName('NumBits').GetValueAsSigned(0) return 'size=%d' % num_bits ############################################################ # # Provider for TSparseArray # ############################################################ class TSparseArraySynthProvider(IndexedSynthProvider): def get_child_at_index(self, index): if index < 0 or index >= self.count: return lldb.SBValue() raw_idx = self.live_indices[index] slot = self.data_array.GetChildAtIndex(raw_idx) target = self.valobj.GetTarget() return target.CreateValueFromAddress( '[%d]' % index, lldb.SBAddress(slot.GetLoadAddress(), target), self.element_type) def update(self): self.count = 0 self.live_indices = [] self.data_array = None self.element_type = None array_num, data = _resolve_tarray(self.valobj.GetChildMemberWithName('Data')) if data is None: return words = _bitarray_words(self.valobj.GetChildMemberWithName('AllocationFlags')) if words is None: return self.data_array = data self.element_type = self.valobj.GetType().GetTemplateArgumentType(0) for i in range(array_num): word = words.GetChildAtIndex(i // 32).GetValueAsUnsigned(0) if (word >> (i % 32)) & 1: self.live_indices.append(i) self.count = len(self.live_indices) def TSparseArraySummaryProvider(valobj, dict): raw = valobj.GetNonSyntheticValue() array_num = raw.GetChildMemberWithName('Data').GetNonSyntheticValue().GetChildMemberWithName('ArrayNum').GetValueAsSigned(0) num_free = raw.GetChildMemberWithName('NumFreeIndices').GetValueAsSigned(0) return 'size=%d' % (array_num - num_free) ############################################################ # # Provider for TSet # ############################################################ class TSetSynthProvider(ForwardingSynthProvider): def update(self): self.forward_to = TryGetSyntheticValue(self.valobj.GetChildMemberWithName('Elements')) def TSetSummaryProvider(valobj, dict): elements = valobj.GetNonSyntheticValue().GetChildMemberWithName('Elements') return TSparseArraySummaryProvider(elements, dict) ############################################################ # # Provider for TMap / TMultiMap # ############################################################ class TMapSynthProvider(ForwardingSynthProvider): def update(self): self.forward_to = TryGetSyntheticValue(self.valobj.GetChildMemberWithName('Pairs').GetNonSyntheticValue().GetChildMemberWithName('Elements')) def TMapSummaryProvider(valobj, dict): pairs = valobj.GetNonSyntheticValue().GetChildMemberWithName('Pairs') elements = pairs.GetNonSyntheticValue().GetChildMemberWithName('Elements') return TSparseArraySummaryProvider(elements, dict) ############################################################ # # Provider for TTuple # ############################################################ def _tuple_child_name(index): if index == 0: return 'Key' if index == 1: return 'Value' return 'Value%d' % index class TTupleSynthProvider(StoredSynthProvider): def update(self): self.children = [] raw = self.valobj.GetNonSyntheticValue() target = raw.GetTarget() container = raw first = raw.GetChildAtIndex(0) if first.IsValid() and 'TTupleBase<' in (first.GetType().GetName() or ''): container = first n = container.GetNumChildren() for i in range(n): base = container.GetChildAtIndex(i) field = base.GetChildAtIndex(0) if not field.IsValid(): continue child = target.CreateValueFromAddress( _tuple_child_name(i), lldb.SBAddress(field.GetLoadAddress(), target), field.GetType()) self.children.append(child) ############################################################ # # Provider for TSetElement # ############################################################ class TSetElementSynthProvider(ForwardingSynthProvider): def update(self): self.forward_to = TryGetSyntheticValue(self.valobj.GetChildMemberWithName('Value')) ############################################################## # # The getattr which is built in to codelldb.value.Value # fails to fetch synthetic values. Monkey-patch it. # ############################################################## def _value_getattr(self, name): sbv = self._Value__sbvalue if sbv.GetType().IsPointerType(): sbv = sbv.Dereference() child = sbv.GetChildMemberWithName(name) if not child.IsValid(): for i in range(sbv.GetNumChildren()): c = sbv.GetChildAtIndex(i) if c.GetName() == name: child = c break if not child.IsValid(): raise AttributeError("Attribute '%s' is not defined" % name) return codelldb.value.Value(child) codelldb.value.Value.__getattr__ = _value_getattr ############################################################## # # The standard repr for SBValue will recurse infinitely if # you provide useful synth providers for smart pointers. # Monkey-patch it. # ############################################################## def _sbvalue_repr(self): n = self.GetNumChildren() if n == 0: return _sbvalue_display(self) parts = [] for i in range(n): child = self.GetChildAtIndex(i) name = child.GetName() or str(i) parts.append('%s=%s\n' % (name, _sbvalue_display(child))) return ''.join(parts) def _sbvalue_display(v): s = v.GetSummary() if s: return s s = v.GetValue() if s: return s if v.GetNumChildren() > 0: return '{...}' return '?' lldb.SBValue.__repr__ = _sbvalue_repr ############################################################## # # Module Initialization # ############################################################# def _register_provider(cat, pattern, summary_fn=None, synth_cls=None): """Register summary and/or synthetic providers for a type pattern.""" spec = lldb.SBTypeNameSpecifier(pattern, lldb.eFormatterMatchRegex) if summary_fn: cat.AddTypeSummary(spec, lldb.SBTypeSummary.CreateWithFunctionName( __name__ + '.' + summary_fn, lldb.eTypeOptionCascade)) if synth_cls: cat.AddTypeSynthetic(spec, lldb.SBTypeSynthetic.CreateWithClassName( __name__ + '.' + synth_cls, lldb.eTypeOptionCascade)) def __lldb_init_module(debugger, dict): print("Running lldb_init_module") debugger.HandleCommand('target stop-hook add -P ' + __name__ + '._GlobalsInitStopHook') debugger.HandleCommand('type category delete ' + __name__) target = debugger.GetSelectedTarget() if target.IsValid(): _init_globals(target) cat = debugger.CreateCategory(__name__) _register_provider(cat, '^FString$', summary_fn='UEFStringSummaryProvider') _register_provider(cat, '^FName$', summary_fn='UEFNameSummaryProvider') _register_provider(cat, '^TObjectPtr<.+>$', summary_fn='TObjectPtrSummaryProvider', synth_cls='TObjectPtrSynthProvider') _register_provider(cat, '^TStrongObjectPtr<.+>$', summary_fn='TStrongObjectPtrSummaryProvider', synth_cls='TStrongObjectPtrSynthProvider') _register_provider(cat, '^TSharedPtr<.+>$', summary_fn='TSharedPtrSummaryProvider', synth_cls='TSharedPtrSynthProvider') _register_provider(cat, '^TSharedRef<.+>$', summary_fn='TSharedPtrSummaryProvider', synth_cls='TSharedPtrSynthProvider') _register_provider(cat, '^TWeakPtr<.+>$', summary_fn='TWeakPtrSummaryProvider', synth_cls='TWeakPtrSynthProvider') _register_provider(cat, '^TWeakObjectPtr<.+>$', summary_fn='TWeakObjectPtrSummaryProvider', synth_cls='TWeakObjectPtrSynthProvider') _register_provider(cat, '^FWeakObjectPtr$', summary_fn='TWeakObjectPtrSummaryProvider', synth_cls='TWeakObjectPtrSynthProvider') _register_provider(cat, '^TArray<.+>$', summary_fn='TArraySummaryProvider', synth_cls='TArraySynthProvider') _register_provider(cat, '^TBitArray<.+>$', summary_fn='TBitArraySummaryProvider', synth_cls='TBitArraySynthProvider') _register_provider(cat, '^TSparseArray<.+>$', summary_fn='TSparseArraySummaryProvider', synth_cls='TSparseArraySynthProvider') _register_provider(cat, '^TSetElement<.+>$', synth_cls='TSetElementSynthProvider') _register_provider(cat, '^TTuple<.+>$', synth_cls='TTupleSynthProvider') _register_provider(cat, '^TSet<.+>$', summary_fn='TSetSummaryProvider', synth_cls='TSetSynthProvider') _register_provider(cat, '^TMap<.+>$', summary_fn='TMapSummaryProvider', synth_cls='TMapSynthProvider') _register_provider(cat, '^TMultiMap<.+>$', summary_fn='TMapSummaryProvider', synth_cls='TMapSynthProvider') _register_provider(cat, '^UObject$', summary_fn='UObjectSummaryProvider', synth_cls='UObjectSynthProvider') for class_name in ( 'AActor', 'APawn', 'ACharacter', 'APlayerController', 'AGameModeBase', 'UActorComponent', 'USceneComponent', 'UPrimitiveComponent', 'UWidget', 'UUserWidget', 'UWorld', 'ULevel', 'UGameInstance'): _register_provider(cat, '^%s$' % class_name, summary_fn='UObjectSummaryProvider') cat.SetEnabled(True)