Skip to content

Commit da539d2

Browse files
authored
Merge pull request #20 from Unity-Technologies/line-numbers
Line number and multiple domain support
2 parents abb5a72 + 1a3e340 commit da539d2

14 files changed

+152871
-104
lines changed

.idea/.idea.UnityMixedCallstack/.idea/.gitignore

Lines changed: 13 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net472</TargetFramework>
5+
6+
<IsPackable>false</IsPackable>
7+
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
8+
<PlatformTarget>x64</PlatformTarget>
9+
<Platforms>AnyCPU;x64</Platforms>
10+
<VSSdkPath Condition="'$(VisualStudioVersion.Contains(`17`))'">$(ProgramFiles)\Microsoft Visual Studio\2022\Professional\VSSDK\</VSSdkPath>
11+
<VSSdkPath Condition="'$(VisualStudioVersion.Contains(`16`))'">$(MSBuildProgramFiles32)\Microsoft Visual Studio\2019\Professional\VSSDK\</VSSdkPath>
12+
<ConcordSDKDir>$(VSSdkPath)VisualStudioIntegration\</ConcordSDKDir>
13+
</PropertyGroup>
14+
15+
<ItemGroup>
16+
<None Remove="pmip_32260_3.txt" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<PackageReference Include="Microsoft.TestPlatform" Version="17.5.0" />
21+
<PackageReference Include="MSTest.TestAdapter" Version="3.0.2" />
22+
<PackageReference Include="NUnit" Version="3.13.3" />
23+
<PackageReference Include="NUnit3TestAdapter" Version="4.4.0" />
24+
</ItemGroup>
25+
26+
<Import Project="$(ConcordSDKDir)Tools\bin\Microsoft.VSSDK.Debugger.VSDConfigTool.targets" />
27+
28+
<ItemGroup>
29+
<ProjectReference Include="..\UnityMixedCallStack.csproj" />
30+
</ItemGroup>
31+
32+
<ItemGroup>
33+
<None Update="TestData\line-numbers\pmip_23672_1_0.txt">
34+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
35+
</None>
36+
<None Update="TestData\line-numbers\pmip_23672_4_1.txt">
37+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
38+
</None>
39+
<None Update="TestData\pmip_31964_3.txt">
40+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
41+
</None>
42+
<None Update="TestData\pmip_32260_3.txt">
43+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
44+
</None>
45+
<None Update="TestData\pmip_44920_4.txt">
46+
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
47+
</None>
48+
</ItemGroup>
49+
50+
</Project>

CallstackTestProj/TestData/line-numbers/pmip_23672_1_0.txt

Lines changed: 208 additions & 0 deletions
Large diffs are not rendered by default.

CallstackTestProj/TestData/line-numbers/pmip_23672_4_1.txt

Lines changed: 95270 additions & 0 deletions
Large diffs are not rendered by default.

CallstackTestProj/TestData/pmip_31964_3.txt

Lines changed: 15699 additions & 0 deletions
Large diffs are not rendered by default.

CallstackTestProj/TestData/pmip_32260_3.txt

Lines changed: 20617 additions & 0 deletions
Large diffs are not rendered by default.

