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());
            }
        }