Browse Source

Fix some bugs in debug info decoration for ReadyToRun

pull/2279/head
Andrew Au 5 years ago
parent
commit
2c44b7fb90
  1. 70
      ILSpy.ReadyToRun/ReadyToRunDisassembler.cs
  2. 9
      ILSpy.ReadyToRun/ReadyToRunLanguage.cs

70
ILSpy.ReadyToRun/ReadyToRunDisassembler.cs

@ -162,45 +162,65 @@ namespace ICSharpCode.ILSpy.ReadyToRun
{ {
public List<NativeVarInfoRecord> records; public List<NativeVarInfoRecord> records;
public int i; public int i;
public Dictionary<string, Dictionary<int, Variable>> registerRelativeVariables; public Dictionary<string, Dictionary<int, HashSet<Variable>>> registerRelativeVariables;
public Dictionary<string, Variable> registerVariables; public Dictionary<string, HashSet<Variable>> registerVariables;
public DebugInfoHelper() public DebugInfoHelper()
{ {
this.registerRelativeVariables = new Dictionary<string, Dictionary<int, Variable>>(); this.registerRelativeVariables = new Dictionary<string, Dictionary<int, HashSet<Variable>>>();
this.registerVariables = new Dictionary<string, Variable>(); this.registerVariables = new Dictionary<string, HashSet<Variable>>();
} }
public void Update(ulong codeOffset) public void Update(ulong codeOffset)
{ {
HashSet<Variable> variables;
while (i < records.Count && records[i].codeOffset == codeOffset) while (i < records.Count && records[i].codeOffset == codeOffset)
{ {
if (records[i].isRegRelative) if (records[i].isRegRelative)
{ {
Dictionary<int, HashSet<Variable>> offsetToVariableMap;
if (records[i].isStart) if (records[i].isStart)
{ {
Dictionary<int, Variable> offsetToVariableMap;
if (!this.registerRelativeVariables.TryGetValue(records[i].register, out offsetToVariableMap)) if (!this.registerRelativeVariables.TryGetValue(records[i].register, out offsetToVariableMap))
{ {
offsetToVariableMap = new Dictionary<int, Variable>(); offsetToVariableMap = new Dictionary<int, HashSet<Variable>>();
this.registerRelativeVariables.Add(records[i].register, offsetToVariableMap); this.registerRelativeVariables.Add(records[i].register, offsetToVariableMap);
} }
offsetToVariableMap.Add(records[i].registerOffset, records[i].variable); if (!offsetToVariableMap.TryGetValue(records[i].registerOffset, out variables))
{
variables = new HashSet<Variable>();
offsetToVariableMap.Add(records[i].registerOffset, variables);
}
variables.Add(records[i].variable);
} }
else else
{ {
this.registerRelativeVariables[records[i].register].Remove(records[i].registerOffset); offsetToVariableMap = this.registerRelativeVariables[records[i].register];
variables = offsetToVariableMap[records[i].registerOffset];
variables.Remove(records[i].variable);
} }
} }
else else
{ {
if (records[i].isStart) if (records[i].isStart)
{ {
this.registerVariables.Add(records[i].register, records[i].variable); if (!this.registerVariables.TryGetValue(records[i].register, out variables))
{
variables = new HashSet<Variable>();
this.registerVariables.Add(records[i].register, variables);
}
variables.Add(records[i].variable);
} }
else else
{ {
this.registerVariables.Remove(records[i].register); // If the optimizing compiler decides that two variables will always have the same value within a basic block
// It might assign the same location for two variables.
// The compiler also generates potentially wrong 1 byte long debug info record for arguments in prolog.
// These record might describe the same variable in overlapping ranges.
// See https://cshung.github.io/posts/debug-info-debugging/ for the investigation.
variables = this.registerVariables[records[i].register];
variables.Remove(records[i].variable);
} }
} }
i++; i++;
@ -219,7 +239,13 @@ namespace ICSharpCode.ILSpy.ReadyToRun
for (int i = 0; i < debugInfo.VariablesList.Count; ++i) for (int i = 0; i < debugInfo.VariablesList.Count; ++i)
{ {
var varLoc = debugInfo.VariablesList[i]; var varLoc = debugInfo.VariablesList[i];
if (varLoc.StartOffset == varLoc.EndOffset)
{
// This could happen if the compiler is generating bogus variable info mapping record that covers 0 instructions
// See https://github.com/dotnet/runtime/issues/47202
Debug.Assert(false);
continue;
}
switch (varLoc.VariableLocation.VarLocType) switch (varLoc.VariableLocation.VarLocType)
{ {
case VarLocType.VLT_STK: case VarLocType.VLT_STK:
@ -344,32 +370,40 @@ namespace ICSharpCode.ILSpy.ReadyToRun
{ {
if (debugRecords != null) if (debugRecords != null)
{ {
HashSet<Variable> variables;
InstructionInfoFactory factory = new InstructionInfoFactory(); InstructionInfoFactory factory = new InstructionInfoFactory();
InstructionInfo info = factory.GetInfo(instr); InstructionInfo info = factory.GetInfo(instr);
ulong codeOffset = instr.IP - baseInstrIP; ulong codeOffset = instr.IP - baseInstrIP;
debugRecords.Update(codeOffset); debugRecords.Update(codeOffset);
Variable variable = null;
foreach (UsedMemory usedMemInfo in info.GetUsedMemory()) foreach (UsedMemory usedMemInfo in info.GetUsedMemory())
{ {
string baseRegister = usedMemInfo.Base.ToString(); string baseRegister = usedMemInfo.Base.ToString();
int displacement; int displacement;
unchecked unchecked
{ displacement = (int)usedMemInfo.Displacement; } { displacement = (int)usedMemInfo.Displacement; }
Dictionary<int, Variable> offsetToVariableMap; Dictionary<int, HashSet<Variable>> offsetToVariableMap;
if (debugRecords.registerRelativeVariables.TryGetValue(usedMemInfo.Base.ToString(), out offsetToVariableMap)) if (debugRecords.registerRelativeVariables.TryGetValue(usedMemInfo.Base.ToString(), out offsetToVariableMap))
{ {
if (offsetToVariableMap.TryGetValue(displacement, out variable)) if (offsetToVariableMap.TryGetValue(displacement, out variables))
{ {
output.Write($"; [{usedMemInfo.Base.ToString().ToLower()}{(displacement < 0 ? '-' : '+')}{Math.Abs(displacement):X}h] = {variable.Type} {variable.Index}"); output.Write($";");
foreach (Variable variable in variables)
{
output.Write($" [{usedMemInfo.Base.ToString().ToLower()}{(displacement < 0 ? '-' : '+')}{Math.Abs(displacement):X}h] = {variable.Type} {variable.Index}");
}
} }
} }
} }
foreach (UsedRegister usedMemInfo in info.GetUsedRegisters()) foreach (UsedRegister usedMemInfo in info.GetUsedRegisters())
{ {
if (debugRecords.registerVariables.TryGetValue(usedMemInfo.Register.ToString(), out variable)) // TODO, if the code is accessing EAX but the debug info maps to RAX, then this match is going to fail.
if (debugRecords.registerVariables.TryGetValue(usedMemInfo.Register.ToString(), out variables))
{ {
output.Write($"; {usedMemInfo.Register.ToString().ToLower()} = {variable.Type} {variable.Index}"); output.Write($";");
foreach (Variable variable in variables)
{
output.Write($" {usedMemInfo.Register.ToString().ToLower()} = {variable.Type} {variable.Index}");
}
} }
} }
} }

9
ILSpy.ReadyToRun/ReadyToRunLanguage.cs

@ -16,7 +16,7 @@
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE. // DEALINGS IN THE SOFTWARE.
// #define STRESS #define STRESS
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
@ -154,10 +154,11 @@ namespace ICSharpCode.ILSpy.ReadyToRun
bool showMetadataTokens = ILSpy.Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokens; bool showMetadataTokens = ILSpy.Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokens;
bool showMetadataTokensInBase10 = ILSpy.Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokensInBase10; bool showMetadataTokensInBase10 = ILSpy.Options.DisplaySettingsPanel.CurrentDisplaySettings.ShowMetadataTokensInBase10;
#if STRESS #if STRESS
ITextOutput originalOutput = output;
output = new DummyOutput(); output = new DummyOutput();
{ {
foreach (var readyToRunMethod in reader.Methods) foreach (var readyToRunMethod in reader.Methods)
{ {
#else #else
if (cacheEntry.methodMap.TryGetValue(method.MetadataToken, out var methods)) if (cacheEntry.methodMap.TryGetValue(method.MetadataToken, out var methods))
{ {
@ -170,6 +171,10 @@ namespace ICSharpCode.ILSpy.ReadyToRun
} }
} }
} }
#if STRESS
output = originalOutput;
output.WriteLine("Passed");
#endif
} }
} }

Loading…
Cancel
Save