Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .idea/.idea.UnityMixedCallstack/.idea/.gitignore

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

50 changes: 50 additions & 0 deletions CallstackTestProj/CallstackTestProj.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net472</TargetFramework>

<IsPackable>false</IsPackable>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<PlatformTarget>x64</PlatformTarget>
<Platforms>AnyCPU;x64</Platforms>
<VSSdkPath Condition="'$(VisualStudioVersion.Contains(`17`))'">$(ProgramFiles)\Microsoft Visual Studio\2022\Professional\VSSDK\</VSSdkPath>
<VSSdkPath Condition="'$(VisualStudioVersion.Contains(`16`))'">$(MSBuildProgramFiles32)\Microsoft Visual Studio\2019\Professional\VSSDK\</VSSdkPath>
<ConcordSDKDir>$(VSSdkPath)VisualStudioIntegration\</ConcordSDKDir>
</PropertyGroup>

<ItemGroup>
<None Remove="pmip_32260_3.txt" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.TestPlatform" Version="17.5.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
<PackageReference Include="NUnit" Version="3.13.3" />
<PackageReference Include="NUnit3TestAdapter" Version="4.4.0" />
</ItemGroup>

<Import Project="$(ConcordSDKDir)Tools\bin\Microsoft.VSSDK.Debugger.VSDConfigTool.targets" />

<ItemGroup>
<ProjectReference Include="..\UnityMixedCallStack.csproj" />
</ItemGroup>

<ItemGroup>
<None Update="TestData\line-numbers\pmip_23672_1_0.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestData\line-numbers\pmip_23672_4_1.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestData\pmip_31964_3.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestData\pmip_32260_3.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="TestData\pmip_44920_4.txt">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>

</Project>
208 changes: 208 additions & 0 deletions CallstackTestProj/TestData/line-numbers/pmip_23672_1_0.txt

Large diffs are not rendered by default.

95,270 changes: 95,270 additions & 0 deletions CallstackTestProj/TestData/line-numbers/pmip_23672_4_1.txt

Large diffs are not rendered by default.

15,699 changes: 15,699 additions & 0 deletions CallstackTestProj/TestData/pmip_31964_3.txt

Large diffs are not rendered by default.

20,617 changes: 20,617 additions & 0 deletions CallstackTestProj/TestData/pmip_32260_3.txt

Large diffs are not rendered by default.

98 changes: 98 additions & 0 deletions CallstackTestProj/UnitTest1.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using UnityMixedCallStack;

namespace CallstackTestProj
{
[TestFixture]
public class Tests
{
private Tuple<string, string>[] m_legacyData =
{
Tuple.Create("000001C44A1C81D7", "[UnityEditor.CoreModule.dll] (wrapper managed-to-native) UnityEditor.EditorGUIUtility:RenderPlayModeViewCamerasInternal_Injected (UnityEngine.RenderTexture,int,UnityEngine.Vector2&,bool,bool)"),
Tuple.Create("000001C44A1C8083", "[UnityEditor.CoreModule.dll] UnityEditor.EditorGUIUtility:RenderPlayModeViewCamerasInternal (UnityEngine.RenderTexture,int,UnityEngine.Vector2,bool,bool)"),
Tuple.Create("000001C44A1C52EB", "[UnityEditor.CoreModule.dll] UnityEditor.PlayModeView:RenderView (UnityEngine.Vector2,bool)"),
Tuple.Create("000001C449F19A23", "[UnityEditor.CoreModule.dll] UnityEditor.GameView:OnGUI ()"),
Tuple.Create("000001C449F12BF8", "[UnityEditor.CoreModule.dll] UnityEditor.HostView:InvokeOnGUI (UnityEngine.Rect)"),
Tuple.Create("000001C449F12793", "[UnityEditor.CoreModule.dll] UnityEditor.DockArea:DrawView (UnityEngine.Rect)"),
Tuple.Create("000001C449EF1353", "[UnityEditor.CoreModule.dll] UnityEditor.DockArea:OldOnGUI ()"),
Tuple.Create("000001C449EBCCE9", "[UnityEngine.UIElementsModule.dll] UnityEngine.UIElements.IMGUIContainer:DoOnGUI (UnityEngine.Event,UnityEngine.Matrix4x4,UnityEngine.Rect,bool,UnityEngine.Rect,System.Action,bool)"),
Tuple.Create("000001C449EBAC13", "[UnityEngine.UIElementsModule.dll] UnityEngine.UIElements.IMGUIContainer:HandleIMGUIEvent (UnityEngine.Event,UnityEngine.Matrix4x4,UnityEngine.Rect,System.Action,bool)")
};

[Test]
public void LegacyDataTest()
{
Assert.IsTrue(PmipReader.ReadPmipFile(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "pmip_32260_3.txt")));
PmipReader.Sort();

foreach (Tuple<string, string> t in m_legacyData)
{
if (PmipReader.TryGetDescriptionForIp(ulong.Parse(t.Item1, NumberStyles.HexNumber), out string retVal))
{
Assert.AreEqual(t.Item2, retVal);
}
else
Assert.Fail();
}
PmipReader.DisposeStreams();
}

private Tuple<string, string>[] m_legacyModeData =
{
Tuple.Create("000001C4F5DC03FF", "[Assembly-CSharp.dll] SpinMe:FooBar ()"),
Tuple.Create("000001C4F5DC02D3", "[Assembly-CSharp.dll] SpinMe:Foo ()"),
Tuple.Create("000001C4F5DBF6F3", "[Assembly-CSharp.dll] SpinMe:Update ()"),
Tuple.Create("000001C3FF868578", "[mscorlib.dll] (wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)")
};

[Test]
public void LegacyModeDataTest()
{
Assert.IsTrue(PmipReader.ReadPmipFile(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "pmip_31964_3.txt")));
PmipReader.Sort();

foreach (Tuple<string, string> t in m_legacyModeData)
{
if (PmipReader.TryGetDescriptionForIp(ulong.Parse(t.Item1, NumberStyles.HexNumber), out string retVal))
{
Assert.AreEqual(t.Item2, retVal);
}
else
Assert.Fail();
};
PmipReader.DisposeStreams();
}

