Skip to content

Commit e01e44e

Browse files
committed
Refactoring and fixing of HeapExplorer
* Introduced the `Option<A>` type to represent values that might not be there. * Replaced values representing failure (like `-1`) with `Option` to indicate the possibility of failure. * Replaced possibly-invalid types with `Option`s. Now if you if have an instance - it's guaranteed to be valid. * Replaced magic values (like using positive integers for managed object indexes and negative integers for static object indexes) with proper semantic types to help with code clarity. * Replaced dangerous numeric casts that could under/overflow with typesafe alternatives (`PInt` positive 32bit integer type) that guarantee no such things can happen. * Replaced instances of `List` which were being used as stacks with `Stack`. * Replaced as many fields in objects as possible with readonly ones to indicate that we have no intent of changing them after the object is constructed. This is a part of 'objects should stay valid after construction' philosophy. * Added logging for potentially dangerous operations (like int -> uint). * Replaced primitive loop guards with cycle tracking that tracks seen objects. This eliminates the false positives and errors out sooner if we actually have an error. * Replaced places where things got cancelled silently with emitting warnings / errors. Failures should never be silent. * Fixed algorithm that checks if a type contains reference types: it didn't account for nested value-types which eventually have reference types in them and only looked for reference types in the first level. * Fixed bad lookups in `PackedCoreTypes`. Also made `PackedCoreTypes` to be fully initialized, without any missing entries. * Added a warning about the inability to crawl the `[ThreadStatic]` variables. * Improved search speed by: * caching search results. * downcasing everything before the search and then using fast ordinal string comparisons instead of slower `OrdinalIgnoreCase` comparisons. * Improved documentation by adding new docstrings and converting existing docstrings into proper XMLDocs. Now IDEs and tools can properly render the documentation. * Rewrote a part of code using C# 7.3 (supported since Unity 2019) to enhance readability. * bumped version to 4.1.2 * Added capability to copy the root path as text in "Find GC roots" window. * Added information about source fields that are pointing to the object in "Find GC roots" window. * Added progress reporting to Unity when a memory snapshot is being converted from the Unity format to our format.
1 parent f7b5567 commit e01e44e

File tree

77 files changed

+4005
-3121
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

77 files changed

+4005
-3121
lines changed

Editor/Scripts/AbstractTreeView.cs

+55-14
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,11 @@
55

66
using System;
77
using System.Collections.Generic;
8+
using HeapExplorer.Utilities;
89
using UnityEngine;
910
using UnityEditor.IMGUI.Controls;
1011
using UnityEditor;
12+
using static HeapExplorer.Utilities.Option;
1113

1214
namespace HeapExplorer
1315
{
@@ -36,7 +38,6 @@ public HeapExplorerWindow window
3638
IList<int> m_Expanded = new List<int>(32);
3739
TreeViewItem m_Tree;
3840
string[] m_SearchCache = new string[32];
39-
System.Text.StringBuilder m_SearchBuilder = new System.Text.StringBuilder();
4041

4142
public AbstractTreeView(HeapExplorerWindow window, string editorPrefsKey, TreeViewState state)
4243
: base(state)
@@ -301,23 +302,36 @@ protected override bool DoesItemMatchSearch(TreeViewItem item, string search)
301302
var i = item as AbstractTreeViewItem;
302303
if (i != null)
303304
{
304-
int searchCount;
305-
string type;
306-
string label;
307-
i.GetItemSearchString(m_SearchCache, out searchCount, out type, out label);
305+
if (!i.m_MaybeCachedItemSearchString.valueOut(out var searchString)) {
306+
int searchCount;
307+
string type;
308+
string label;
309+
i.GetItemSearchString(m_SearchCache, out searchCount, out type, out label);
310+
311+
var names = new List<string>(capacity: searchCount);
312+
for (var n=0; n < searchCount; ++n)
313+
{
314+
var str = m_SearchCache[n];
315+
if (!string.IsNullOrEmpty(str)) names.Add(str.ToLowerInvariant());
316+
}
308317

309-
if (!m_Search.IsTypeMatch(type) || !m_Search.IsLabelMatch(label))
310-
return false;
318+
searchString = new AbstractTreeViewItem.Cache(
319+
lowerCasedNames: names.ToArray(), type: type, label: label
320+
);
321+
i.m_MaybeCachedItemSearchString = Some(searchString);
322+
}
311323

312-
m_SearchBuilder.Length = 0;
313-
for (var n=0; n < searchCount; ++n)
314-
{
315-
m_SearchBuilder.Append(m_SearchCache[n]);
316-
m_SearchBuilder.Append(" ");
324+
if (!m_Search.IsTypeMatch(searchString.type) || !m_Search.IsLabelMatch(searchString.label)) {
325+
return false;
317326
}
318-
m_SearchBuilder.Append("\0");
327+
else {
328+
// ReSharper disable once LoopCanBeConvertedToQuery
329+
foreach (var lowerCasedName in searchString.lowerCasedNames) {
330+
if (m_Search.IsNameMatch(lowerCasedName)) return true;
331+
}
319332

320-
return m_Search.IsNameMatch(m_SearchBuilder.ToString());
333+
return false;
334+
}
321335
}
322336

323337
return base.DoesItemMatchSearch(item, search);
@@ -461,7 +475,34 @@ public virtual void GetItemSearchString(string[] target, out int count, out stri
461475
label = null;
462476
}
463477

478+
/// <summary>
479+
/// Results of <see cref="GetItemSearchString"/> are cached here to avoid re-computation. If this is `None`,
480+
/// invoke the <see cref="GetItemSearchString"/> and store the result here.
481+
/// </summary>
482+
public Option<Cache> m_MaybeCachedItemSearchString;
483+
464484
public abstract void OnGUI(Rect position, int column);
485+
486+
public sealed class Cache {
487+
/// <summary>
488+
/// Parameters for <see cref="SearchTextParser.Result.IsNameMatch"/>.
489+
/// <para/>
490+
/// The search will match if any of these matches.
491+
/// </summary>
492+
public readonly string[] lowerCasedNames;
493+
494+
/// <summary>Parameter for <see cref="SearchTextParser.Result.IsTypeMatch"/>.</summary>
495+
public readonly string type;
496+
497+
/// <summary>Parameter for <see cref="SearchTextParser.Result.IsLabelMatch"/>.</summary>
498+
public readonly string label;
499+
500+
public Cache(string[] lowerCasedNames, string type, string label) {
501+
this.lowerCasedNames = lowerCasedNames;
502+
this.type = type;
503+
this.label = label;
504+
}
505+
}
465506
}
466507

467508
public static class TreeViewUtility

Editor/Scripts/AbstractView.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ protected string GetPrefsKey(Expression<Func<object>> exp)
8181
body = ubody.Operand as MemberExpression;
8282
}
8383

