Exemple #1
0
        public override ClrObject GetNextObject(ClrObject obj)
        {
            if (obj.IsNull)
            {
                return(obj);
            }

            uint minObjSize = (uint)_clr.PointerSize * 3;

            ClrType type = obj.Type;

            if (type == null)
            {
                return(new ClrObject(0, _heap.NullType));
            }

            ulong size = obj.Size;

            size = Align(size, _large);
            if (size < minObjSize)
            {
                size = minObjSize;
            }

            // Move to the next object
            ulong addr = obj.Address + size;

            // Check to make sure a GC didn't cause "count" to be invalid, leading to too large
            // of an object
            if (addr >= End)
            {
                return(new ClrObject(0, _heap.NullType));
            }

            // Ensure we aren't at the start of an alloc context
            ulong tmp;

            while (!IsLarge && _subHeap.AllocPointers.TryGetValue(addr, out tmp))
            {
                tmp += Align(minObjSize, _large);

                // Only if there's data corruption:
                if (addr >= tmp)
                {
                    return(new ClrObject(0, _heap.NullType));
                }

                // Otherwise:
                addr = tmp;

                if (addr >= End)
                {
                    return(new ClrObject(0, _heap.NullType));
                }
            }

            type = _heap.GetObjectType(addr);
            return(new ClrObject(addr, type));
        }
Exemple #2
0
 public ClrStackRoot(ulong address, ClrObject obj, ClrStackFrame stackFrame, bool interior, bool pinned)
 {
     Address    = address;
     Object     = obj;
     StackFrame = stackFrame;
     IsInterior = interior;
     IsPinned   = pinned;
 }
        internal static ClrObject Create(ulong address, ClrType type)
        {
            ClrObject obj = new ClrObject();

            obj._address = address;
            obj._type    = type;
            return(obj);
        }
Exemple #4
0
        /// <summary>
        ///     Converts the specified o.
        /// </summary>
        /// <param name="o">The o.</param>
        /// <returns>IClrObject.</returns>
        public IClrObject Convert(ClrMd.ClrObject o)
        {
            if (o == null)
            {
                return(null);
            }
            var item = new ClrObjectAdapter(this, o);

            return(Cache.GetOrAdd <IClrObject>(o, () => item, () => item.Setup()));
        }
Exemple #5
0
        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="del">The parent delgate that this target came from.</param>
        /// <param name="target">The "target" of this delegate.</param>
        /// <param name="method">The method this delegate will call.</param>
        public ClrDelegateTarget(ClrDelegate del, ClrObject target, ClrMethod method)
        {
            if (method is null)
            {
                throw new ArgumentNullException(nameof(method));
            }

            Parent       = del;
            TargetObject = target;
            Method       = method;
        }
Exemple #6
0
        public ClrException(IExceptionHelpers helpers, ClrThread?thread, ClrObject obj)
        {
            if (obj.IsNull)
            {
                throw new InvalidOperationException($"Cannot construct a ClrException from a null object.");
            }

            _helpers = helpers ?? throw new ArgumentNullException(nameof(helpers));
            _object  = obj;
            Thread   = thread;

            DebugOnly.Assert(obj.IsException);
        }
Exemple #7
0
        private Task <Tuple <LinkedList <ClrObject>, ClrRoot> > PathToParallel(ObjectSet seen, Dictionary <ulong, LinkedListNode <ClrObject> > knownEndPoints, ClrHandle handle, ulong target, bool unique, CancellationToken cancelToken)
        {
            Debug.Assert(IsFullyCached);

            Task <Tuple <LinkedList <ClrObject>, ClrRoot> > t = new Task <Tuple <LinkedList <ClrObject>, ClrRoot> >(() =>
            {
                LinkedList <ClrObject> path = PathTo(seen, knownEndPoints, ClrObject.Create(handle.Object, handle.Type), target, unique, true, cancelToken).FirstOrDefault();
                return(new Tuple <LinkedList <ClrObject>, ClrRoot>(path, path != null ? GetHandleRoot(handle) : null));
            });

            t.Start();
            return(t);
        }
Exemple #8
0
        public override IEnumerable <ClrObject> EnumerateObjects()
        {
            RevisionValidator.Validate(Revision, GetRuntimeRevision());

            for (int i = 0; i < _segments.Length; ++i)
            {
                ClrSegment seg = _segments[i];

                for (ulong obj = seg.GetFirstObject(out ClrType type); obj != 0; obj = seg.NextObject(obj, out type))
                {
                    _lastSegmentIdx = i;
                    yield return(ClrObject.Create(obj, type));
                }
            }
        }
