1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "mozilla/ProcInfo.h"
8 #include "mozilla/ipc/GeckoChildProcessHost.h"
9 #include <windows.h>
10 #include <psapi.h>
11 #include <tlhelp32.h>
12 
13 typedef HRESULT(WINAPI* GETTHREADDESCRIPTION)(HANDLE hThread,
14                                               PWSTR* threadDescription);
15 
16 namespace mozilla {
17 
ToNanoSeconds(const FILETIME & aFileTime)18 uint64_t ToNanoSeconds(const FILETIME& aFileTime) {
19   // FILETIME values are 100-nanoseconds units, converting
20   ULARGE_INTEGER usec = {{aFileTime.dwLowDateTime, aFileTime.dwHighDateTime}};
21   return usec.QuadPart * 100;
22 }
23 
AppendThreads(ProcInfo * info)24 void AppendThreads(ProcInfo* info) {
25   THREADENTRY32 te32;
26   // `GetThreadDescription` is available as of Windows 10.
27   // We attempt to import it dynamically, knowing that it
28   // may be `nullptr`.
29   auto getThreadDescription =
30       reinterpret_cast<GETTHREADDESCRIPTION>(::GetProcAddress(
31           ::GetModuleHandleW(L"Kernel32.dll"), "GetThreadDescription"));
32 
33   // Take a snapshot of all running threads, system-wide.
34   nsAutoHandle hThreadSnap(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
35   if (!hThreadSnap) {
36     return;
37   }
38   te32.dwSize = sizeof(THREADENTRY32);
39 
40   // Retrieve information about the first thread,
41   // and exit if unsuccessful
42   if (!Thread32First(hThreadSnap.get(), &te32)) {
43     return;
44   }
45 
46   do {
47     if (te32.th32OwnerProcessID == info->pid) {
48       nsAutoHandle hThread(
49           OpenThread(THREAD_QUERY_INFORMATION, TRUE, te32.th32ThreadID));
50       if (!hThread) {
51         continue;
52       }
53 
54       FILETIME createTime, exitTime, kernelTime, userTime;
55       if (!GetThreadTimes(hThread.get(), &createTime, &exitTime, &kernelTime,
56                           &userTime)) {
57         continue;
58       }
59 
60       ThreadInfo thread;
61       if (getThreadDescription) {
62         PWSTR threadName = nullptr;
63         if (getThreadDescription(hThread.get(), &threadName) && threadName) {
64           thread.name = threadName;
65         }
66         if (threadName) {
67           LocalFree(threadName);
68         }
69       }
70       thread.tid = te32.th32ThreadID;
71       thread.cpuKernel = ToNanoSeconds(kernelTime);
72       thread.cpuUser = ToNanoSeconds(userTime);
73       info->threads.AppendElement(thread);
74     }
75   } while (Thread32Next(hThreadSnap.get(), &te32));
76 }
77 
GetProcInfo(base::ProcessId pid,int32_t childId,const ProcType & type,const nsAString & origin)78 RefPtr<ProcInfoPromise> GetProcInfo(base::ProcessId pid, int32_t childId,
79                                     const ProcType& type,
80                                     const nsAString& origin) {
81   auto holder = MakeUnique<MozPromiseHolder<ProcInfoPromise>>();
82   RefPtr<ProcInfoPromise> promise = holder->Ensure(__func__);
83 
84   nsresult rv = NS_OK;
85   nsCOMPtr<nsIEventTarget> target =
86       do_GetService(NS_STREAMTRANSPORTSERVICE_CONTRACTID, &rv);
87   if (NS_FAILED(rv)) {
88     NS_WARNING("Failed to get stream transport service");
89     holder->Reject(rv, __func__);
90     return promise;
91   }
92 
93   // Ensure that the string is still alive when `ResolveGetProcInfo` is called.
94   nsString originCopy(origin);
95   RefPtr<nsIRunnable> r = NS_NewRunnableFunction(
96       __func__,
97       [holder = std::move(holder), originCopy = std::move(originCopy), pid,
98        type, childId]() -> void {
99         nsAutoHandle handle(OpenProcess(
100             PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid));
101 
102         if (!handle) {
103           holder->Reject(NS_ERROR_FAILURE, __func__);
104           return;
105         }
106 
107         wchar_t filename[MAX_PATH];
108         if (GetProcessImageFileNameW(handle.get(), filename, MAX_PATH) == 0) {
109           holder->Reject(NS_ERROR_FAILURE, __func__);
110           return;
111         }
112         FILETIME createTime, exitTime, kernelTime, userTime;
113         if (!GetProcessTimes(handle.get(), &createTime, &exitTime, &kernelTime,
114                              &userTime)) {
115           holder->Reject(NS_ERROR_FAILURE, __func__);
116           return;
117         }
118         PROCESS_MEMORY_COUNTERS memoryCounters;
119         if (!GetProcessMemoryInfo(handle.get(),
120                                   (PPROCESS_MEMORY_COUNTERS)&memoryCounters,
121                                   sizeof(memoryCounters))) {
122           holder->Reject(NS_ERROR_FAILURE, __func__);
123           return;
124         }
125         ProcInfo info;
126         info.pid = pid;
127         info.childId = childId;
128         info.type = type;
129         info.origin = originCopy;
130         info.filename.Assign(filename);
131         info.cpuKernel = ToNanoSeconds(kernelTime);
132         info.cpuUser = ToNanoSeconds(userTime);
133         info.residentSetSize = memoryCounters.WorkingSetSize;
134         info.virtualMemorySize = memoryCounters.PagefileUsage;
135         AppendThreads(&info);
136         holder->Resolve(info, __func__);
137       });
138 
139   rv = target->Dispatch(r.forget(), NS_DISPATCH_NORMAL);
140   if (NS_FAILED(rv)) {
141     NS_WARNING("Failed to dispatch the LoadDataRunnable.");
142   }
143 
144   return promise;
145 }
146 
147 }  // namespace mozilla
148