/// <summary> /// Enumerates GCRoots of a given object. Similar to !gcroot. /// </summary> /// <param name="target">The target object to search for GC rooting.</param> /// <param name="unique">Whether to only return fully unique paths.</param> /// <param name="cancelToken">A cancellation token to stop enumeration.</param> /// <returns>An enumeration of all GC roots found for target.</returns> public IEnumerable <RootPath> EnumerateGCRoots(ulong target, bool unique, CancellationToken cancelToken) { _heap.BuildDependentHandleMap(cancelToken); long totalObjects = _heap.TotalObjects; long lastObjectReported = 0; bool parallel = AllowParallelSearch && IsFullyCached && _maxTasks > 0; Task <Tuple <LinkedList <ClrObject>, ClrRoot> >[] tasks; ObjectSet processedObjects; Dictionary <ulong, LinkedListNode <ClrObject> > knownEndPoints = new Dictionary <ulong, LinkedListNode <ClrObject> >(); if (parallel) { processedObjects = new ParallelObjectSet(_heap); } else { processedObjects = new ObjectSet(_heap); } int initial = 0; tasks = new Task <Tuple <LinkedList <ClrObject>, ClrRoot> > [_maxTasks]; foreach (ClrHandle handle in _heap.EnumerateStrongHandles()) { Debug.Assert(handle.HandleType != HandleType.Dependent); Debug.Assert(handle.Object != 0); if (processedObjects.Contains(handle.Object)) { continue; } Debug.Assert(_heap.GetObjectType(handle.Object) == handle.Type); if (parallel) { var task = PathToParallel(processedObjects, knownEndPoints, handle, target, unique, cancelToken); if (initial < tasks.Length) { tasks[initial++] = task; } else { int i = Task.WaitAny(tasks); var completed = tasks[i]; tasks[i] = task; if (completed.Result.Item1 != null) { yield return new RootPath() { Root = completed.Result.Item2, Path = completed.Result.Item1.ToArray() } } ; } } else { var path = PathTo(processedObjects, knownEndPoints, new ClrObject(handle.Object, handle.Type), target, unique, false, cancelToken).FirstOrDefault(); if (path != null) { yield return new RootPath() { Root = GetHandleRoot(handle), Path = path.ToArray() } } ; } ReportObjectCount(processedObjects.Count, ref lastObjectReported, totalObjects); } foreach (ClrRoot root in _heap.EnumerateStackRoots()) { if (!processedObjects.Contains(root.Object)) { Debug.Assert(_heap.GetObjectType(root.Object) == root.Type); if (parallel) { var task = PathToParallel(processedObjects, knownEndPoints, root, target, unique, cancelToken); if (initial < tasks.Length) { tasks[initial++] = task; } else { int i = Task.WaitAny(tasks); var completed = tasks[i]; tasks[i] = task; if (completed.Result.Item1 != null) { yield return new RootPath() { Root = completed.Result.Item2, Path = completed.Result.Item1.ToArray() } } ; } } else { var path = PathTo(processedObjects, knownEndPoints, new ClrObject(root.Object, root.Type), target, unique, false, cancelToken).FirstOrDefault(); if (path != null) { yield return new RootPath() { Root = root, Path = path.ToArray() } } ; } ReportObjectCount(processedObjects.Count, ref lastObjectReported, totalObjects); } } foreach (Tuple <LinkedList <ClrObject>, ClrRoot> result in WhenEach(tasks)) { ReportObjectCount(processedObjects.Count, ref lastObjectReported, totalObjects); yield return(new RootPath() { Root = result.Item2, Path = result.Item1.ToArray() }); } ReportObjectCount(totalObjects, ref lastObjectReported, totalObjects); }