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