Chromium Code Reviews| 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..9b4657087cfc6fa8dc1633e33f0e5f1f963f42e8 |
| --- /dev/null |
| +++ b/tools/win/ShowThreadNames/ShowThreadNames.cc |
| @@ -0,0 +1,137 @@ |
| +// 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 dwOwnerPID); |
| +// Print the error message. |
| +void printError(TCHAR* msg); |
| + |
| +// The GetThreadDescription API is available since Windows 10, version 1607. |
| +// The reason why this API is binded in this way rather than just using the |
|
brucedawson
2017/02/23 19:33:11
binded -> bound
chengx
2017/02/23 20:45:06
Done.
|
| +// Windows SDK, is to make it consistent with the bind of SetThreadDescription |
| +// in Chrome. Binding SetThreadDescription API in Chrome can only be done via |
| +// the system dll, rather than SDK. |
|
brucedawson
2017/02/23 19:33:12
"can only be done by GetProcAddress, rather than t
chengx
2017/02/23 20:45:06
Done.
|
| +typedef HRESULT(WINAPI* GETTHREADDESCRIPTION)(HANDLE hThread, |
| + PWSTR* threadDescription); |
| + |
| +int main(void) { |
| + unsigned int processId; |
|
brucedawson
2017/02/23 19:33:11
Should use DWORD as type for consistency.
chengx
2017/02/23 20:45:06
Done.
|
| + 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); |
| + if (user_input == "quit") |
| + break; |
| + std::cout << std::endl; |
| + std::stringstream ss(user_input); |
| + if (ss >> processId) { |
| + ListProcessThreadNames(processId); |
| + } else { |
| + std::cout << "input is invalid" << std::endl; |
| + } |
| + std::cout << std::endl; |
| + } |
| + return 0; |
| +} |
| + |
| +BOOL ListProcessThreadNames(DWORD dwOwnerPID) { |
| + 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 threadSnap = INVALID_HANDLE_VALUE; |
| + // Take a snapshot of all running threads. |
| + threadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); |
| + if (threadSnap == 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(threadSnap, &te32)) { |
| + printError(TEXT("Thread32First")); |
| + CloseHandle(threadSnap); |
| + 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 == dwOwnerPID) { |
| + HANDLE threadHandle = |
| + OpenThread(THREAD_QUERY_INFORMATION, FALSE, te32.th32ThreadID); |
| + if (threadHandle) { |
| + PWSTR data; |
| + HRESULT hr = get_thread_description_func(threadHandle, &data); |
| + if (SUCCEEDED(hr)) { |
| + std::wstring data_w(data); |
| + name_id_map.insert(std::make_pair(data_w, te32.th32ThreadID)); |
|
brucedawson
2017/02/23 19:33:11
Don't forget to free the text with LocalFree, per
chengx
2017/02/23 20:45:06
Done.
|
| + } else { |
| + printError(TEXT("GetThreadDescription")); |
| + } |
| + CloseHandle(threadHandle); |
| + } else { |
| + printError(TEXT("OpenThread")); |
| + } |
| + } |
| + } while (Thread32Next(threadSnap, &te32)); |
| + |
| + // Clean up the snapshot object. |
| + CloseHandle(threadSnap); |
| + |
| + // Show all thread ID/name pairs. |
| + for (auto it = name_id_map.begin(); it != name_id_map.end(); ++it) { |
| + std::cout << it->second << "\t"; |
| + std::wcout << it->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); |
| +} |
|
brucedawson
2017/02/23 19:37:32
Put a line terminator on the last line.
chengx
2017/02/23 20:45:06
Done.
|