Skip to content

Commit e3f9e86

Browse files
committed
Add suport for C# LinkedList.
1 parent 8bc2516 commit e3f9e86

File tree

3 files changed

+138
-4
lines changed

3 files changed

+138
-4
lines changed

Visual_Studio_2017/GraphicalDebugging/ExpressionLoader.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,7 @@ private ExpressionLoader(DTE2 dte)
108108

109109
loadersCS = new Loaders();
110110

111+
loadersCS.Add(new CSLinkedList());
111112
loadersCS.Add(new CSList());
112113
loadersCS.Add(new CSArray());
113114

Visual_Studio_2017/GraphicalDebugging/ExpressionLoader_ContainerLoader.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -864,5 +864,101 @@ public override bool ForEachMemoryBlock(MemoryReader mreader, Debugger debugger,
864864
elementConverter, memoryBlockPredicate);
865865
}
866866
}
867+
868+
class CSLinkedList : ContainerLoader
869+
{
870+
public override string Id() { return "System.Collections.Generic.LinkedList"; }
871+
872+
public override string ElementName(string name, string elType)
873+
{
874+
return name + ".head.item";
875+
}
876+
877+
public override int LoadSize(Debugger debugger, string name)
878+
{
879+
return ExpressionParser.LoadSize(debugger, name + ".count");
880+
}
881+
882+
public override bool ForEachMemoryBlock(MemoryReader mreader, Debugger debugger,
883+
string name, string type,
884+
MemoryReader.Converter<double> elementConverter,
885+
MemoryBlockPredicate memoryBlockPredicate)
886+
{
887+
int size = LoadSize(debugger, name);
888+
if (size <= 0)
889+
return true;
890+
891+
// TODO: All of the debugger-related things should be done
892+
// in Initialize().
893+
894+
// TODO: Handle non-value types,
895+
// It is not clear for now where the distinction should be made
896+
// in the container or outside. When non-value types are stored
897+
// the container effectively stores pointers to objects.
898+
// So whether or not it's a pointer-container is defined by the
899+
// element type in C# and by the container in C++.
900+
string elementType = debugger.GetExpression(name + ".head.item").Type;
901+
Expression isValueTypeExpr = debugger.GetExpression("typeof(" + elementType + ").IsValueType");
902+
if (!isValueTypeExpr.IsValidValue || isValueTypeExpr.Value != "true")
903+
return false;
904+
905+
//string headPointerPointerName = "(void*)&(" + name + ".head)"; //(void*)IntPtr*
906+
string headPointerName = "(void*)*(&(" + name + ".head))"; // (void*)IntPtr
907+
string nextPointerPointerName = "(void*)&(" + name + ".head.next)"; //(void*)IntPtr*
908+
string nextPointerName = "(void*)*(&(" + name + ".head.next))"; // (void*)IntPtr
909+
string valPointerName = "(void*)&(" + name + ".head.item)"; // (void*)IntPtr* or (void*)ValueType*
910+
911+
TypeInfo nextPointerInfo = new TypeInfo(debugger, nextPointerPointerName);
912+
TypeInfo nextInfo = new TypeInfo(debugger, nextPointerName);
913+
if (!nextPointerInfo.IsValid || !nextInfo.IsValid)
914+
return false;
915+
916+
MemoryReader.ValueConverter<ulong> pointerConverter = mreader.GetPointerConverter(nextPointerInfo.Type, nextPointerInfo.Size);
917+
if (pointerConverter == null)
918+
return false;
919+
920+
long nextDiff = ExpressionParser.GetPointerDifference(debugger, headPointerName, nextPointerPointerName);
921+
long valDiff = ExpressionParser.GetPointerDifference(debugger, headPointerName, valPointerName);
922+
if (ExpressionParser.IsInvalidAddressDifference(nextDiff)
923+
|| ExpressionParser.IsInvalidAddressDifference(valDiff)
924+
|| nextDiff < 0 || valDiff < 0)
925+
return false;
926+
927+
ulong address = ExpressionParser.GetPointer(debugger, headPointerName);
928+
if (address == 0)
929+
return false;
930+
931+
for (int i = 0; i < size; ++i)
932+
{
933+
double[] values = new double[elementConverter.ValueCount()];
934+
if (!mreader.Read(address + (ulong)valDiff, values, elementConverter))
935+
return false;
936+
937+
if (!memoryBlockPredicate(values))
938+
return false;
939+
940+
ulong[] nextTmp = new ulong[1];
941+
if (!mreader.Read(address + (ulong)nextDiff, nextTmp, pointerConverter))
942+
return false;
943+
address = nextTmp[0];
944+
}
945+
return true;
946+
}
947+
948+
public override bool ForEachElement(Debugger debugger, string name, ElementPredicate elementPredicate)
949+
{
950+
int size = this.LoadSize(debugger, name);
951+
952+
string nodeName = name + ".head";
953+
for (int i = 0; i < size; ++i)
954+
{
955+
string elName = nodeName + ".item";
956+
if (!elementPredicate(elName))
957+
return false;
958+
nodeName = nodeName + ".next";
959+
}
960+
return true;
961+
}
962+
}
867963
}
868964
}

