Ejemplo n.º 1
0
        private IEnumerable <LinkedList <ClrObject> > PathTo(ObjectSet seen, Dictionary <ulong, LinkedListNode <ClrObject> > knownEndPoints, ClrObject source, ulong target, bool unique, bool parallel, CancellationToken cancelToken)
        {
            seen.Add(source.Address);

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

            LinkedList <PathEntry> path = new LinkedList <PathEntry>();

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

                yield break;
            }

            path.AddLast(new PathEntry()
            {
                Object = source,
                Todo   = GetRefs(seen, knownEndPoints, source, target, unique, parallel, cancelToken, out bool foundTarget, out LinkedListNode <ClrObject> foundEnding)
            });
Ejemplo n.º 2
0
        private IEnumerable <LinkedList <ClrObject> > PathTo(ObjectSet seen, Dictionary <ulong, LinkedListNode <ClrObject> > knownEndPoints, ClrObject source, ulong target, bool unique, bool parallel, CancellationToken cancelToken)
        {
            seen.Add(source.Address);
            bool foundTarget;
            LinkedListNode <ClrObject> foundEnding;

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

            LinkedList <PathEntry> path = new LinkedList <PathEntry>();

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

                yield break;
            }

            path.AddLast(new PathEntry()
            {
                Object = source,
                Todo   = GetRefs(seen, knownEndPoints, source, target, unique, parallel, cancelToken, out foundTarget, out foundEnding)
            });

            // Did the 'start' object point directly to 'end'?  If so, early out.
            if (foundTarget)
            {
                path.AddLast(new PathEntry()
                {
                    Object = ClrObject.Create(target, _heap.GetObjectType(target))
                });
                yield return(GetResult(knownEndPoints, path, null, target));
            }
            else if (foundEnding != null)
            {
                yield return(GetResult(knownEndPoints, path, foundEnding, target));
            }

            while (path.Count > 0)
            {
                cancelToken.ThrowIfCancellationRequested();

                TraceFullPath(null, path);
                var last = path.Last.Value;

                if (last.Todo.Count == 0)
                {
                    // We've exhausted all children and didn't find the target.  Remove this node
                    // and continue.
                    path.RemoveLast();
                }
                else
                {
                    // We loop here in case we encounter an object we've already processed (or if
                    // we can't get an object's type...inconsistent heap happens sometimes).
                    do
                    {
                        cancelToken.ThrowIfCancellationRequested();
                        ClrObject next = last.Todo.Pop();

                        // Now that we are in the process of adding 'next' to the path, don't ever consider
                        // this object in the future.
                        if (!seen.Add(next.Address))
                        {
                            continue;
                        }

                        // We should never reach the 'end' here, as we always check if we found the target
                        // value when adding refs below.
                        Debug.Assert(next.Address != target);

                        PathEntry nextPathEntry = new PathEntry()
                        {
                            Object = next,
                            Todo   = GetRefs(seen, knownEndPoints, next, target, unique, parallel, cancelToken, out foundTarget, out foundEnding)
                        };

                        path.AddLast(nextPathEntry);

                        // If we found the target object while enumerating refs of the current object, we are done.
                        if (foundTarget)
                        {
                            path.AddLast(new PathEntry()
                            {
                                Object = ClrObject.Create(target, _heap.GetObjectType(target))
                            });
                            TraceFullPath("FoundTarget", path);

                            yield return(GetResult(knownEndPoints, path, null, target));

                            path.RemoveLast();
                            path.RemoveLast();
                        }
                        else if (foundEnding != null)
                        {
                            TraceFullPath(path, foundEnding);
                            yield return(GetResult(knownEndPoints, path, foundEnding, target));

                            path.RemoveLast();
                        }

                        // Now that we've added a new entry to 'path', break out of the do/while that's looping through Todo.
                        break;
                    }while (last.Todo.Count > 0);
                }
            }
        }
Ejemplo n.º 3
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)
            });