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); |
+} |