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