| // 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 |