private DesktopBlockingObject FindMonitor(ulong start, ulong stop) { ulong obj = 0; foreach (ulong ptr in EnumeratePointersInRange(start, stop)) { ulong tmp = 0; if (_runtime.ReadPointer(ptr, out tmp)) { if (_syncblks.TryGetValue(tmp, out tmp)) { obj = tmp; break; } } } DesktopBlockingObject result = null; if (obj != 0 && _monitors.TryGetValue(obj, out result)) { return(result); } return(null); }
private DesktopBlockingObject FindLocks(ulong start, ulong stop, Func <ulong, ClrType, bool> isCorrectType) { foreach (ulong ptr in EnumeratePointersInRange(start, stop)) { ulong val = 0; if (_runtime.ReadPointer(ptr, out val)) { DesktopBlockingObject result = null; if (_locks.TryGetValue(val, out result) && isCorrectType(val, _heap.GetObjectType(val))) { return(result); } } } return(null); }
internal DesktopBlockingObject[] InitLockInspection() { if (_result != null) { return(_result); } // First, enumerate all thinlocks on the heap. foreach (var seg in _heap.Segments) { for (ulong obj = seg.FirstObject; obj != 0; obj = seg.NextObject(obj)) { ClrType type = _heap.GetObjectType(obj); if (IsReaderWriterLock(obj, type)) { _locks[obj] = CreateRWLObject(obj, type); } else if (IsReaderWriterSlim(obj, type)) { _locks[obj] = CreateRWSObject(obj, type); } // Does this object have a syncblk with monitor associated with it? if (!_heap.GetObjectHeader(obj, out uint header)) { continue; } if ((header & (BIT_SBLK_IS_HASH_OR_SYNCBLKINDEX | BIT_SBLK_SPIN_LOCK)) != 0) { continue; } uint threadId = header & SBLK_MASK_LOCK_THREADID; if (threadId == 0) { continue; } ClrThread thread = _runtime.GetThreadFromThinlockID(threadId); if (thread != null) { int recursion = ((int)header & SBLK_MASK_LOCK_RECLEVEL) >> SBLK_RECLEVEL_SHIFT; _monitors[obj] = new DesktopBlockingObject(obj, true, recursion + 1, thread, BlockingReason.Monitor); } } } // Enumerate syncblocks to find locks int syncblkCnt = _runtime.GetSyncblkCount(); for (int i = 0; i < syncblkCnt; ++i) { ISyncBlkData data = _runtime.GetSyncblkData(i); if (data == null || data.Free) { continue; } _syncblks[data.Address] = data.Object; _syncblks[data.Object] = data.Object; ClrThread thread = null; if (data.MonitorHeld) { ulong threadAddr = data.OwningThread; foreach (var clrThread in _runtime.Threads) { if (clrThread.Address == threadAddr) { thread = clrThread; break; } } } _monitors[data.Object] = new DesktopBlockingObject(data.Object, data.MonitorHeld, (int)data.Recursion, thread, BlockingReason.Monitor); } SetThreadWaiters(); int total = _monitors.Count + _locks.Count + _joinLocks.Count + _waitLocks.Count; _result = new DesktopBlockingObject[total]; int j = 0; foreach (DesktopBlockingObject blocker in _monitors.Values) { _result[j++] = blocker; } foreach (DesktopBlockingObject blocker in _locks.Values) { _result[j++] = blocker; } foreach (DesktopBlockingObject blocker in _joinLocks.Values) { _result[j++] = blocker; } foreach (DesktopBlockingObject blocker in _waitLocks.Values) { _result[j++] = blocker; } Debug.Assert(j == _result.Length); // Free up some memory. _monitors = null; _locks = null; _joinLocks = null; _waitLocks = null; _syncblks = null; return(_result); }
private void SetThreadWaiters() { HashSet <string> eventTypes = null; List <BlockingObject> blobjs = new List <BlockingObject>(); foreach (DesktopThread thread in _runtime.Threads) { int max = thread.StackTrace.Count; if (max > 10) { max = 10; } blobjs.Clear(); for (int i = 0; i < max; ++i) { DesktopBlockingObject blockingObj = null; ClrMethod method = thread.StackTrace[i].Method; if (method == null) { continue; } ClrType type = method.Type; if (type == null) { continue; } switch (method.Name) { case "AcquireWriterLockInternal": case "FCallUpgradeToWriterLock": case "UpgradeToWriterLock": case "AcquireReaderLockInternal": case "AcquireReaderLock": if (type.Name == "System.Threading.ReaderWriterLock") { blockingObj = FindLocks(thread.StackLimit, thread.StackTrace[i].StackPointer, IsReaderWriterLock); if (blockingObj == null) { blockingObj = FindLocks(thread.StackTrace[i].StackPointer, thread.StackBase, IsReaderWriterLock); } if (blockingObj != null && (blockingObj.Reason == BlockingReason.Unknown || blockingObj.Reason == BlockingReason.None)) { // This should have already been set correctly when the BlockingObject was created. This is just a best-guess. if (method.Name == "AcquireReaderLockInternal" || method.Name == "AcquireReaderLock") { blockingObj.Reason = BlockingReason.WriterAcquired; } else { blockingObj.Reason = BlockingReason.ReaderAcquired; } } } break; case "TryEnterReadLockCore": case "TryEnterReadLock": case "TryEnterUpgradeableReadLock": case "TryEnterUpgradeableReadLockCore": case "TryEnterWriteLock": case "TryEnterWriteLockCore": if (type.Name == "System.Threading.ReaderWriterLockSlim") { blockingObj = FindLocks(thread.StackLimit, thread.StackTrace[i].StackPointer, IsReaderWriterSlim); if (blockingObj == null) { blockingObj = FindLocks(thread.StackTrace[i].StackPointer, thread.StackBase, IsReaderWriterSlim); } if (blockingObj != null && (blockingObj.Reason == BlockingReason.Unknown || blockingObj.Reason == BlockingReason.None)) { // This should have already been set correctly when the BlockingObject was created. This is just a best-guess. if (method.Name == "TryEnterWriteLock" || method.Name == "TryEnterWriteLockCore") { blockingObj.Reason = BlockingReason.ReaderAcquired; } else { blockingObj.Reason = BlockingReason.WriterAcquired; } } } break; case "JoinInternal": case "Join": if (type.Name == "System.Threading.Thread") { if (FindThread(thread.StackLimit, thread.StackTrace[i].StackPointer, out ulong threadAddr, out ClrThread target) || FindThread(thread.StackTrace[i].StackPointer, thread.StackBase, out threadAddr, out target)) { if (!_joinLocks.TryGetValue(target, out blockingObj)) { _joinLocks[target] = blockingObj = new DesktopBlockingObject(threadAddr, true, 0, target, BlockingReason.ThreadJoin); } } } break; case "Wait": case "ObjWait": if (type.Name == "System.Threading.Monitor") { blockingObj = FindMonitor(thread.StackLimit, thread.StackTrace[i].StackPointer); if (blockingObj == null) { blockingObj = FindMonitor(thread.StackTrace[i].StackPointer, thread.StackBase); } blockingObj.Reason = BlockingReason.MonitorWait; } break; case "WaitAny": case "WaitAll": if (type.Name == "System.Threading.WaitHandle") { ulong obj = FindWaitObjects(thread.StackLimit, thread.StackTrace[i].StackPointer, "System.Threading.WaitHandle[]"); if (obj == 0) { obj = FindWaitObjects(thread.StackTrace[i].StackPointer, thread.StackBase, "System.Threading.WaitHandle[]"); } if (obj != 0) { BlockingReason reason = method.Name == "WaitAny" ? BlockingReason.WaitAny : BlockingReason.WaitAll; if (!_waitLocks.TryGetValue(obj, out blockingObj)) { _waitLocks[obj] = blockingObj = new DesktopBlockingObject(obj, true, 0, null, reason); } } } break; case "WaitOne": case "InternalWaitOne": case "WaitOneNative": if (type.Name == "System.Threading.WaitHandle") { if (eventTypes == null) { eventTypes = new HashSet <string> { "System.Threading.Mutex", "System.Threading.Semaphore", "System.Threading.ManualResetEvent", "System.Threading.AutoResetEvent", "System.Threading.WaitHandle", "Microsoft.Win32.SafeHandles.SafeWaitHandle" }; } ulong obj = FindWaitHandle(thread.StackLimit, thread.StackTrace[i].StackPointer, eventTypes); if (obj == 0) { obj = FindWaitHandle(thread.StackTrace[i].StackPointer, thread.StackBase, eventTypes); } if (obj != 0) { if (_waitLocks == null) { _waitLocks = new Dictionary <ulong, DesktopBlockingObject>(); } if (!_waitLocks.TryGetValue(obj, out blockingObj)) { _waitLocks[obj] = blockingObj = new DesktopBlockingObject(obj, true, 0, null, BlockingReason.WaitOne); } } } break; case "TryEnter": case "ReliableEnterTimeout": case "TryEnterTimeout": case "ReliableEnter": case "Enter": if (type.Name == "System.Threading.Monitor") { blockingObj = FindMonitor(thread.StackLimit, thread.StackTrace[i].StackPointer); if (blockingObj != null) { blockingObj.Reason = BlockingReason.Monitor; } } break; } if (blockingObj != null) { bool alreadyEncountered = false; foreach (var blobj in blobjs) { if (blobj.Object == blockingObj.Object) { alreadyEncountered = true; break; } } if (!alreadyEncountered) { blobjs.Add(blockingObj); } } } foreach (DesktopBlockingObject blobj in blobjs) { blobj.AddWaiter(thread); } thread.SetBlockingObjects(blobjs.ToArray()); } }