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? uint header; if (!_heap.GetObjectHeader(obj, out 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") { ulong threadAddr; ClrThread target; if (FindThread(thread.StackLimit, thread.StackTrace[i].StackPointer, out threadAddr, out 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>(); eventTypes.Add("System.Threading.Mutex"); eventTypes.Add("System.Threading.Semaphore"); eventTypes.Add("System.Threading.ManualResetEvent"); eventTypes.Add("System.Threading.AutoResetEvent"); eventTypes.Add("System.Threading.WaitHandle"); eventTypes.Add("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 "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()); } }