|  | // 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 <memory> | 
|  |  | 
|  | #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<HWCT, WaitChainSessionDeleter>; | 
|  |  | 
|  | 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 |