Visual_Studio_2017/GraphicalDebugging/ExpressionParser.cs

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -84,20 +84,51 @@ public static long GetAddressDifference(Debugger debugger, string valName1, stri
8484
: -(long)(addr1 - addr2);
8585
}
8686

87+
public static long GetPointerDifference(Debugger debugger, string pointerName1, string pointerName2)
88+
{
89+
ulong addr1 = GetPointer(debugger, pointerName1);
90+
ulong addr2 = GetPointer(debugger, pointerName2);
91+
if (addr1 == 0 || addr2 == 0)
92+
return long.MinValue;
93+
return (addr2 >= addr1)
94+
? (long)(addr2 - addr1)
95+
: -(long)(addr1 - addr2);
96+
}
97+
8798
public static bool IsInvalidAddressDifference(long diff)
8899
{
89100
return diff == long.MinValue;
90101
}
91102

92103
public ulong GetValueAddress(string valName)
93104
{
94-
return GetValueAddress(debugger, valName);
105+
Expression ptrExpr = debugger.GetExpression("(void*)&(" + valName + ")");
106+
if (!ptrExpr.IsValidValue)
107+
return 0;
108+
string addr = ptrExpr.Value;
109+
110+
// NOTE: Hexadecimal value is automatically detected, this is probably not needed.
111+
// But automatically detect the format just in case of various versions
112+
// of VS displayed it differently regardless of debugger mode.
113+
return Util.ParseULong(addr/*, true*/);
95114
}
96115

97-
// Valid address of variable valName or 0
98-
public static ulong GetValueAddress(Debugger debugger, string valName)
116+
// TODO: C# classes
117+
// For value-types, structs, etc.
118+
// "typeof(" + type + ").IsValueType" == "true"
119+
// "&(" + name + ")" is of type SomeType*
120+
// - address: "&(" + name + ")"
121+
// - size: "sizeof(" + type + ")"
122+
// For non-value-types, e.g. classes
123+
// "typeof(" + type + ").IsValueType" == "false"
124+
// "&(" + name + ")" is of type IntPtr*
125+
// - address: "*(&(" + name + "))"
126+
// - size: "System.Runtime.InteropServices.Marshal.ReadInt32(typeof(" + type + ").TypeHandle.Value, 4)"
127+
// - size: "*(((int*)(void*)typeof(" + type + ").TypeHandle.Value) + 1)"
128+
129+
public static ulong GetPointer(Debugger debugger, string pointerName)
99130
{
100-
Expression ptrExpr = debugger.GetExpression("((void*)&(" + valName + "))");
131+
Expression ptrExpr = debugger.GetExpression("(void*)(" + pointerName + ")");
101132
if (!ptrExpr.IsValidValue)
102133
return 0;
103134
string addr = ptrExpr.Value;
@@ -108,6 +139,12 @@ public static ulong GetValueAddress(Debugger debugger, string valName)
108139
return Util.ParseULong(addr/*, true*/);
109140
}
110141

142+
// Valid address of variable valName or 0
143+
public static ulong GetValueAddress(Debugger debugger, string valName)
144+
{
145+
return GetPointer(debugger, "&(" + valName + ")");
146+
}
147+
111148
// Valid size or 0
112149
// NOTE: In C++ the actual byte size depends on CHAR_BIT
113150
public int GetValueSizeof(string valName)

0 commit comments

Comments
 (0)