private Tuple<string, string>[] m_lineNumbersData =
{
Tuple.Create("00000244C7A990BF", "[Assembly-CSharp.dll] SpinMe:FooBar ()"),
Tuple.Create("00000244C7A98F93", "[Assembly-CSharp.dll] SpinMe:Foo () : 32"),
Tuple.Create("00000244C7A98153", "[Assembly-CSharp.dll] SpinMe:Update () : 26"),
Tuple.Create("00000244EC9DF6B8", "[mscorlib.dll] (wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)")
};

[Test]
public void LineNumbersDataTest()
{
Assert.IsTrue(PmipReader.ReadPmipFile(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "line-numbers", "pmip_23672_1_0.txt")));
Assert.IsTrue(PmipReader.ReadPmipFile(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "line-numbers", "pmip_23672_4_1.txt")));
PmipReader.Sort();

foreach (Tuple<string, string> t in m_lineNumbersData)
{
if (PmipReader.TryGetDescriptionForIp(ulong.Parse(t.Item1, NumberStyles.HexNumber), out string retVal))
{
Assert.AreEqual(t.Item2, retVal);
}
else
Assert.Fail($"Couldn't find address: {t.Item1}");
};

PmipReader.DisposeStreams();
}
}
}
6 changes: 6 additions & 0 deletions FuzzyRangeComparer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ struct Range
public ulong Start;
public ulong End;
public string Name;
public string File;

