1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 //
5 // The main point of this class is to cache ARC proc nspid<->pid mapping
6 // globally. Since the calculation is costly, a dedicated worker thread is
7 // used. All read/write of its internal data structure (i.e., the mapping)
8 // should be on this thread.
9
10 #include "chrome/browser/chromeos/arc/process/arc_process_service.h"
11
12 #include <algorithm>
13 #include <set>
14 #include <string>
15 #include <unordered_map>
16 #include <unordered_set>
17 #include <utility>
18
19 #include "base/bind.h"
20 #include "base/containers/flat_set.h"
21 #include "base/containers/queue.h"
22 #include "base/logging.h"
23 #include "base/memory/scoped_refptr.h"
24 #include "base/memory/singleton.h"
25 #include "base/process/process.h"
26 #include "base/process/process_iterator.h"
27 #include "base/stl_util.h"
28 #include "base/task/post_task.h"
29 #include "base/task/task_traits.h"
30 #include "base/task/thread_pool.h"
31 #include "base/task_runner_util.h"
32 #include "base/time/time.h"
33 #include "base/trace_event/trace_event.h"
34 #include "chrome/browser/chromeos/process_snapshot_server.h"
35 #include "components/arc/arc_browser_context_keyed_service_factory_base.h"
36 #include "components/arc/arc_util.h"
37 #include "components/arc/mojom/process.mojom.h"
38 #include "components/arc/session/arc_bridge_service.h"
39 #include "content/public/browser/browser_thread.h"
40
41 namespace arc {
42
43 using base::kNullProcessId;
44 using base::Process;
45 using base::ProcessId;
46 using std::vector;
47
48 namespace {
49
50 static constexpr char kInitName[] = "/init";
51 static constexpr bool kNotFocused = false;
52 static constexpr int64_t kNoActivityTimeInfo = 0L;
53
54 // Matches the process name "/init" in the process tree and get the
55 // corresponding process ID.
GetArcInitProcessId(const base::ProcessIterator::ProcessEntries & entry_list)56 base::ProcessId GetArcInitProcessId(
57 const base::ProcessIterator::ProcessEntries& entry_list) {
58 for (const base::ProcessEntry& entry : entry_list) {
59 if (entry.cmd_line_args().empty()) {
60 continue;
61 }
62 // TODO(nya): Add more constraints to avoid mismatches.
63 const std::string& process_name = entry.cmd_line_args()[0];
64 if (process_name == kInitName) {
65 return entry.pid();
66 }
67 }
68 return base::kNullProcessId;
69 }
70
GetArcSystemProcessList(const base::ProcessIterator::ProcessEntries & process_list)71 std::vector<ArcProcess> GetArcSystemProcessList(
72 const base::ProcessIterator::ProcessEntries& process_list) {
73 TRACE_EVENT0("browser", "GetArcSystemProcessList");
74 std::vector<ArcProcess> ret_processes;
75 if (arc::IsArcVmEnabled()) {
76 // TODO(b/122992194): Fix this for ARCVM.
77 return ret_processes;
78 }
79
80 const base::ProcessId arc_init_pid = GetArcInitProcessId(process_list);
81
82 if (arc_init_pid == base::kNullProcessId) {
83 return ret_processes;
84 }
85
86 // Enumerate the child processes of ARC init for gathering ARC System
87 // Processes.
88 for (const base::ProcessEntry& entry : process_list) {
89 if (entry.cmd_line_args().empty()) {
90 continue;
91 }
92 // TODO(hctsai): For now, we only gather direct child process of init, need
93 // to get the processes below. For example, installd might fork dex2oat and
94 // it can be executed for minutes.
95 if (entry.parent_pid() == arc_init_pid) {
96 const base::ProcessId child_pid = entry.pid();
97 const base::ProcessId child_nspid =
98 base::Process(child_pid).GetPidInNamespace();
99 if (child_nspid != base::kNullProcessId) {
100 const std::string& process_name = entry.cmd_line_args()[0];
101 // The is_focused and last_activity_time is not needed thus mocked
102 ret_processes.emplace_back(child_nspid, child_pid, process_name,
103 mojom::ProcessState::PERSISTENT, kNotFocused,
104 kNoActivityTimeInfo);
105 }
106 }
107 }
108 return ret_processes;
109 }
110
UpdateNspidToPidMap(const base::ProcessIterator::ProcessEntries & process_list,scoped_refptr<ArcProcessService::NSPidToPidMap> pid_map)111 void UpdateNspidToPidMap(
112 const base::ProcessIterator::ProcessEntries& process_list,
113 scoped_refptr<ArcProcessService::NSPidToPidMap> pid_map) {
114 TRACE_EVENT0("browser", "ArcProcessService::UpdateNspidToPidMap");
115
116 // NB: |process_list| may have inconsistent information because the
117 // ProcessSnapshotServer gets them by simply walking procfs. Especially
118 // we must not assume the parent-child relationships are consistent.
119
120 // Construct the process tree.
121 // NB: This can contain a loop in case of race conditions.
122 std::unordered_map<ProcessId, std::vector<ProcessId>> process_tree;
123 for (const base::ProcessEntry& entry : process_list)
124 process_tree[entry.parent_pid()].push_back(entry.pid());
125
126 ProcessId arc_init_pid = GetArcInitProcessId(process_list);
127
128 // Enumerate all processes under ARC init and create nspid -> pid map.
129 if (arc_init_pid != kNullProcessId) {
130 base::queue<ProcessId> queue;
131 std::unordered_set<ProcessId> visited;
132 queue.push(arc_init_pid);
133 while (!queue.empty()) {
134 ProcessId pid = queue.front();
135 queue.pop();
136 // Do not visit the same process twice. Otherwise we may enter an infinite
137 // loop if |process_tree| contains a loop.
138 if (!visited.insert(pid).second)
139 continue;
140
141 const ProcessId nspid = base::Process(pid).GetPidInNamespace();
142
143 // All ARC processes should be in namespace so nspid is usually non-null,
144 // but this can happen if the process has already gone.
145 // Only add processes we're interested in (those appear as keys in
146 // |pid_map|).
147 if (nspid != kNullProcessId && pid_map->find(nspid) != pid_map->end())
148 (*pid_map)[nspid] = pid;
149
150 for (ProcessId child_pid : process_tree[pid])
151 queue.push(child_pid);
152 }
153 }
154 }
155
FilterProcessList(const ArcProcessService::NSPidToPidMap & pid_map,std::vector<mojom::RunningAppProcessInfoPtr> processes)156 std::vector<ArcProcess> FilterProcessList(
157 const ArcProcessService::NSPidToPidMap& pid_map,
158 std::vector<mojom::RunningAppProcessInfoPtr> processes) {
159 std::vector<ArcProcess> ret_processes;
160 for (const auto& entry : processes) {
161 base::ProcessId pid;
162 if (arc::IsArcVmEnabled()) {
163 // When VM is enabled, there is no external pid. Set the pid here to the
164 // guest pid. Setting the pid to zero was considered but the task manager
165 // groups tasks by pid and this can cause incorrect aggregated stats to
166 // be displayed. The task manager will handle these cases by checking if
167 // the task is in VM (via Task::IsRunningInVM) and know to partition off
168 // these processes.
169 pid = entry->pid;
170 } else {
171 const auto it = pid_map.find(entry->pid);
172 // The nspid could be missing due to race condition. For example, the
173 // process is still running when we get the process snapshot and ends when
174 // we update the nspid to pid mapping.
175 if (it == pid_map.end() || it->second == base::kNullProcessId) {
176 continue;
177 }
178 pid = it->second;
179 }
180 // Constructs the ArcProcess instance if the mapping is found.
181 ArcProcess arc_process(entry->pid, pid, entry->process_name,
182 entry->process_state, entry->is_focused,
183 entry->last_activity_time);
184 // |entry->packages| is provided only when process.mojom's verion is >=4.
185 if (entry->packages) {
186 for (const auto& package : *entry->packages) {
187 arc_process.packages().push_back(package);
188 }
189 }
190 ret_processes.push_back(std::move(arc_process));
191 }
192 return ret_processes;
193 }
194
UpdateAndReturnProcessList(const base::ProcessIterator::ProcessEntries & process_list,scoped_refptr<ArcProcessService::NSPidToPidMap> nspid_map,std::vector<mojom::RunningAppProcessInfoPtr> processes)195 std::vector<ArcProcess> UpdateAndReturnProcessList(
196 const base::ProcessIterator::ProcessEntries& process_list,
197 scoped_refptr<ArcProcessService::NSPidToPidMap> nspid_map,
198 std::vector<mojom::RunningAppProcessInfoPtr> processes) {
199 ArcProcessService::NSPidToPidMap& pid_map = *nspid_map;
200 if (!arc::IsArcVmEnabled()) {
201 // Cleanup dead pids in the cache |pid_map|.
202 std::unordered_set<ProcessId> nspid_to_remove;
203 for (const auto& entry : pid_map) {
204 nspid_to_remove.insert(entry.first);
205 }
206 bool unmapped_nspid = false;
207 for (const auto& entry : processes) {
208 // erase() returns 0 if coudln't find the key. It means a new process.
209 if (nspid_to_remove.erase(entry->pid) == 0) {
210 pid_map[entry->pid] = base::kNullProcessId;
211 unmapped_nspid = true;
212 }
213 }
214 for (const auto& entry : nspid_to_remove) {
215 pid_map.erase(entry);
216 }
217
218 // The operation is costly so avoid calling it when possible.
219 if (unmapped_nspid) {
220 UpdateNspidToPidMap(process_list, nspid_map);
221 }
222 }
223
224 return FilterProcessList(pid_map, std::move(processes));
225 }
226
UpdateAndReturnMemoryInfo(const base::ProcessIterator::ProcessEntries & process_list,scoped_refptr<ArcProcessService::NSPidToPidMap> nspid_map,std::vector<mojom::ArcMemoryDumpPtr> process_dumps)227 std::vector<mojom::ArcMemoryDumpPtr> UpdateAndReturnMemoryInfo(
228 const base::ProcessIterator::ProcessEntries& process_list,
229 scoped_refptr<ArcProcessService::NSPidToPidMap> nspid_map,
230 std::vector<mojom::ArcMemoryDumpPtr> process_dumps) {
231 if (!arc::IsArcVmEnabled()) {
232 ArcProcessService::NSPidToPidMap& pid_map = *nspid_map;
233 // Cleanup dead processes in pid_map
234 base::flat_set<ProcessId> nspid_to_remove;
235 for (const auto& entry : pid_map)
236 nspid_to_remove.insert(entry.first);
237
238 bool unmapped_nspid = false;
239 for (const auto& proc : process_dumps) {
240 // erase() returns 0 if couldn't find the key (new process)
241 if (nspid_to_remove.erase(proc->pid) == 0) {
242 pid_map[proc->pid] = base::kNullProcessId;
243 unmapped_nspid = true;
244 }
245 }
246 for (const auto& old_nspid : nspid_to_remove)
247 pid_map.erase(old_nspid);
248
249 if (unmapped_nspid)
250 UpdateNspidToPidMap(process_list, nspid_map);
251
252 // Return memory info only for processes that have a mapping nspid->pid
253 for (auto& proc : process_dumps) {
254 auto it = pid_map.find(proc->pid);
255 proc->pid = it == pid_map.end() ? kNullProcessId : it->second;
256 }
257 base::EraseIf(process_dumps,
258 [](const auto& proc) { return proc->pid == kNullProcessId; });
259 }
260 return process_dumps;
261 }
262
Reset(scoped_refptr<ArcProcessService::NSPidToPidMap> pid_map)263 void Reset(scoped_refptr<ArcProcessService::NSPidToPidMap> pid_map) {
264 if (pid_map.get())
265 pid_map->clear();
266 }
267
268 // Singleton factory for ArcProcessService.
269 class ArcProcessServiceFactory
270 : public internal::ArcBrowserContextKeyedServiceFactoryBase<
271 ArcProcessService,
272 ArcProcessServiceFactory> {
273 public:
274 // Factory name used by ArcBrowserContextKeyedServiceFactoryBase.
275 static constexpr const char* kName = "ArcProcessServiceFactory";
276
GetInstance()277 static ArcProcessServiceFactory* GetInstance() {
278 return base::Singleton<ArcProcessServiceFactory>::get();
279 }
280
281 private:
282 friend base::DefaultSingletonTraits<ArcProcessServiceFactory>;
283 ArcProcessServiceFactory() = default;
284 ~ArcProcessServiceFactory() override = default;
285 };
286
287 } // namespace
288
289 // static
GetForBrowserContext(content::BrowserContext * context)290 ArcProcessService* ArcProcessService::GetForBrowserContext(
291 content::BrowserContext* context) {
292 return ArcProcessServiceFactory::GetForBrowserContext(context);
293 }
294
ArcProcessService(content::BrowserContext * context,ArcBridgeService * bridge_service)295 ArcProcessService::ArcProcessService(content::BrowserContext* context,
296 ArcBridgeService* bridge_service)
297 : ProcessSnapshotServer::Observer(kProcessSnapshotRefreshTime),
298 arc_bridge_service_(bridge_service),
299 task_runner_(base::ThreadPool::CreateSequencedTaskRunner(
300 {base::MayBlock(), base::TaskShutdownBehavior::SKIP_ON_SHUTDOWN,
301 base::TaskPriority::USER_VISIBLE})),
302 nspid_to_pid_(new NSPidToPidMap()) {
303 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
304 arc_bridge_service_->process()->AddObserver(this);
305 }
306
~ArcProcessService()307 ArcProcessService::~ArcProcessService() {
308 arc_bridge_service_->process()->RemoveObserver(this);
309 if (is_observing_process_snapshot_)
310 ProcessSnapshotServer::Get()->RemoveObserver(this);
311 }
312
313 // static
314 constexpr base::TimeDelta ArcProcessService::kProcessSnapshotRefreshTime;
315
316 // static
Get()317 ArcProcessService* ArcProcessService::Get() {
318 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
319 // This is called from TaskManager implementation, which is isolated
320 // from BrowserContext.
321 // Use ArcServiceManager's BrowserContext instance, since 1) it is always
322 // allowed to use ARC, and 2) the rest of ARC service's lifetime are
323 // tied to it.
324 auto* arc_service_manager = ArcServiceManager::Get();
325 if (!arc_service_manager || !arc_service_manager->browser_context())
326 return nullptr;
327 return GetForBrowserContext(arc_service_manager->browser_context());
328 }
329
RequestAppProcessList(RequestProcessListCallback callback)330 void ArcProcessService::RequestAppProcessList(
331 RequestProcessListCallback callback) {
332 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
333 HandleRequest(
334 base::BindOnce(&ArcProcessService::ContinueAppProcessListRequest,
335 base::Unretained(this), std::move(callback)));
336 }
337
RequestSystemProcessList(RequestProcessListCallback callback)338 void ArcProcessService::RequestSystemProcessList(
339 RequestProcessListCallback callback) {
340 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
341 HandleRequest(
342 base::BindOnce(&ArcProcessService::ContinueSystemProcessListRequest,
343 base::Unretained(this), std::move(callback)));
344 }
345
RequestAppMemoryInfo(RequestMemoryInfoCallback callback)346 void ArcProcessService::RequestAppMemoryInfo(
347 RequestMemoryInfoCallback callback) {
348 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
349 HandleRequest(base::BindOnce(&ArcProcessService::ContinueAppMemoryInfoRequest,
350 base::Unretained(this), std::move(callback)));
351 }
352
RequestSystemMemoryInfo(RequestMemoryInfoCallback callback)353 void ArcProcessService::RequestSystemMemoryInfo(
354 RequestMemoryInfoCallback callback) {
355 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
356 HandleRequest(
357 base::BindOnce(&ArcProcessService::ContinueSystemMemoryInfoRequest,
358 base::Unretained(this), std::move(callback)));
359 }
360
OnProcessSnapshotRefreshed(const base::ProcessIterator::ProcessEntries & snapshot)361 void ArcProcessService::OnProcessSnapshotRefreshed(
362 const base::ProcessIterator::ProcessEntries& snapshot) {
363 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
364 cached_process_snapshot_ = snapshot;
365 last_process_snapshot_time_ = base::Time::Now();
366
367 // Handle any pending requests.
368 while (!pending_requests_.empty()) {
369 std::move(pending_requests_.front()).Run();
370 pending_requests_.pop();
371 }
372 }
373
OnReceiveProcessList(RequestProcessListCallback callback,std::vector<mojom::RunningAppProcessInfoPtr> processes)374 void ArcProcessService::OnReceiveProcessList(
375 RequestProcessListCallback callback,
376 std::vector<mojom::RunningAppProcessInfoPtr> processes) {
377 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
378 base::PostTaskAndReplyWithResult(
379 task_runner_.get(), FROM_HERE,
380 base::BindOnce(&UpdateAndReturnProcessList, cached_process_snapshot_,
381 nspid_to_pid_, std::move(processes)),
382 std::move(callback));
383
384 MaybeStopObservingProcessSnapshots();
385 }
386
OnReceiveMemoryInfo(RequestMemoryInfoCallback callback,std::vector<mojom::ArcMemoryDumpPtr> process_dumps)387 void ArcProcessService::OnReceiveMemoryInfo(
388 RequestMemoryInfoCallback callback,
389 std::vector<mojom::ArcMemoryDumpPtr> process_dumps) {
390 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
391 base::PostTaskAndReplyWithResult(
392 task_runner_.get(), FROM_HERE,
393 base::BindOnce(&UpdateAndReturnMemoryInfo, cached_process_snapshot_,
394 nspid_to_pid_, std::move(process_dumps)),
395 std::move(callback));
396
397 MaybeStopObservingProcessSnapshots();
398 }
399
OnGetSystemProcessList(RequestMemoryInfoCallback callback,std::vector<ArcProcess> procs)400 void ArcProcessService::OnGetSystemProcessList(
401 RequestMemoryInfoCallback callback,
402 std::vector<ArcProcess> procs) {
403 mojom::ProcessInstance* process_instance = ARC_GET_INSTANCE_FOR_METHOD(
404 arc_bridge_service_->process(), RequestSystemProcessMemoryInfo);
405 if (!process_instance) {
406 LOG(ERROR) << "could not find method / get ProcessInstance";
407 return;
408 }
409 std::vector<uint32_t> nspids;
410 if (!arc::IsArcVmEnabled()) {
411 for (const auto& proc : procs)
412 nspids.push_back(proc.nspid());
413 }
414 // TODO(b/122992194): Fix this for ARCVM.
415 process_instance->RequestSystemProcessMemoryInfo(
416 nspids,
417 base::BindOnce(&ArcProcessService::OnReceiveMemoryInfo,
418 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
419 }
420
OnConnectionReady()421 void ArcProcessService::OnConnectionReady() {
422 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
423 task_runner_->PostTask(FROM_HERE, base::BindOnce(&Reset, nspid_to_pid_));
424 connection_ready_ = true;
425 }
426
OnConnectionClosed()427 void ArcProcessService::OnConnectionClosed() {
428 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
429 connection_ready_ = false;
430 }
431
CanUseStaleProcessSnapshot() const432 bool ArcProcessService::CanUseStaleProcessSnapshot() const {
433 return base::Time::Now() - last_process_snapshot_time_ <=
434 kProcessSnapshotRefreshTime;
435 }
436
MaybeStopObservingProcessSnapshots()437 void ArcProcessService::MaybeStopObservingProcessSnapshots() {
438 if (!is_observing_process_snapshot_)
439 return;
440
441 // We can stop observing the ProcessSnapshotServer only if there are no more
442 // pending requests, and we have a recent enough |cached_process_snapshot_|.
443 const bool should_stop_observing =
444 pending_requests_.empty() && CanUseStaleProcessSnapshot();
445 if (!should_stop_observing)
446 return;
447
448 ProcessSnapshotServer::Get()->RemoveObserver(this);
449 is_observing_process_snapshot_ = false;
450 }
451
HandleRequest(base::OnceClosure request)452 void ArcProcessService::HandleRequest(base::OnceClosure request) {
453 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
454
455 if (CanUseStaleProcessSnapshot()) {
456 // Handle the request immediately.
457 std::move(request).Run();
458 return;
459 }
460
461 // We have a too stale |cached_process_snapshot_|, therefore request a fresher
462 // one by observing the ProcessSnapshotServer, and add |request| to the
463 // pending requests.
464 if (!is_observing_process_snapshot_) {
465 ProcessSnapshotServer::Get()->AddObserver(this);
466 is_observing_process_snapshot_ = true;
467 }
468
469 pending_requests_.push(std::move(request));
470 }
471
ContinueAppProcessListRequest(RequestProcessListCallback callback)472 void ArcProcessService::ContinueAppProcessListRequest(
473 RequestProcessListCallback callback) {
474 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
475 // Since several services call this class to get information about the ARC
476 // process list, it can produce a lot of logspam when the board is ARC-ready
477 // but the user has not opted into ARC. This redundant check avoids that
478 // logspam.
479 if (!connection_ready_) {
480 std::move(callback).Run(base::nullopt);
481 return;
482 }
483
484 mojom::ProcessInstance* process_instance = ARC_GET_INSTANCE_FOR_METHOD(
485 arc_bridge_service_->process(), RequestProcessList);
486 if (!process_instance) {
487 std::move(callback).Run(base::nullopt);
488 return;
489 }
490
491 process_instance->RequestProcessList(
492 base::BindOnce(&ArcProcessService::OnReceiveProcessList,
493 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
494 }
495
ContinueSystemProcessListRequest(RequestProcessListCallback callback)496 void ArcProcessService::ContinueSystemProcessListRequest(
497 RequestProcessListCallback callback) {
498 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
499 base::PostTaskAndReplyWithResult(
500 task_runner_.get(), FROM_HERE,
501 base::BindOnce(&GetArcSystemProcessList, cached_process_snapshot_),
502 std::move(callback));
503
504 MaybeStopObservingProcessSnapshots();
505 }
506
ContinueAppMemoryInfoRequest(RequestMemoryInfoCallback callback)507 void ArcProcessService::ContinueAppMemoryInfoRequest(
508 RequestMemoryInfoCallback callback) {
509 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
510 if (!connection_ready_) {
511 std::move(callback).Run({});
512 return;
513 }
514
515 mojom::ProcessInstance* process_instance = ARC_GET_INSTANCE_FOR_METHOD(
516 arc_bridge_service_->process(), RequestApplicationProcessMemoryInfo);
517 if (!process_instance) {
518 LOG(ERROR) << "could not find method / get ProcessInstance";
519 std::move(callback).Run({});
520 return;
521 }
522
523 process_instance->RequestApplicationProcessMemoryInfo(
524 base::BindOnce(&ArcProcessService::OnReceiveMemoryInfo,
525 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
526 return;
527 }
528
ContinueSystemMemoryInfoRequest(RequestMemoryInfoCallback callback)529 void ArcProcessService::ContinueSystemMemoryInfoRequest(
530 RequestMemoryInfoCallback callback) {
531 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
532 if (!connection_ready_) {
533 std::move(callback).Run({});
534 return;
535 }
536
537 base::PostTaskAndReplyWithResult(
538 task_runner_.get(), FROM_HERE,
539 base::BindOnce(&GetArcSystemProcessList, cached_process_snapshot_),
540 base::BindOnce(&ArcProcessService::OnGetSystemProcessList,
541 weak_ptr_factory_.GetWeakPtr(), std::move(callback)));
542 }
543
544 // -----------------------------------------------------------------------------
545 // ArcProcessService::NSPidToPidMap:
546
547 inline ArcProcessService::NSPidToPidMap::NSPidToPidMap() = default;
548
549 inline ArcProcessService::NSPidToPidMap::~NSPidToPidMap() = default;
550
551 } // namespace arc
552