summaryrefslogtreecommitdiffstats
path: root/toolkit/components/processtools/ProcInfo.h
blob: 7fd2d7aae8be621528b5179a443a39c938e8ef14 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef __mozilla_ProcInfo_h
#define __mozilla_ProcInfo_h

#include <base/process.h>
#include <stdint.h>
#include "mozilla/dom/BindingDeclarations.h"
#include "mozilla/dom/ChromeUtilsBinding.h"
#include "mozilla/dom/ipc/IdType.h"
#include "mozilla/HashTable.h"
#include "mozilla/MozPromise.h"

namespace mozilla {

namespace ipc {
class GeckoChildProcessHost;
}

/**
 * Return the number of milliseconds of CPU time used since process start.
 *
 * @return NS_OK on success.
 */
nsresult GetCpuTimeSinceProcessStartInMs(uint64_t* aResult);

/**
 * Return the number of milliseconds of GPU time used since process start.
 *
 * @return NS_OK on success.
 */
nsresult GetGpuTimeSinceProcessStartInMs(uint64_t* aResult);

// Process types. When updating this enum, please make sure to update
// WebIDLProcType, ChromeUtils::RequestProcInfo and ProcTypeToWebIDL to
// mirror the changes.
enum class ProcType {
  // These must match the ones in RemoteType.h, and E10SUtils.sys.mjs
  Web,
  WebIsolated,
  File,
  Extension,
  PrivilegedAbout,
  PrivilegedMozilla,
  WebCOOPCOEP,
  WebServiceWorker,
// the rest matches GeckoProcessTypes.h
#define GECKO_PROCESS_TYPE(enum_value, enum_name, string_name, proc_typename, \
                           process_bin_type, procinfo_typename,               \
                           webidl_typename, allcaps_name)                     \
  procinfo_typename,
#define SKIP_PROCESS_TYPE_CONTENT
#ifndef MOZ_ENABLE_FORKSERVER
#  define SKIP_PROCESS_TYPE_FORKSERVER
#endif  // MOZ_ENABLE_FORKSERVER
#include "mozilla/GeckoProcessTypes.h"
#undef SKIP_PROCESS_TYPE_CONTENT
#ifndef MOZ_ENABLE_FORKSERVER
#  undef SKIP_PROCESS_TYPE_FORKSERVER
#endif  // MOZ_ENABLE_FORKSERVER
#undef GECKO_PROCESS_TYPE
  Preallocated,
  // Unknown type of process
  Unknown,
  Max = Unknown,
};

using UtilityActorName = mozilla::dom::WebIDLUtilityActorName;

// String that will be used e.g. to annotate crash reports
nsCString GetUtilityActorName(const UtilityActorName aActorName);

#ifdef XP_WIN
int GetCpuFrequencyMHz();
#endif

/* Get the CPU frequency to use to convert cycle time values to actual time.
 * @returns the TSC (Time Stamp Counter) frequency in MHz, or 0 if converting
 * cycle time values should not be attempted. */
int GetCycleTimeFrequencyMHz();

struct ThreadInfo {
  // Thread Id.
  base::ProcessId tid = 0;
  // Thread name, if any.
  nsString name;
  // CPU time in ns.
  uint64_t cpuTime = 0;
  // CPU time in cycles if available.
  uint64_t cpuCycleCount = 0;
};

// Info on a DOM window.
struct WindowInfo {
  explicit WindowInfo()
      : outerWindowId(0),
        documentURI(nullptr),
        documentTitle(u""_ns),
        isProcessRoot(false),
        isInProcess(false) {}
  WindowInfo(uint64_t aOuterWindowId, nsIURI* aDocumentURI,
             nsAString&& aDocumentTitle, bool aIsProcessRoot, bool aIsInProcess)
      : outerWindowId(aOuterWindowId),
        documentURI(aDocumentURI),
        documentTitle(std::move(aDocumentTitle)),
        isProcessRoot(aIsProcessRoot),
        isInProcess(aIsInProcess) {}

  // Internal window id.
  const uint64_t outerWindowId;

  // URI of the document.
  const nsCOMPtr<nsIURI> documentURI;

  // Title of the document.
  const nsString documentTitle;

  // True if this is the toplevel window of the process.
  // Note that this may be an iframe from another process.
  const bool isProcessRoot;

  const bool isInProcess;
};

// Info on a Utility process actor
struct UtilityInfo {
  explicit UtilityInfo() : actorName(UtilityActorName::Unknown) {}
  explicit UtilityInfo(UtilityActorName aActorName) : actorName(aActorName) {}
  const UtilityActorName actorName;
};