Exemple #9
0
        /// <summary>
        /// Enumerates all delegate targets of this delegate.  If called on a MulitcastDelegate, this will enumerate all
        /// targets that will be called when this delegate is invoked.  If called on a non-MulticastDelegate, this will
        /// enumerate the value of GetDelegateTarget.
        /// </summary>
        /// <returns></returns>
        public IEnumerable <ClrDelegateTarget> EnumerateDelegateTargets()
        {
            ClrDelegateTarget?first = GetDelegateTarget();

            if (first != null)
            {
                yield return(first);
            }

            // The call to GetDelegateMethod will validate that we are a valid object and a subclass of System.Delegate
            if (!Object.TryReadField("_invocationCount", out int count) ||
                count == 0 ||
                !Object.TryReadObjectField("_invocationList", out ClrObject invocationList) ||
                !invocationList.IsArray)
            {
                yield break;
            }

            ClrArray invocationArray = invocationList.AsArray();

            count = Math.Min(count, invocationArray.Length);

            ClrHeap heap = Object.Type !.Heap;

            UIntPtr[]? pointers = invocationArray.ReadValues <UIntPtr>(0, count);
            if (pointers is not null)
            {
                foreach (UIntPtr ptr in pointers)
                {
                    if (ptr == UIntPtr.Zero)
                    {
                        continue;
                    }

                    ClrObject delegateObj = heap.GetObject(ptr.ToUInt64());
                    if (delegateObj.IsDelegate)
                    {
                        ClrDelegateTarget?delegateTarget = new ClrDelegate(delegateObj).GetDelegateTarget();
                        if (delegateTarget is not null)
                        {
                            yield return(delegateTarget);
                        }
                    }
                }
            }
        }
Exemple #10
0
        public override IEnumerable <ClrObject> EnumerateObjects()
        {
            if (Revision != GetRuntimeRevision())
            {
                ClrDiagnosticsException.ThrowRevisionError(Revision, GetRuntimeRevision());
            }

            for (int i = 0; i < _segments.Length; ++i)
            {
                var seg = _segments[i];
                for (ClrObject obj = seg.FirstObject; !obj.IsNull; obj = seg.GetNextObject(obj))
                {
                    _lastSegmentIdx = i;
                    yield return(obj);
                }
            }
        }
Exemple #11
0
        /// <summary>
        /// Creates a ClrFieldReference from an actual field.
        /// </summary>
        /// <param name="reference">The object referenced.</param>
        /// <param name="containingType">The type of the object which points to <paramref name="reference"/>.</param>
        /// <param name="offset">The offset within the source object where <paramref name="reference"/> was located.</param>
        public static ClrReference CreateFromFieldOrArray(ClrObject reference, ClrType containingType, int offset)
        {
            if (containingType == null)
            {
                throw new ArgumentNullException(nameof(containingType));
            }

            offset -= IntPtr.Size;
            DebugOnly.Assert(offset >= 0);

            ClrInstanceField?field = containingType.IsArray ? null : containingType.Fields.First(f => f.Offset <= offset && offset < f.Offset + f.Size);

            unchecked
            {
                return(new ClrReference(reference, field, OffsetFlag | (uint)offset));
            }
        }
Exemple #12
0
        /// <summary>
        /// Gets a string field from the object.  Note that the type must match exactly, as this method
        /// will not do type coercion.
        /// </summary>
        /// <param name="fieldName">The name of the field to get the value for.</param>
        /// <param name="maxLength">The maximum length of the string returned.  Warning: If the DataTarget
        /// being inspected has corrupted or an inconsistent heap state, the length of a string may be
        /// incorrect, leading to OutOfMemory and other failures.</param>
        /// <returns>The value of the given field.</returns>
        /// <exception cref="ArgumentException">No field matches the given name.</exception>
        /// <exception cref="InvalidOperationException">The field is not a string.</exception>
        /// <exception cref="MemoryReadException">There was an error reading the value of this field out of the data target.</exception>
        public string?GetStringField(string fieldName, int maxLength = 4096)
        {
            ulong address = GetFieldAddress(fieldName, ClrElementType.String, "string");

            if (!DataReader.ReadPointer(address, out ulong str))
            {
                throw new MemoryReadException(address);
            }

            if (str == 0)
            {
                return(null);
            }

            ClrObject obj = new ClrObject(str, Type.Heap.StringType);

            return(obj.AsString(maxLength));
        }
