Esempio n. 1
0
        /// <summary>
        ///     Converts the specified object set.
        /// </summary>
        /// <param name="objectSet">The object set.</param>
        /// <returns>IObjectSet.</returns>
        public IObjectSet Convert(ClrMd.ObjectSet objectSet)
        {
            if (objectSet == null)
            {
                return(null);
            }
            var item = new ObjectSetAdapter(this, objectSet);

            return(Cache.GetOrAdd <IObjectSet>(objectSet, () => item, () => item.Setup()));
        }
Esempio n. 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="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);
        }
Esempio n. 3
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);
                }
            }
        }
Esempio n. 4
0
        private IEnumerable <LinkedList <ClrObject> > PathsTo(
            ObjectSet deadEnds,
            Dictionary <ulong, LinkedListNode <ClrObject> >?knownEndPoints,
            ClrObject source,
            ulong target,
            bool unique,
            CancellationToken cancellationToken)
        {
            /* knownEndPoints: A set of objects that are known to point to the target, once we see one of
             *                 these, we can end the search early and return the current path plus the
             *                 nodes there.
             *
             * deadEnds  When unique == false, these are Objects we've fully processed and know doesn't
             *           point to the target object.  When unique == true, deadEnds are updated to include
             *           objects that other threads are processing, ensuring we don't yield duplicate
             *           roots. Note that we must be careful to ONLY add objects to this list when we are
             *           sure we've exhausted all potential paths because parallel threads use this same
             *           set.
             *
             * path: The current path we are considering.  As we walk through objects in the rooting chain,
             *       we will add a new node to this list with the object in question and create a stack of
             *       all objects it points to.  This is a depth-first search, so we will search as deeply
             *       as we can, and pop off objects from path as soon as the stack of references is empty.
             *       If we find that the reference stack is empty and we didn't find the target/known end
             *       point, then we'll mark the object in deadEnds.
             *
             * processing: The list of objects we are currently considering on this thread.  Since objects
             *             can contain circular references, we need to know we shouldn't re-consider objects
             *             we encounter while walking objects.
             *
             * It's important to note that multiple threads may race and consider the same path at the same
             * time.  This is expected (and similar to how the GC operates).  The thing we have to make sure
             * of is that we only add objects to knownEndPoints/deadEnds when we are 100% sure of the result
             * because it will directly affect other threads walking these paths.
             */

            HashSet <ulong> processing = new HashSet <ulong>()
            {
                source.Address
            };
            LinkedList <PathEntry> path = new LinkedList <PathEntry>();

            if (knownEndPoints != null)
            {
                lock (knownEndPoints)
                {
                    if (knownEndPoints.TryGetValue(source.Address, out LinkedListNode <ClrObject>?ending))
                    {
                        if (!unique || ending.Value.Address == target)
                        {
                            yield return(GetResult(ending));
                        }

                        yield break;
                    }
                }
            }

            if (unique && !deadEnds.Add(source.Address))
            {
                yield break;
            }

            if (source.Type is null)
            {
                yield break;
            }

            if (source.Address == target)
            {
                path.AddLast(new PathEntry {
                    Object = source
                });
                yield return(GetResult());

                yield break;
            }

            path.AddLast(
                new PathEntry
            {
                Object = source,
                Todo   = GetRefs(source, out bool foundTarget, out LinkedListNode <ClrObject>?foundEnding)
            });