84-
return string.Format("HeapExplorer.{0}.{1}", editorPrefsKey, body.Member.Name);
84+
return $"HeapExplorer.{editorPrefsKey}.{body.Member.Name}";
8585
}
8686

8787
public HeapExplorerView()

Editor/Scripts/CompareSnapshotsView/CompareSnapshotsControl.cs

+9-6
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
//
55
using System.Collections;
66
using System.Collections.Generic;
7+
using HeapExplorer.Utilities;
78
using UnityEditor;
89
using UnityEditor.IMGUI.Controls;
910
using UnityEngine;
@@ -139,7 +140,7 @@ void BuildMemoryTree(PackedMemorySnapshot[] snapshots, ref int uniqueId, TreeVie
139140

140141
foreach (var section in snapshot.managedHeapSections)
141142
{
142-
parent.size[k] += (long)section.size;
143+
parent.size[k] += section.size;
143144
}
144145

145146
parent.count[k] += snapshot.managedHeapSections.Length;
@@ -164,7 +165,9 @@ void BuildGCHandleTree(PackedMemorySnapshot[] snapshots, ref int uniqueId, TreeV
164165

165166
var snapshot = snapshots[k];
166167

167-
parent.size[k] += snapshot.gcHandles.Length * snapshot.virtualMachineInformation.pointerSize;
168+
parent.size[k] += (
169+
snapshot.gcHandles.Length * snapshot.virtualMachineInformation.pointerSize.sizeInBytes()
170+
).ToUIntClamped();
168171
parent.count[k] += snapshot.gcHandles.Length;
169172
}
170173
}
@@ -303,14 +306,14 @@ void BuildManagedTree(PackedMemorySnapshot[] snapshots, ref int uniqueId, TreeVi
303306

304307
class Item : AbstractTreeViewItem
305308
{
306-
public long[] size = new long[2];
309+
public ulong[] size = new ulong[2];
307310
public long[] count = new long[2];
308311

309312
public long sizeDiff
310313
{
311314
get
312315
{
313-
return size[1] - size[0];
316+
return size[1].ToLongClamped() - size[0].ToLongClamped();
314317
}
315318
}
316319

@@ -357,11 +360,11 @@ public override void OnGUI(Rect position, int column)
357360
break;
358361

359362
case EColumn.SizeA:
360-
HeEditorGUI.Size(position, size[0]);
363+
HeEditorGUI.Size(position, size[0].ToLongClamped());
361364
break;
362365

363366
case EColumn.SizeB:
364-
HeEditorGUI.Size(position, size[1]);
367+
HeEditorGUI.Size(position, size[1].ToLongClamped());
365368
break;
366369

367370
case EColumn.SizeDiff:

0 commit comments

Comments
 (0)