Files
UnrealEngine/Engine/Extras/ThirdPartyNotUE/crunchersharp/SymbolInfo.cs
2025-05-18 13:04:45 +08:00

695 lines
18 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Ports;
using System.Linq;
using System.Reflection;
namespace CruncherSharp
{
public class SymbolInfo
{
public string Name { get; private set; }
public string TypeName { get; set; }
public ulong Size { get; set; }
public ulong? NewSize { get; set; }
public uint EndPadding { get; set; }
public uint Padding => (uint)(EndPadding + Members.Sum(info => info.PaddingBefore)); // This is the local (intrinsic) padding
public uint? MinAlignment { get; set; }
public uint PaddingZonesCount => (uint)((EndPadding > 0 ? 1 : 0) + Members.Sum(info => info.PaddingBefore > 0 ? 1 : 0));
public uint? TotalPadding { get; set; } // Includes padding from base classes and members
public ulong? PotentialSaving { get; set; }
public ulong NumInstances { get; set; }
public ulong TotalCount { get; set; }
public ulong LowerMemPool { get; set; }
public ulong CurrentMemPool { get; set; }
public ulong? NewMemPool { get; set; }
public bool IsAbstract { get; set; }
public bool IsTemplate { get; set; }
public bool IsImportedFromCSV { get; set; }
public List<SymbolMemberInfo> Members { get; set; }
public List<SymbolFunctionInfo> Functions { get; set; }
public List<SymbolInfo> DerivedClasses { get; set; }
public SymbolInfo(string name, string typeName, ulong size, List<uint> MemPools)
{
Name = name;
TypeName = typeName;
Size = size;
NewSize = null;
EndPadding = 0;
LowerMemPool = 0;
CurrentMemPool = 0;
NewMemPool = null;
MinAlignment = null;
TotalPadding = null;
PotentialSaving = null;
Members = new List<SymbolMemberInfo>();
Functions = null;
IsAbstract = false;
IsImportedFromCSV = false;
if (Name.Contains("<") && Name.Contains(">"))
IsTemplate = true;
SetMemPools(MemPools);
}
public void AddMember(SymbolMemberInfo member)
{
Members.Add(member);
}
public void AddFunction(SymbolFunctionInfo function)
{
if (Functions == null)
{
Functions = new List<SymbolFunctionInfo>();
}
Functions.Add(function);
if (function.IsPure)
{
IsAbstract = true;
}
}
public void SetMemPools(List<uint> MemPools)
{
uint previousMemPool = 0;
foreach (var memPool in MemPools)
{
if (Size > memPool)
{
previousMemPool = memPool;
continue;
}
CurrentMemPool = memPool;
LowerMemPool = previousMemPool;
break;
}
if (CurrentMemPool == 0)
{
CurrentMemPool = Size;
}
SetNewMemPools(MemPools);
}
public void SetNewMemPools(List<uint> MemPools)
{
if (!NewSize.HasValue)
return;
foreach (var memPool in MemPools)
{
if (NewSize > memPool)
{
continue;
}
NewMemPool = memPool;
return;
}
NewMemPool = NewSize;
}
private bool ComputeOffsetCollision(int index)
{
return index > 0 && Members[index].Offset == Members[index - 1].Offset;
}
public bool HasVtable
{
get
{
foreach (var member in Members)
{
if (member.Category == SymbolMemberInfo.MemberCategory.VTable)
{
return true;
}
}
return false;
}
}
public bool HasBaseClass
{
get
{
foreach (var member in Members)
{
if (member.Category == SymbolMemberInfo.MemberCategory.Base)
{
return true;
}
}
return false;
}
}
public bool HasUnusedVTable()
{
return HasVtable && !HasBaseClassWithVTable() && DerivedClasses == null;
}
public bool HasBaseClassWithVTable()
{
foreach (var member in Members)
{
if (member.Category == SymbolMemberInfo.MemberCategory.Base)
{
if (member.TypeInfo == null)
{
continue;
}
if (member.TypeInfo.HasVtable)
return true;
}
}
return false;
}
// https://randomascii.wordpress.com/2013/12/01/vc-2013-class-layout-change-and-wasted-space/
public bool HasMSVCExtraPadding()
{
if (HasBaseClassWithVTable())
return false;
if (!HasVtable)
return false;
if (Members.Count < 2)
return false;
return Members[1].Size < 16 && Members[1].Offset == 16;
}
public bool HasMSVCEmptyBaseClass
{
get
{
var n = 1;
while (n + 2 < Members.Count)
{
if (Members[n - 1].IsBase && Members[n].IsBase && Members[n].Size == 1 && Members[n].Offset > Members[n - 1].Offset && Members[n + 1].Offset > Members[n].Offset)
return true;
n++;
}
return false;
}
}
public void SortAndCalculate()
{
// Sort members by offset, recompute padding.
// Sorting is usually not needed (for data fields), but sometimes base class order is wrong.
Members.Sort(SymbolMemberInfo.CompareOffsets);
for (int i = 0; i < Members.Count; ++i)
{
var member = Members[i];
member.AlignWithPrevious = ComputeOffsetCollision(i);
member.PaddingBefore = ComputePadding(i);
member.BitPaddingAfter = ComputeBitPadding(i);
}
EndPadding = ComputeEndPadding();
}
private uint ComputePadding(int index)
{
if (index < 1 || index > Members.Count)
{
return 0;
}
if (index < Members.Count && Members[index].AlignWithPrevious)
{
return 0;
}
int previousIndex = index - 1;
ulong biggestSize = Members[previousIndex].Size;
while (Members[previousIndex].AlignWithPrevious && previousIndex > 0)
{
previousIndex--;
if (biggestSize < Members[previousIndex].Size)
biggestSize = Members[previousIndex].Size;
}
ulong currentOffset = index > Members.Count - 1 ? Size : Members[index].Offset;
ulong previousEnd = Members[previousIndex].Offset + biggestSize;
return currentOffset > previousEnd ? (uint)(currentOffset - previousEnd) : 0u;
}
private uint ComputeBitPadding(int index)
{
if (index > Members.Count)
{
return 0;
}
if (!Members[index].BitField)
{
return 0;
}
if (index + 1 < Members.Count)
{
if (Members[index + 1].BitField && Members[index + 1].Offset == Members[index].Offset)
return 0;
}
return (uint)(8 * Members[index].Size) - (Members[index].BitPosition + Members[index].BitSize);
}
private uint ComputeEndPadding()
{
return ComputePadding(Members.Count);
}
public uint ComputeTotalPadding()
{
if (TotalPadding.HasValue)
{
return TotalPadding.Value;
}
TotalPadding = (uint)((uint)Padding + Members.Sum(info =>
{
if (info.AlignWithPrevious)
return 0;
if (info.Category == SymbolMemberInfo.MemberCategory.Member)
return 0;
if (info.TypeInfo == this)
return 0; // avoid infinite loops
if (info.TypeInfo == null)
{
return 0;
}
return info.TypeInfo.ComputeTotalPadding();
}));
return TotalPadding.Value;
}
public ulong ComputePotentialSaving(SymbolAnalyzer symbolAnalyzer)
{
if (PotentialSaving.HasValue)
{
return PotentialSaving.Value;
}
if (Name == "UObjectBase")
{
PotentialSaving = 0;
}
//List<uint> BitsRemaining = new List<uint>();
uint BitsRemaining = 0;
foreach (var member in Members)
{
if (member.Category != SymbolMemberInfo.MemberCategory.Member)
{
continue;
}
if (member.Size == 1 &&member.TypeName.StartsWith("bool"))
{
if (member.BitField)
{
BitsRemaining += member.BitPaddingAfter;
}
else if (BitsRemaining == 0)
{
BitsRemaining = 7u;
}
else
{
BitsRemaining--;
member.PotentialSaving = 1u;
}
}
else if (member.Size == 4 && member.TypeName.StartsWith("enum "))
{
if (!symbolAnalyzer.ConfigEnum.ContainsKey(member.TypeName.Remove(0, 5)))
{
member.PotentialSaving = 1u;
}
}
}
PotentialSaving = 0;
if (EndPadding < 16)
{
uint InternalPadding = (uint)Members.Sum(info => (long)info.PaddingBefore);
InternalPadding += BitsRemaining / 8u;
InternalPadding += (uint)(Members.Sum(info =>
{
if (info.AlignWithPrevious)
return 0u;
if (info.PotentialSaving.HasValue)
return info.PotentialSaving;
return 0u;
}));
uint MinAlignment = ComputeMinAlignment();
if (InternalPadding > 0 && (InternalPadding + EndPadding) >= MinAlignment)
{
PotentialSaving = InternalPadding + EndPadding;
PotentialSaving -= (PotentialSaving % MinAlignment);
}
}
if (HasUnusedVTable())
PotentialSaving += 8;
PotentialSaving += (ulong)(Members.Sum(info =>
{
if (info.AlignWithPrevious)
return 0u;
if (info.Category == SymbolMemberInfo.MemberCategory.Member)
return 0u;
if (info.PotentialSaving.HasValue)
return info.PotentialSaving;
if (info.TypeName == Name)
return 0u; // avoid infinite loops
if (info.TypeInfo == null)
{
return 0u;
}
return (long)info.Count * (long)info.TypeInfo.ComputePotentialSaving(symbolAnalyzer);
}));
return PotentialSaving.Value;
}
public uint ComputeMinAlignment()
{
if (MinAlignment.HasValue)
{
return MinAlignment.Value;
}
if (EndPadding >= 8)
{
MinAlignment = 16;
return MinAlignment.Value;
}
else if (EndPadding >= 4 || HasVtable)
{
MinAlignment = 8;
}
else if (EndPadding >= 2)
{
MinAlignment = 4;
}
else if (EndPadding > 0)
{
MinAlignment = 2;
}
else
{
MinAlignment = 1;
}
foreach (var member in Members)
{
if (member.MinAlignment > MinAlignment)
{
MinAlignment = member.MinAlignment;
}
}
return MinAlignment.Value;
}
public ulong ComputeTotalMempoolUsage()
{
ulong TotalUsage = CurrentMemPool * NumInstances;
if (DerivedClasses != null)
{
foreach (var derivedClass in DerivedClasses)
{
TotalUsage += derivedClass.ComputeTotalMempoolUsage();
}
}
return TotalUsage;
}
public ulong ComputePotentialTotalSaving()
{
if (PotentialSaving.HasValue)
return ComputePotentialTotalSaving(PotentialSaving.Value);
return 0;
}
private ulong ComputePotentialTotalSaving(ulong Win)
{
ulong TotalWin = 0;
if (LowerMemPool > 0 && PotentialSaving.HasValue && (Size - Win) <= LowerMemPool)
TotalWin = (CurrentMemPool - LowerMemPool) * NumInstances;
if (DerivedClasses != null)
{
foreach (var derivedClass in DerivedClasses)
{
TotalWin += derivedClass.ComputePotentialTotalSaving(Win);
}
}
return TotalWin;
}
public ulong ComputeTotalMempoolWin()
{
return ComputeTotalMempoolWin(Size - LowerMemPool);
}
private ulong ComputeTotalMempoolWin(ulong Win)
{
ulong TotalWin = 0;
if (LowerMemPool > 0 && (Size - Win) <= LowerMemPool)
TotalWin = (CurrentMemPool - LowerMemPool) * NumInstances;
if (DerivedClasses != null)
{
foreach (var derivedClass in DerivedClasses)
{
TotalWin += derivedClass.ComputeTotalMempoolWin(Win);
}
}
return TotalWin;
}
public long ComputeTotalDelta()
{
long TotalUsage = ((long)NewSize - (long)Size) * (long)NumInstances;
if (DerivedClasses != null)
{
foreach (var derivedClass in DerivedClasses)
{
TotalUsage += derivedClass.ComputeTotalDelta();
}
}
return TotalUsage;
}
public long ComputeTotalMempoolDelta()
{
if (!NewMemPool.HasValue)
return 0;
long TotalUsage = ((long)NewMemPool - (long)CurrentMemPool) * (long)NumInstances;
if (DerivedClasses != null)
{
foreach (var derivedClass in DerivedClasses)
{
TotalUsage += derivedClass.ComputeTotalMempoolDelta();
}
}
return TotalUsage;
}
private bool _BaseClassUpdated = false;
public void UpdateBaseClass(SymbolAnalyzer symbolAnalyzer)
{
if (_BaseClassUpdated)
return;
_BaseClassUpdated = true;
bool _ChildClassUpdated = false;
foreach (var member in Members)
{
if (member.Category == SymbolMemberInfo.MemberCategory.Base)
{
member.TypeInfo = symbolAnalyzer.FindSymbolInfo(member.TypeName);
if (member.TypeInfo != null)
{
if (member.TypeInfo.DerivedClasses == null)
member.TypeInfo.DerivedClasses = new List<SymbolInfo>();
member.TypeInfo.DerivedClasses.Add(this);
}
}
else
{
if (member.UpdateTypeInfo(symbolAnalyzer))
{
_ChildClassUpdated = true;
}
}
}
if (_ChildClassUpdated)
{
SortAndCalculate();
}
}
public bool IsA(string name, SymbolAnalyzer symbolAnalyzer)
{
var SymbolInfo = symbolAnalyzer.FindSymbolInfo(name);
if (SymbolInfo == null)
{
return false;
}
foreach (var member in Members)
{
if (member.Category == SymbolMemberInfo.MemberCategory.Base)
{
if (member.TypeInfo != null)
{
if (member.TypeInfo == SymbolInfo)
{
return true;
}
if (member.TypeInfo.IsA(name, symbolAnalyzer))
{
return true;
}
}
}
}
return false;
}
public void CheckOverride()
{
foreach (var function in Functions)
{
if (function.Virtual &&
function.IsOverloaded == false &&
function.Category == SymbolFunctionInfo.FunctionCategory.Function &&
DerivedClasses != null)
{
foreach(var derivedClass in DerivedClasses)
{
if (derivedClass.IsOverloadingFunction(function))
{
function.IsOverloaded = true;
break;
}
}
}
}
}
public void CheckMasking()
{
foreach (var function in Functions)
{
if (function.Virtual == false && function.Category == SymbolFunctionInfo.FunctionCategory.Function && DerivedClasses != null)
{
foreach (var derivedClass in DerivedClasses)
{
derivedClass.CheckMasking(function);
}
}
}
}
private void CheckMasking(SymbolFunctionInfo func)
{
foreach (var function in Functions)
{
if (function.Virtual == false && function.Name == func.Name)
{
function.IsMasking = true;
if (DerivedClasses != null)
{
foreach (var derivedClass in DerivedClasses)
{
derivedClass.CheckMasking(func);
}
}
}
}
}
private bool IsOverloadingFunction(SymbolFunctionInfo func)
{
foreach (var function in Functions)
{
if (function.Name == func.Name)
return true;
}
if (DerivedClasses != null)
{
foreach (var derivedClass in DerivedClasses)
{
if (derivedClass.IsOverloadingFunction(func))
return true;
}
}
return false;
}
public void UpdateTotalCount(ulong count)
{
foreach (var member in Members)
{
if (member.Category == SymbolMemberInfo.MemberCategory.UDT || member.Category == SymbolMemberInfo.MemberCategory.Base)
{
if (member.TypeInfo != null)
{
count *= member.Count;
member.TypeInfo.TotalCount += count;
member.TypeInfo.UpdateTotalCount(count);
}
}
}
}
public override string ToString()
{
var sw = new StringWriter();
sw.WriteLine($"Symbol: {Name}");
sw.WriteLine($"TypeName: {TypeName}");
sw.WriteLine($"Size: {Size}");
sw.WriteLine($"Padding: {Padding}");
sw.WriteLine($"Total padding: {TotalPadding}");
sw.WriteLine("Members:");
sw.WriteLine("-------");
const string PaddingMarker = "****Padding";
foreach (var member in Members)
{
if (member.PaddingBefore > 0)
{
var paddingOffset = member.Offset - member.PaddingBefore;
sw.WriteLine($"{PaddingMarker,-40} {paddingOffset,5} {member.PaddingBefore,5}");
}
sw.WriteLine($"{member.DisplayName,-40} {member.Offset,5} {member.Size,5}");
}
if (EndPadding > 0)
{
var endPaddingOffset = Size - EndPadding;
sw.WriteLine($"{PaddingMarker,-40} {endPaddingOffset,5} {EndPadding,5}");
}
return sw.ToString();
}
}
}