 Chromium Code Reviews
 Chromium Code Reviews Issue 2713773002:
  ShowThreadNames tool to get thread ID/name pairs in a Chrome process  (Closed)
    
  
    Issue 2713773002:
  ShowThreadNames tool to get thread ID/name pairs in a Chrome process  (Closed) 
  | 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..004e615d22bd14b6664b57b5a5bcbbbfc600d26e | 
| --- /dev/null | 
| +++ b/tools/win/ShowThreadNames/ShowThreadNames.cc | 
| @@ -0,0 +1,126 @@ | 
| +// 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 threads in a process specified. | 
| +BOOL ListProcessThreadNames(DWORD dwOwnerPID); | 
| + | 
| +// The GetThreadDescription API is available since Windows 10, version 1607. | 
| 
stanisc
2017/02/23 02:29:59
Perhaps you should mention why you have to bind to
 
chengx
2017/02/23 03:26:04
Done.
 | 
| +typedef HRESULT(WINAPI* GETTHREADDESCRIPTION)(HANDLE hThread, | 
| + PWSTR* threadDescription); | 
| + | 
| +template <typename A, typename B> | 
| +std::pair<B, A> flip_pair(const std::pair<A, B>& p) { | 
| + return std::pair<B, A>(p.second, p.first); | 
| +} | 
| + | 
| +template <typename A, typename B> | 
| +std::multimap<B, A> flip_map(const std::map<A, B>& src) { | 
| + std::multimap<B, A> dst; | 
| + std::transform(src.begin(), src.end(), std::inserter(dst, dst.begin()), | 
| + flip_pair<A, B>); | 
| + return dst; | 
| +} | 
| + | 
| +int main(void) { | 
| + unsigned int processId; | 
| + 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); | 
| + std::cout << std::endl; | 
| + if (user_input == "quit") | 
| + break; | 
| + 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) { | 
| + HMODULE kernel32dll = ::GetModuleHandle(L"Kernel32.dll"); | 
| + | 
| + if (!kernel32dll) { | 
| 
brucedawson
2017/02/23 01:57:26
I'd skip this error checking 'cause kernel32.dll w
 
chengx
2017/02/23 03:26:04
Done.
 | 
| + std::cout << "Error: cannot find Kernel32.dll" << std::endl; | 
| + return (FALSE); | 
| + } | 
| + | 
| + auto get_thread_description_func = reinterpret_cast<GETTHREADDESCRIPTION>( | 
| + ::GetProcAddress(kernel32dll, "GetThreadDescription")); | 
| + | 
| + if (!get_thread_description_func) { | 
| + std::cout << "GetThreadDescription API is not available in current OS" | 
| + << std::endl; | 
| + return (FALSE); | 
| + } | 
| + | 
| + HANDLE hThreadSnap = INVALID_HANDLE_VALUE; | 
| 
stanisc
2017/02/23 02:29:59
I'd suggest to not use Hungarian notation i.e. han
 
chengx
2017/02/23 03:26:03
Changed to threadSnap.
 | 
| + // Take a snapshot of all running threads | 
| + hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0); | 
| + if (hThreadSnap == INVALID_HANDLE_VALUE) | 
| + return (FALSE); | 
| + | 
| + THREADENTRY32 te32; | 
| + te32.dwSize = sizeof(THREADENTRY32); | 
| + | 
| + // Retrieve information about the first thread, and exit if unsuccessful | 
| + if (!Thread32First(hThreadSnap, &te32)) { | 
| + std::cout << "Thread32First() call failed" << std::endl; | 
| + CloseHandle(hThreadSnap); | 
| + 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::map<DWORD, std::wstring> id_name_map; | 
| + do { | 
| + if (te32.th32OwnerProcessID == dwOwnerPID) { | 
| + HANDLE threadHandle = | 
| + OpenThread(THREAD_ALL_ACCESS, FALSE, te32.th32ThreadID); | 
| 
brucedawson
2017/02/23 01:57:26
You can probably use THREAD_QUERY_LIMITED_INFORMAT
 
chengx
2017/02/23 03:26:03
Done.
 | 
| + if (threadHandle) { | 
| + PWSTR data; | 
| + HRESULT hr = get_thread_description_func(threadHandle, &data); | 
| + if (SUCCEEDED(hr)) { | 
| + std::wstring data_w(data); | 
| + id_name_map[te32.th32ThreadID] = data_w; | 
| + } else { | 
| + std::cout << "GetThreadDescription API call failed" << std::endl; | 
| + } | 
| + CloseHandle(threadHandle); | 
| + } | 
| + } | 
| + } while (Thread32Next(hThreadSnap, &te32)); | 
| + | 
| + // reverse the map so now it is sorted by thread name. | 
| 
stanisc
2017/02/23 02:29:59
Nit: comments are supposed to be complete sentence
 
chengx
2017/02/23 03:26:04
Done.
 | 
| + std::multimap<std::wstring, DWORD> name_id_map = flip_map(id_name_map); | 
| 
brucedawson
2017/02/23 01:57:26
If you're going to put it in a multimap<wstring,DW
 
chengx
2017/02/23 03:26:03
I will use multimap<wstring,DWORD> directly.
 | 
| + | 
| + // show thread ID and name. | 
| + for (auto it = name_id_map.begin(); it != name_id_map.end(); ++it) { | 
| + std::cout << it->second << "\t"; | 
| + std::wcout << it->first << std::endl; | 
| + } | 
| + | 
| + // clean up the snapshot object. | 
| + CloseHandle(hThreadSnap); | 
| 
brucedawson
2017/02/23 01:57:26
For tidiness consider closing the handle as soon a
 
chengx
2017/02/23 03:26:04
Done.
 | 
| + return (TRUE); | 
| +} |