Exemple #13
0
        public override IEnumerable <ClrObject> EnumerateObjects()
        {
            if (Revision != GetRuntimeRevision())
            {
                ClrDiagnosticsException.ThrowRevisionError(Revision, GetRuntimeRevision());
            }

            for (int i = 0; i < _segments.Length; ++i)
            {
                ClrSegment seg = _segments[i];

                for (ulong obj = seg.GetFirstObject(out ClrType type); obj != 0; obj = seg.NextObject(obj, out type))
                {
                    _lastSegmentIdx = i;
                    yield return(ClrObject.Create(obj, type));
                }
            }
        }
Exemple #14
0
        /// <summary>
        /// Creates a ClrFieldReference from an actual field.
        /// </summary>
        /// <param name="reference">The object referenced.</param>
        /// <param name="containingType">The type of the object which points to <paramref name="reference"/>.</param>
        /// <param name="offset">The offset within the source object where <paramref name="reference"/> was located.  This offset
        /// should start from where the object's data starts (IE this offset should NOT contain the MethodTable in the offset
        /// calculation.</param>
        public static ClrReference CreateFromFieldOrArray(ClrObject reference, ClrType containingType, int offset)
        {
            if (containingType == null)
            {
                throw new ArgumentNullException(nameof(containingType));
            }

            if (offset < 0)
            {
                throw new ArgumentOutOfRangeException($"{nameof(offset)} must be >= 0.");
            }

            ClrInstanceField?field = FindField(containingType.Fields, offset);

            unchecked
            {
                return(new ClrReference(reference, field, OffsetFlag | (uint)offset));
            }
        }
Exemple #15
0
        /// <summary>
        /// Creates a ClrFieldReference from an actual field.
        /// </summary>
        /// <param name="reference">The object referenced.</param>
        /// <param name="containingType">The type of the object which points to <paramref name="reference"/>.</param>
        /// <param name="offset">The offset within the source object where <paramref name="reference"/> was located.</param>
        public static ClrReference CreateFromFieldOrArray(ClrObject reference, ClrType containingType, int offset)
        {
            if (containingType == null)
            {
                throw new ArgumentNullException(nameof(containingType));
            }

            offset -= IntPtr.Size;
            DebugOnly.Assert(offset >= 0);

            ClrInstanceField?field = null;

            foreach (ClrInstanceField curr in containingType.Fields)
            {
                // If we found the correct field, stop searching
                if (curr.Offset <= offset && offset <= curr.Offset + curr.Size)
                {
                    field = curr;
                    break;
                }

                // Sometimes .Size == 0 if we failed to properly determine the type of the field,
                // instead search for the field closest to the offset we are searching for.
                if (curr.Offset <= offset)
                {
                    if (field == null)
                    {
                        field = curr;
                    }
                    else if (field.Offset < curr.Offset)
                    {
                        field = curr;
                    }
                }
            }

            unchecked
            {
                return(new ClrReference(reference, field, OffsetFlag | (uint)offset));
            }
        }
        public ClrObject?AsObject()
        {
            if (_object.HasValue)
            {
                return(_object.Value);
            }

            // It's possible that ObjectPointer points the the beginning of an object, though that's rare.  Check that first.
            ClrType?type = _segment.Heap.GetObjectType(ObjectPointer);

            if (!(type is null))
            {
                _object = new ClrObject(ObjectPointer, type);
                return(_object.Value);
            }

            // ObjectPointer is pointing in the middle of an object, get the previous object for the address.
            ulong obj = _segment.GetPreviousObjectAddress(ObjectPointer);

            if (obj == 0)
            {
                return(null);
            }

            type = _segment.Heap.GetObjectType(obj);
            if (type is null)
            {
                // This is heap corruption, or an inconsistent dump.  We should have found a real object here.
                return(null);
            }

            ClrObject result = new ClrObject(obj, type);

            _object = result;
            return(result);
        }
Exemple #17
0
        /// <summary>
        /// Constructs a <see cref="ClrDelegate"/> from a <see cref="ClrObject"/>.  Note that obj.IsDelegate
        /// must be true.
        /// </summary>
        /// <param name="obj">A delegate object</param>
        public ClrDelegate(ClrObject obj)
        {
            DebugOnly.Assert(obj.IsDelegate);

            Object = obj;
        }
