Exemple #1
0
        /// <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);
        }
Exemple #2
0
        /// <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="maxDegreeOfParallelism">The number of threads this class is allowed to use to calculate the result.
        /// Setting this to 1 will cause the algorithm to run on the current thread.</param>
        /// <param name="roots">The roots to consider.  You can pass ClrMD.</param>
        /// <param name="cancellationToken">A cancellation token to stop enumeration.</param>
        /// <returns>An enumeration of all GC roots found for target.</returns>
        public IEnumerable <GCRootPath> EnumerateGCRoots(ulong target, bool unique, int maxDegreeOfParallelism, IEnumerable <IClrRoot> roots, CancellationToken cancellationToken = default)
        {
            if (roots is null)
            {
                throw new ArgumentNullException(nameof(roots));
            }

            bool parallel = Heap.Runtime.IsThreadSafe && maxDegreeOfParallelism > 1;

            Dictionary <ulong, LinkedListNode <ClrObject> > knownEndPoints = new Dictionary <ulong, LinkedListNode <ClrObject> >()
            {
                { target, new LinkedListNode <ClrObject>(Heap.GetObject(target)) }
            };

            if (!parallel)
            {
                int count = 0;

                ObjectSet processedObjects = new ObjectSet(Heap);
                foreach (IClrRoot root in roots)
                {
                    LinkedList <ClrObject> path = PathsTo(processedObjects, knownEndPoints, root.Object, target, unique, cancellationToken).FirstOrDefault();
                    if (path != null)
                    {
                        yield return(new GCRootPath(root, path.ToImmutableArray()));
                    }

                    if (count != processedObjects.Count)
                    {
                        count = processedObjects.Count;
                        ProgressUpdated?.Invoke(this, count);
                    }
                }
            }
            else
            {
                ParallelObjectSet            processedObjects = new ParallelObjectSet(Heap);
                ConcurrentQueue <GCRootPath> results          = new ConcurrentQueue <GCRootPath>();
                using BlockingCollection <IClrRoot?> queue    = new BlockingCollection <IClrRoot?>();

                Thread[] threads = new Thread[Math.Min(maxDegreeOfParallelism, Environment.ProcessorCount)];
                for (int i = 0; i < threads.Length; i++)
                {
                    threads[i] = new Thread(() => WorkerThread(queue, results, processedObjects, knownEndPoints, target, all: true, unique, cancellationToken))
                    {
                        Name = "GCRoot Worker Thread"
                    };
                    threads[i].Start();
                }

                foreach (IClrRoot root in roots)
                {
                    queue.Add(root);
                }

                // Add one sentinal value for every thread
                for (int i = 0; i < threads.Length; i++)
                {
                    queue.Add(null);
                }

                int count = 0;

                // Worker threads end when they have run out of roots to process.  While we are waiting for them to exit, yield return
                // any results they've found.  We'll use a 100 msec timeout because processing roots is slooooow and finding a root is
                // rare.  There's no reason to check these results super quickly and starve worker threads.
                for (int i = 0; i < threads.Length; i++)
                {
                    while (!threads[i].Join(100))
                    {
                        while (results.TryDequeue(out GCRootPath result))
                        {
                            yield return(result);
                        }
                    }

                    if (count != processedObjects.Count)
                    {
                        count = processedObjects.Count;
                        ProgressUpdated?.Invoke(this, count);
                    }
                }

                // We could have raced to put an object in the results queue while joining the last thread, so we need to drain the
                // results queue one last time.
                while (results.TryDequeue(out GCRootPath result))
                {
                    yield return(result);
                }

                if (count != processedObjects.Count)
                {
                    count = processedObjects.Count;
                    ProgressUpdated?.Invoke(this, count);
                }
            }
        }