CallstackTestProj/UnitTest1.cs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
using NUnit.Framework;
2+
using System;
3+
using System.Collections.Generic;
4+
using System.Globalization;
5+
using System.IO;
6+
using UnityMixedCallStack;
7+
8+
namespace CallstackTestProj
9+
{
10+
[TestFixture]
11+
public class Tests
12+
{
13+
private Tuple<string, string>[] m_legacyData =
14+
{
15+
Tuple.Create("000001C44A1C81D7", "[UnityEditor.CoreModule.dll] (wrapper managed-to-native) UnityEditor.EditorGUIUtility:RenderPlayModeViewCamerasInternal_Injected (UnityEngine.RenderTexture,int,UnityEngine.Vector2&,bool,bool)"),
16+
Tuple.Create("000001C44A1C8083", "[UnityEditor.CoreModule.dll] UnityEditor.EditorGUIUtility:RenderPlayModeViewCamerasInternal (UnityEngine.RenderTexture,int,UnityEngine.Vector2,bool,bool)"),
17+
Tuple.Create("000001C44A1C52EB", "[UnityEditor.CoreModule.dll] UnityEditor.PlayModeView:RenderView (UnityEngine.Vector2,bool)"),
18+
Tuple.Create("000001C449F19A23", "[UnityEditor.CoreModule.dll] UnityEditor.GameView:OnGUI ()"),
19+
Tuple.Create("000001C449F12BF8", "[UnityEditor.CoreModule.dll] UnityEditor.HostView:InvokeOnGUI (UnityEngine.Rect)"),
20+
Tuple.Create("000001C449F12793", "[UnityEditor.CoreModule.dll] UnityEditor.DockArea:DrawView (UnityEngine.Rect)"),
21+
Tuple.Create("000001C449EF1353", "[UnityEditor.CoreModule.dll] UnityEditor.DockArea:OldOnGUI ()"),
22+
Tuple.Create("000001C449EBCCE9", "[UnityEngine.UIElementsModule.dll] UnityEngine.UIElements.IMGUIContainer:DoOnGUI (UnityEngine.Event,UnityEngine.Matrix4x4,UnityEngine.Rect,bool,UnityEngine.Rect,System.Action,bool)"),
23+
Tuple.Create("000001C449EBAC13", "[UnityEngine.UIElementsModule.dll] UnityEngine.UIElements.IMGUIContainer:HandleIMGUIEvent (UnityEngine.Event,UnityEngine.Matrix4x4,UnityEngine.Rect,System.Action,bool)")
24+
};
25+
26+
[Test]
27+
public void LegacyDataTest()
28+
{
29+
Assert.IsTrue(PmipReader.ReadPmipFile(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "pmip_32260_3.txt")));
30+
PmipReader.Sort();
31+
32+
foreach (Tuple<string, string> t in m_legacyData)
33+
{
34+
if (PmipReader.TryGetDescriptionForIp(ulong.Parse(t.Item1, NumberStyles.HexNumber), out string retVal))
35+
{
36+
Assert.AreEqual(t.Item2, retVal);
37+
}
38+
else
39+
Assert.Fail();
40+
}
41+
PmipReader.DisposeStreams();
42+
}
43+
44+
private Tuple<string, string>[] m_legacyModeData =
45+
{
46+
Tuple.Create("000001C4F5DC03FF", "[Assembly-CSharp.dll] SpinMe:FooBar ()"),
47+
Tuple.Create("000001C4F5DC02D3", "[Assembly-CSharp.dll] SpinMe:Foo ()"),
48+
Tuple.Create("000001C4F5DBF6F3", "[Assembly-CSharp.dll] SpinMe:Update ()"),
49+
Tuple.Create("000001C3FF868578", "[mscorlib.dll] (wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)")
50+
};
51+
52+
[Test]
53+
public void LegacyModeDataTest()
54+
{
55+
Assert.IsTrue(PmipReader.ReadPmipFile(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "pmip_31964_3.txt")));
56+
PmipReader.Sort();
57+
58+
foreach (Tuple<string, string> t in m_legacyModeData)
59+
{
60+
if (PmipReader.TryGetDescriptionForIp(ulong.Parse(t.Item1, NumberStyles.HexNumber), out string retVal))
61+
{
62+
Assert.AreEqual(t.Item2, retVal);
63+
}
64+
else
65+
Assert.Fail();
66+
};
67+
PmipReader.DisposeStreams();
68+
}
69+
70+
private Tuple<string, string>[] m_lineNumbersData =
71+
{
72+
Tuple.Create("00000244C7A990BF", "[Assembly-CSharp.dll] SpinMe:FooBar ()"),
73+
Tuple.Create("00000244C7A98F93", "[Assembly-CSharp.dll] SpinMe:Foo () : 32"),
74+
Tuple.Create("00000244C7A98153", "[Assembly-CSharp.dll] SpinMe:Update () : 26"),
75+
Tuple.Create("00000244EC9DF6B8", "[mscorlib.dll] (wrapper runtime-invoke) object:runtime_invoke_void__this__ (object,intptr,intptr,intptr)")
76+
};
77+
78+
[Test]
79+
public void LineNumbersDataTest()
80+
{
81+
Assert.IsTrue(PmipReader.ReadPmipFile(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "line-numbers", "pmip_23672_1_0.txt")));
82+
Assert.IsTrue(PmipReader.ReadPmipFile(Path.Combine(TestContext.CurrentContext.TestDirectory, "TestData", "line-numbers", "pmip_23672_4_1.txt")));
83+
PmipReader.Sort();
84+
85+
foreach (Tuple<string, string> t in m_lineNumbersData)
86+
{
87+
if (PmipReader.TryGetDescriptionForIp(ulong.Parse(t.Item1, NumberStyles.HexNumber), out string retVal))
88+
{
89+
Assert.AreEqual(t.Item2, retVal);
90+
}
91+
else
92+
Assert.Fail($"Couldn't find address: {t.Item1}");
93+
};
94+
95+
PmipReader.DisposeStreams();
96+
}
97+
}
98+
}