public override string ToString()
{
return $"{File}::{Name} -- IP Range: {Start:X16} -> {End:X16}";
}
}
class FuzzyRangeComparer : IComparer<Range>
{
Expand Down
168 changes: 168 additions & 0 deletions PmipReader.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using UnityMixedCallstack;

namespace UnityMixedCallStack
{
public class PmipReader
{
private static List<Range> _rangesSortedByIp = new List<Range>();
private static List<Range> _legacyRanges = new List<Range>();
private static FuzzyRangeComparer _comparer = new FuzzyRangeComparer();

private struct PmipStreams
{
public FileStream fileStream;
public StreamReader fileStreamReader;

public void Dispose()
{
fileStreamReader.Dispose();
fileStreamReader = null;
fileStream.Dispose();
fileStream = null;
}
}

private static Dictionary<string, PmipStreams> _currentFiles = new Dictionary<string, PmipStreams>();

public static void Sort()
{
_legacyRanges.Sort((r1, r2) => r1.Start.CompareTo(r2.Start));
_rangesSortedByIp.Sort((r1, r2) => r1.Start.CompareTo(r2.Start));
}

public static void DisposeStreams()
{
foreach (PmipStreams streams in _currentFiles.Values)
streams.Dispose();
_currentFiles.Clear();

_rangesSortedByIp.Clear();
_legacyRanges.Clear();
}
public static bool ReadPmipFile(string filePath)
{
var _debugPane = UnityMixedCallstackFilter._debugPane;
#if DEBUG
_debugPane?.OutputString("MIXEDCALLSTACK :: Reading pmip file: " + filePath + "\n");
#endif
//DisposeStreams();

if (!_currentFiles.TryGetValue(filePath, out PmipStreams pmipStreams))
{
pmipStreams = new PmipStreams();
try
{
pmipStreams.fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
pmipStreams.fileStreamReader = new StreamReader(pmipStreams.fileStream);
var versionStr = pmipStreams.fileStreamReader.ReadLine();
const char delimiter = ':';
var tokens = versionStr.Split(delimiter);

if (tokens.Length != 2)
throw new Exception("Failed reading input file " + filePath + ": Incorrect format");

if (!double.TryParse(tokens[1], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var version))
throw new Exception("Failed reading input file " + filePath + ": Incorrect version format");

if (version > 2.0)
throw new Exception("Failed reading input file " + filePath + ": A newer version of UnityMixedCallstacks plugin is required to read this file");
}
catch (Exception ex)
{
_debugPane?.OutputString("MIXEDCALLSTACK :: Unable to read dumped pmip file: " + ex.Message + "\n");
DisposeStreams();
return false;
}
_currentFiles.Add(filePath, pmipStreams);
}

try
{
string line;
int count = 0;
int legacyCount = 0;
while ((line = pmipStreams.fileStreamReader.ReadLine()) != null)
{
const char delemiter = ';';
var tokens = line.Split(delemiter);

//should never happen, but lets be safe and not get array out of bounds if it does
if (tokens.Length == 3 || tokens.Length == 4)
{
string startip = tokens[0];
string endip = tokens[1];
string description = tokens[2];
string file = "";
if (tokens.Length == 4)
file = tokens[3];

if (startip.StartsWith("---"))
{
startip = startip.Remove(0, 3);
}

var startiplong = ulong.Parse(startip, NumberStyles.HexNumber);
var endipint = ulong.Parse(endip, NumberStyles.HexNumber);
if (tokens[0].StartsWith("---"))
{
// legacy stored in new pmip file
_legacyRanges.Add(new Range() { Name = description, File = file, Start = startiplong, End = endipint });
legacyCount++;
}
else
{
Range range = new Range() { Name = description, File = file, Start = startiplong, End = endipint };
#if DEBUG
_debugPane?.OutputString($"MIXEDCALLSTACK :: adding range: {range}\n");
#endif
_rangesSortedByIp.Add(range);
count++;
}
}
}
#if DEBUG
if (count > 0 || legacyCount > 0)
_debugPane?.OutputString($"MIXEDCALLSTACK :: added {count} to map for a total of {_rangesSortedByIp.Count} entries! Added {legacyCount} to legacy map for a total of {_legacyRanges.Count} \n");
#endif
}
catch (Exception ex)
{
_debugPane?.OutputString("MIXEDCALLSTACK :: Unable to read dumped pmip file: " + ex.Message + "\n");
DisposeStreams();
return false;
}
return true;
}

public static bool TryGetDescriptionForIp(ulong ip, out string name)
{
name = string.Empty;

//_debugPane?.OutputString("MIXEDCALLSTACK :: Looking for ip: " + String.Format("{0:X}", ip) + "\n");
var rangeToFindIp = new Range() { Start = ip };
var index = _rangesSortedByIp.BinarySearch(rangeToFindIp, _comparer);

if (index >= 0)
{
//_debugPane?.OutputString("MIXEDCALLSTACK :: SUCCESS!!\n");
name = _rangesSortedByIp[index].Name;
return true;
}

index = _legacyRanges.BinarySearch(rangeToFindIp, _comparer);
if (index >= 0)
{
//_debugPane?.OutputString("MIXEDCALLSTACK :: LEGACY SUCCESS!! "+ String.Format("{0:X}", _legacyRanges[index].Start) +" -- "+ String.Format("{0:X}", _legacyRanges[index].End) + "\n");
name = _legacyRanges[index].Name;
return true;
}

return false;
}

}
}
Loading