blob: a18a1ea44b185e0ad34c81873885da5fd0f5c0fe [file] [log] [blame]
// 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