// Copyright 2016 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "base/win/wait_chain.h" #include #include "base/logging.h" #include "base/strings/stringprintf.h" namespace base { namespace win { namespace { // Helper deleter to hold a HWCT into a unique_ptr. struct WaitChainSessionDeleter { using pointer = HWCT; void operator()(HWCT session_handle) const { ::CloseThreadWaitChainSession(session_handle); } }; using ScopedWaitChainSessionHandle = std::unique_ptr; const wchar_t* WctObjectTypeToString(WCT_OBJECT_TYPE type) { switch (type) { case WctCriticalSectionType: return L"CriticalSection"; case WctSendMessageType: return L"SendMessage"; case WctMutexType: return L"Mutex"; case WctAlpcType: return L"Alpc"; case WctComType: return L"Com"; case WctThreadWaitType: return L"ThreadWait"; case WctProcessWaitType: return L"ProcessWait"; case WctThreadType: return L"Thread"; case WctComActivationType: return L"ComActivation"; case WctUnknownType: return L"Unknown"; case WctSocketIoType: return L"SocketIo"; case WctSmbIoType: return L"SmbIo"; case WctMaxType: break; } NOTREACHED(); return L""; } const wchar_t* WctObjectStatusToString(WCT_OBJECT_STATUS status) { switch (status) { case WctStatusNoAccess: return L"NoAccess"; case WctStatusRunning: return L"Running"; case WctStatusBlocked: return L"Blocked"; case WctStatusPidOnly: return L"PidOnly"; case WctStatusPidOnlyRpcss: return L"PidOnlyRpcss"; case WctStatusOwned: return L"Owned"; case WctStatusNotOwned: return L"NotOwned"; case WctStatusAbandoned: return L"Abandoned"; case WctStatusUnknown: return L"Unknown"; case WctStatusError: return L"Error"; case WctStatusMax: break; } NOTREACHED(); return L""; } } // namespace bool GetThreadWaitChain(DWORD thread_id, WaitChainNodeVector* wait_chain, bool* is_deadlock, base::string16* failure_reason, DWORD* last_error) { DCHECK(wait_chain); DCHECK(is_deadlock); constexpr wchar_t kWaitChainSessionFailureReason[] = L"OpenThreadWaitChainSession() failed."; constexpr wchar_t kGetWaitChainFailureReason[] = L"GetThreadWaitChain() failed."; // Open a synchronous session. ScopedWaitChainSessionHandle session_handle( ::OpenThreadWaitChainSession(0, nullptr)); if (!session_handle) { if (last_error) *last_error = ::GetLastError(); if (failure_reason) *failure_reason = kWaitChainSessionFailureReason; DPLOG(ERROR) << kWaitChainSessionFailureReason; return false; } DWORD nb_nodes = WCT_MAX_NODE_COUNT; wait_chain->resize(nb_nodes); BOOL is_cycle; if (!::GetThreadWaitChain(session_handle.get(), NULL, 0, thread_id, &nb_nodes, wait_chain->data(), &is_cycle)) { if (last_error) *last_error = ::GetLastError(); if (failure_reason) *failure_reason = kGetWaitChainFailureReason; DPLOG(ERROR) << kGetWaitChainFailureReason; return false; } *is_deadlock = is_cycle ? true : false; wait_chain->resize(nb_nodes); return true; } base::string16 WaitChainNodeToString(const WAITCHAIN_NODE_INFO& node) { if (node.ObjectType == WctThreadType) { return base::StringPrintf(L"Thread %d in process %d with status %ls", node.ThreadObject.ThreadId, node.ThreadObject.ProcessId, WctObjectStatusToString(node.ObjectStatus)); } else { return base::StringPrintf(L"Lock of type %ls with status %ls", WctObjectTypeToString(node.ObjectType), WctObjectStatusToString(node.ObjectStatus)); } } } // namespace win } // namespace base