FuzzyRangeComparer.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ struct Range
77
public ulong Start;
88
public ulong End;
99
public string Name;
10+
public string File;
11+
12+
public override string ToString()
13+
{
14+
return $"{File}::{Name} -- IP Range: {Start:X16} -> {End:X16}";
15+
}
1016
}
1117
class FuzzyRangeComparer : IComparer<Range>
1218
{

PmipReader.cs

Lines changed: 168 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,168 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Globalization;
4+
using System.IO;
5+
using UnityMixedCallstack;
6+
7+
namespace UnityMixedCallStack
8+
{
9+
public class PmipReader
10+
{
11+
private static List<Range> _rangesSortedByIp = new List<Range>();
12+
private static List<Range> _legacyRanges = new List<Range>();
13+
private static FuzzyRangeComparer _comparer = new FuzzyRangeComparer();
14+
15+
private struct PmipStreams
16+
{
17+
public FileStream fileStream;
18+
public StreamReader fileStreamReader;
19+
20+
public void Dispose()
21+
{
22+
fileStreamReader.Dispose();
23+
fileStreamReader = null;
24+
fileStream.Dispose();
25+
fileStream = null;
26+
}
27+
}
28+
29+
private static Dictionary<string, PmipStreams> _currentFiles = new Dictionary<string, PmipStreams>();
30+
31+
public static void Sort()
32+
{
33+
_legacyRanges.Sort((r1, r2) => r1.Start.CompareTo(r2.Start));
34+
_rangesSortedByIp.Sort((r1, r2) => r1.Start.CompareTo(r2.Start));
35+
}
36+
37+
public static void DisposeStreams()
38+
{
39+
foreach (PmipStreams streams in _currentFiles.Values)
40+
streams.Dispose();
41+
_currentFiles.Clear();
42+
43+
_rangesSortedByIp.Clear();
44+
_legacyRanges.Clear();
45+
}
46+
public static bool ReadPmipFile(string filePath)
47+
{
48+
var _debugPane = UnityMixedCallstackFilter._debugPane;
49+
#if DEBUG
50+
_debugPane?.OutputString("MIXEDCALLSTACK :: Reading pmip file: " + filePath + "\n");
51+
#endif
52+
//DisposeStreams();
53+
54+
if (!_currentFiles.TryGetValue(filePath, out PmipStreams pmipStreams))
55+
{
56+
pmipStreams = new PmipStreams();
57+
try
58+
{
59+
pmipStreams.fileStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite | FileShare.Delete);
60+
pmipStreams.fileStreamReader = new StreamReader(pmipStreams.fileStream);
61+
var versionStr = pmipStreams.fileStreamReader.ReadLine();
62+
const char delimiter = ':';
63+
var tokens = versionStr.Split(delimiter);
64+
65+
if (tokens.Length != 2)
66+
throw new Exception("Failed reading input file " + filePath + ": Incorrect format");
67+
68+
if (!double.TryParse(tokens[1], NumberStyles.AllowDecimalPoint, CultureInfo.InvariantCulture, out var version))
69+
throw new Exception("Failed reading input file " + filePath + ": Incorrect version format");
70+
71+
if (version > 2.0)
72+
throw new Exception("Failed reading input file " + filePath + ": A newer version of UnityMixedCallstacks plugin is required to read this file");
73+
}
74+
catch (Exception ex)
75+
{
76+
_debugPane?.OutputString("MIXEDCALLSTACK :: Unable to read dumped pmip file: " + ex.Message + "\n");
77+
DisposeStreams();
78+
return false;
79+
}
80+
_currentFiles.Add(filePath, pmipStreams);
81+
}
82+
83+
try
84+
{
85+
string line;
86+
int count = 0;
87+
int legacyCount = 0;
88+
while ((line = pmipStreams.fileStreamReader.ReadLine()) != null)
89+
{
90+
const char delemiter = ';';
91+
var tokens = line.Split(delemiter);
92+
93+
//should never happen, but lets be safe and not get array out of bounds if it does
94+
if (tokens.Length == 3 || tokens.Length == 4)
95+
{
96+
string startip = tokens[0];
97+
string endip = tokens[1];
98+
string description = tokens[2];
99+
string file = "";
100+
if (tokens.Length == 4)
101+
file = tokens[3];
102+
103+
if (startip.StartsWith("---"))
104+
{
105+
startip = startip.Remove(0, 3);
106+
}
107+
108+
var startiplong = ulong.Parse(startip, NumberStyles.HexNumber);
109+
var endipint = ulong.Parse(endip, NumberStyles.HexNumber);
110+
if (tokens[0].StartsWith("---"))
111+
{
112+
// legacy stored in new pmip file
113+
_legacyRanges.Add(new Range() { Name = description, File = file, Start = startiplong, End = endipint });
114+
legacyCount++;
115+
}
116+
else
117+
{
118+
Range range = new Range() { Name = description, File = file, Start = startiplong, End = endipint };
119+
#if DEBUG
120+
_debugPane?.OutputString($"MIXEDCALLSTACK :: adding range: {range}\n");
121+
#endif
122+
_rangesSortedByIp.Add(range);
123+
count++;
124+
}
125+
}
126+
}
127+
#if DEBUG
128+
if (count > 0 || legacyCount > 0)
129+
_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");
130+
#endif
131+
}
132+
catch (Exception ex)
133+
{
134+
_debugPane?.OutputString("MIXEDCALLSTACK :: Unable to read dumped pmip file: " + ex.Message + "\n");
135+
DisposeStreams();
136+
return false;
137+
}
138+
return true;
139+
}
140+
141+
public static bool TryGetDescriptionForIp(ulong ip, out string name)
142+
{
143+
name = string.Empty;
144+
145+
//_debugPane?.OutputString("MIXEDCALLSTACK :: Looking for ip: " + String.Format("{0:X}", ip) + "\n");
146+
var rangeToFindIp = new Range() { Start = ip };
147+
var index = _rangesSortedByIp.BinarySearch(rangeToFindIp, _comparer);
148+
149+
if (index >= 0)
150+
{
151+
//_debugPane?.OutputString("MIXEDCALLSTACK :: SUCCESS!!\n");
152+
name = _rangesSortedByIp[index].Name;
153+
return true;
154+
}
155+
156+
index = _legacyRanges.BinarySearch(rangeToFindIp, _comparer);
157+
if (index >= 0)
158+
{
159+
//_debugPane?.OutputString("MIXEDCALLSTACK :: LEGACY SUCCESS!! "+ String.Format("{0:X}", _legacyRanges[index].Start) +" -- "+ String.Format("{0:X}", _legacyRanges[index].End) + "\n");
160+
name = _legacyRanges[index].Name;
161+
return true;
162+
}
163+
164+
return false;
165+
}
166+
167+
}
168+
}

0 commit comments

Comments
 (0)