struct ProcInfo {
  // Process Id
  base::ProcessId pid = 0;
  // Child Id as defined by Firefox when a child process is created.
  dom::ContentParentId childId;
  // Process type
  ProcType type;
  // Origin, if any
  nsCString origin;
  // Memory size in bytes.
  uint64_t memory = 0;
  // CPU time in ns.
  uint64_t cpuTime = 0;
  uint64_t cpuCycleCount = 0;
  // Threads owned by this process.
  CopyableTArray<ThreadInfo> threads;
  // DOM windows represented by this process.
  CopyableTArray<WindowInfo> windows;
  // Utility process actors, empty for non Utility process
  CopyableTArray<UtilityInfo> utilityActors;
};

typedef MozPromise<mozilla::HashMap<base::ProcessId, ProcInfo>, nsresult, true>
    ProcInfoPromise;

/**
 * Data we need to request process info (e.g. CPU usage, memory usage)
 * from the operating system and populate the resulting `ProcInfo`.
 *
 * Note that this structure contains a mix of:
 * - low-level handles that we need to request low-level process info
 *    (`aChildTask` on macOS, `aPid` on other platforms); and
 * - high-level data that we already acquired while looking for
 * `aPid`/`aChildTask` and that we will need further down the road.
 */
struct ProcInfoRequest {
  ProcInfoRequest(base::ProcessId aPid, ProcType aProcessType,
                  const nsACString& aOrigin, nsTArray<WindowInfo>&& aWindowInfo,
                  nsTArray<UtilityInfo>&& aUtilityInfo, uint32_t aChildId = 0
#ifdef XP_DARWIN
                  ,
                  mach_port_t aChildTask = 0
#endif  // XP_DARWIN
                  )
      : pid(aPid),
        processType(aProcessType),
        origin(aOrigin),
        windowInfo(std::move(aWindowInfo)),
        utilityInfo(std::move(aUtilityInfo)),
        childId(aChildId)
#ifdef XP_DARWIN
        ,
        childTask(aChildTask)
#endif  // XP_DARWIN
  {
  }
  const base::ProcessId pid;
  const ProcType processType;
  const nsCString origin;
  const nsTArray<WindowInfo> windowInfo;
  const nsTArray<UtilityInfo> utilityInfo;
  // If the process is a child, its child id, otherwise `0`.
  const int32_t childId;
#ifdef XP_DARWIN
  const mach_port_t childTask;
#endif  // XP_DARWIN
};

/**
 * Batch a request for low-level information on Gecko processes.
 *
 * # Request
 *
 * Argument `aRequests` is a list of processes, along with high-level data
 * we have already obtained on them and that we need to populate the
 * resulting array of `ProcInfo`.
 *
 * # Result
 *
 * This call succeeds (possibly with missing data, see below) unless we
 * cannot allocate memory.
 *
 * # Performance
 *
 * - This call is always executed on a background thread.
 * - This call does NOT wake up children processes.
 * - This function is sometimes observably slow to resolve, in particular
 *   under Windows.
 *
 * # Error-handling and race conditions
 *
 * Requesting low-level information on a process and its threads is inherently
 * subject to race conditions. Typically, if a process or a thread is killed
 * while we're preparing to fetch information, we can easily end up with
 * system/lib calls that return failures.
 *
 * For this reason, this API assumes that errors when placing a system/lib call
 * are likely and normal. When some information cannot be obtained, the API will
 * simply skip over said information.
 *
 * Note that due to different choices by OSes, the exact information we skip may
 * vary across platforms. For instance, under Unix, failing to access the
 * threads of a process will cause us to skip all data on the process, while
 * under Windows, process information will be returned without thread
 * information.
 */
RefPtr<ProcInfoPromise> GetProcInfo(nsTArray<ProcInfoRequest>&& aRequests);

/**
 * Synchronous version of GetProcInfo.
 */
ProcInfoPromise::ResolveOrRejectValue GetProcInfoSync(
    nsTArray<ProcInfoRequest>&& aRequests);

/**
 * Utility function: copy data from a `ProcInfo` and into either a
 * `ParentProcInfoDictionary` or a `ChildProcInfoDictionary`.
 */
template <typename T>
nsresult CopySysProcInfoToDOM(const ProcInfo& source, T* dest) {
  // Copy system info.
  dest->mPid = source.pid;
  dest->mMemory = source.memory;
  dest->mCpuTime = source.cpuTime;
  dest->mCpuCycleCount = source.cpuCycleCount;

  // Copy thread info.
  mozilla::dom::Sequence<mozilla::dom::ThreadInfoDictionary> threads;
  for (const ThreadInfo& entry : source.threads) {
    mozilla::dom::ThreadInfoDictionary* thread =
        threads.AppendElement(fallible);
    if (NS_WARN_IF(!thread)) {
      return NS_ERROR_OUT_OF_MEMORY;
    }
    thread->mCpuTime = entry.cpuTime;
    thread->mCpuCycleCount = entry.cpuCycleCount;
    thread->mTid = entry.tid;
    thread->mName.Assign(entry.name);
  }
  dest->mThreads = std::move(threads);
  return NS_OK;
}

}  // namespace mozilla
#endif  // ProcInfo_h