Exemple #18
0
        private Stack <ClrObject> GetRefs(ObjectSet seen, Dictionary <ulong, LinkedListNode <ClrObject> > knownEndPoints, ClrObject obj, ulong target, bool unique, bool parallel, CancellationToken cancelToken, out bool foundTarget, out LinkedListNode <ClrObject> foundEnding)
        {
            // These asserts slow debug down by a lot, but it's important to ensure consistency in retail.
            //Debug.Assert(obj.Type != null);
            //Debug.Assert(obj.Type == _heap.GetObjectType(obj.Address));

            Stack <ClrObject> result = s_emptyStack;

            bool found = false;
            LinkedListNode <ClrObject> ending = null;

            if (obj.ContainsPointers)
            {
                foreach (ClrObject reference in obj.EnumerateObjectReferences(true))
                {
                    cancelToken.ThrowIfCancellationRequested();
                    if (ending == null && knownEndPoints != null)
                    {
                        lock (knownEndPoints)
                        {
                            if (unique)
                            {
                                if (knownEndPoints.ContainsKey(reference.Address))
                                {
                                    continue;
                                }
                            }
                            else
                            {
                                knownEndPoints.TryGetValue(reference.Address, out ending);
                            }
                        }
                    }

                    if (!seen.Contains(reference.Address))
                    {
                        if (result == s_emptyStack)
                        {
                            result = new Stack <ClrObject>();
                        }

                        result.Push(reference);
                        if (reference.Address == target)
                        {
                            found = true;
                        }
                    }
                }
            }

            foundTarget = found;
            foundEnding = ending;

            return(result);
        }
Exemple #19
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);
                }
            }
        }
Exemple #20
0
        private Task <Tuple <LinkedList <ClrObject>, ClrRoot> > PathToParallel(ObjectSet seen, Dictionary <ulong, LinkedListNode <ClrObject> > knownEndPoints, ClrRoot root, ulong target, bool unique, CancellationToken cancelToken)
        {
            Debug.Assert(IsFullyCached);

            Task <Tuple <LinkedList <ClrObject>, ClrRoot> > t = new Task <Tuple <LinkedList <ClrObject>, ClrRoot> >(() => new Tuple <LinkedList <ClrObject>, ClrRoot>(PathTo(seen, knownEndPoints, ClrObject.Create(root.Object, root.Type), target, unique, true, cancelToken).FirstOrDefault(), root));

            t.Start();
            return(t);
        }
Exemple #21
0
 private ClrReference(ClrObject obj, ClrInstanceField?field, ulong offsetOrHandleValue)
 {
     _offsetOrHandle = offsetOrHandleValue;
     Object          = obj;
     Field           = field;
 }
Exemple #22
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)
            });
Exemple #23
0
 /// <summary>
 /// Create a field reference from a dependent handle value.  We do not keep track of the dependent handle it came from
 /// so we don't accept the value here.
 /// </summary>
 /// <param name="reference">The object referenced.</param>
 public static ClrReference CreateFromDependentHandle(ClrObject reference) => new ClrReference(reference, null, DependentFlag);
Exemple #24
0
 /// <summary>
 /// Gets a <see cref="ClrObject"/> for the given address on this heap.
 /// </summary>
 /// <remarks>
 /// The returned object will have a <c>null</c> <see cref="ClrObject.Type"/> if objRef does not point to
 /// a valid managed object.
 /// </remarks>
 /// <param name="objRef"></param>
 /// <returns></returns>
 public ClrObject GetObject(ulong objRef) => ClrObject.Create(objRef, GetObjectType(objRef));
Exemple #25
0
        public static dynamic AsDynamic(this ClrObject clrObject)
        {
            var heap = clrObject.Type.Heap;

            return(heap.GetProxy(clrObject.Address));
        }
Exemple #26
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)
            });
Exemple #27
0
        public override Address NextObject(Address addr)
        {
            ClrObject obj = Heap.GetObject(addr);

            return(GetNextObject(obj).Address);
        }
Exemple #28
0
 /// <summary>
 /// Returns the next object given an object on this segment.  Returns an object with
 /// IsNull set to true when reaching the end of this segment.
 /// </summary>
 public abstract ClrObject GetNextObject(ClrObject obj);