| Index: tools/win/ShowThreadNames/ShowThreadNames.cc
|
| diff --git a/tools/win/ShowThreadNames/ShowThreadNames.cc b/tools/win/ShowThreadNames/ShowThreadNames.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..b083f2feef8a0d7875adb05f99c343c38a81eca7
|
| --- /dev/null
|
| +++ b/tools/win/ShowThreadNames/ShowThreadNames.cc
|
| @@ -0,0 +1,142 @@
|
| +// Copyright (c) 2017 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 <windows.h>
|
| +
|
| +#include <stdio.h>
|
| +#include <tchar.h>
|
| +#include <tlhelp32.h>
|
| +
|
| +#include <algorithm>
|
| +#include <iostream>
|
| +#include <iterator>
|
| +#include <map>
|
| +#include <sstream>
|
| +#include <string>
|
| +
|
| +// List all thread names in a process specified.
|
| +BOOL ListProcessThreadNames(DWORD owner_pid);
|
| +// Print the error message.
|
| +void printError(TCHAR* msg);
|
| +
|
| +// The GetThreadDescription API is available since Windows 10, version 1607.
|
| +// The reason why this API is bound in this way rather than just using the
|
| +// Windows SDK, is that this API isn't yet available in the SDK that Chrome
|
| +// builds with.
|
| +// Binding SetThreadDescription API in Chrome can only be done by
|
| +// GetProcAddress, rather than the import library.
|
| +typedef HRESULT(WINAPI* GETTHREADDESCRIPTION)(HANDLE hThread,
|
| + PWSTR* threadDescription);
|
| +
|
| +int main(void) {
|
| + DWORD process_Id;
|
| + std::string user_input;
|
| + while (true) {
|
| + std::cout
|
| + << "\nPlease enter the process Id, or \"quit\" to end the program : ";
|
| + std::getline(std::cin, user_input);
|
| + // Convert the user input to lower case.
|
| + std::transform(user_input.begin(), user_input.end(), user_input.begin(),
|
| + ::tolower);
|
| + if (user_input == "quit")
|
| + break;
|
| + std::cout << std::endl;
|
| + std::stringstream ss(user_input);
|
| + if (ss >> process_Id) {
|
| + ListProcessThreadNames(process_Id);
|
| + } else {
|
| + std::cout << "Input is invalid" << std::endl;
|
| + }
|
| + std::cout << std::endl;
|
| + }
|
| + return 0;
|
| +}
|
| +
|
| +BOOL ListProcessThreadNames(DWORD owner_pid) {
|
| + auto get_thread_description_func =
|
| + reinterpret_cast<GETTHREADDESCRIPTION>(::GetProcAddress(
|
| + ::GetModuleHandle(L"Kernel32.dll"), "GetThreadDescription"));
|
| +
|
| + if (!get_thread_description_func) {
|
| + printError(TEXT("GetThreadDescription"));
|
| + return (FALSE);
|
| + }
|
| +
|
| + HANDLE thread_snapshot = INVALID_HANDLE_VALUE;
|
| + // Take a snapshot of all running threads.
|
| + thread_snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
|
| + if (thread_snapshot == INVALID_HANDLE_VALUE) {
|
| + printError(TEXT("CreateToolhelp32Snapshot"));
|
| + return (FALSE);
|
| + }
|
| +
|
| + THREADENTRY32 te32;
|
| + te32.dwSize = sizeof(THREADENTRY32);
|
| +
|
| + // Retrieve information about the first thread, and exit if unsuccessful.
|
| + if (!Thread32First(thread_snapshot, &te32)) {
|
| + printError(TEXT("Thread32First"));
|
| + CloseHandle(thread_snapshot);
|
| + return (FALSE);
|
| + }
|
| +
|
| + // Walk the thread list of the system, and display ID and name about each
|
| + // thread associated with the process specified.
|
| + std::cout << "thread_ID thread_name" << std::endl;
|
| + std::multimap<std::wstring, DWORD> name_id_map;
|
| + do {
|
| + if (te32.th32OwnerProcessID == owner_pid) {
|
| + HANDLE thread_handle =
|
| + OpenThread(THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID);
|
| + if (thread_handle) {
|
| + PWSTR data;
|
| + HRESULT hr = get_thread_description_func(thread_handle, &data);
|
| + if (SUCCEEDED(hr)) {
|
| + std::wstring thread_name(data);
|
| + LocalFree(data);
|
| + name_id_map.insert(std::make_pair(thread_name, te32.th32ThreadID));
|
| + } else {
|
| + printError(TEXT("GetThreadDescription"));
|
| + }
|
| + CloseHandle(thread_handle);
|
| + } else {
|
| + printError(TEXT("OpenThread"));
|
| + }
|
| + }
|
| + } while (Thread32Next(thread_snapshot, &te32));
|
| +
|
| + // Clean up the snapshot object.
|
| + CloseHandle(thread_snapshot);
|
| +
|
| + // Show all thread ID/name pairs.
|
| + for (auto name_id_pair : name_id_map) {
|
| + std::cout << name_id_pair.second << "\t";
|
| + std::wcout << name_id_pair.first << std::endl;
|
| + }
|
| +
|
| + return (TRUE);
|
| +}
|
| +
|
| +void printError(TCHAR* msg) {
|
| + DWORD eNum;
|
| + TCHAR sysMsg[256];
|
| + TCHAR* p;
|
| +
|
| + eNum = GetLastError();
|
| + FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
|
| + NULL, eNum, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), sysMsg,
|
| + 256, NULL);
|
| +
|
| + // Trim the end of the line and terminate it with a null.
|
| + p = sysMsg;
|
| + while ((*p > 31) || (*p == 9))
|
| + ++p;
|
| + do {
|
| + *p-- = 0;
|
| + } while ((p >= sysMsg) && ((*p == '.') || (*p < 33)));
|
| +
|
| + // Display the message.
|
| + _tprintf(TEXT("\n WARNING: %s failed with error %d (%s)"), msg, eNum,
|
| + sysMsg);
|
| +}
|
|
|