• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "src/trace_processor/importers/common/process_tracker.h"
18 #include "src/trace_processor/storage/stats.h"
19 
20 #include <cinttypes>
21 #include <utility>
22 
23 namespace perfetto {
24 namespace trace_processor {
25 
ProcessTracker(TraceProcessorContext * context)26 ProcessTracker::ProcessTracker(TraceProcessorContext* context)
27     : context_(context), args_tracker_(context) {
28   // Reserve utid/upid 0. These are special as embedders (e.g. Perfetto UI)
29   // exclude them from certain views (e.g. thread state) under the assumption
30   // that they correspond to the idle (swapper) process. When parsing Linux
31   // system traces, SetPidZeroIsUpidZeroIdleProcess will be called to associate
32   // tid0/pid0 to utid0/upid0. If other types of traces refer to tid0/pid0,
33   // then they will get their own non-zero utid/upid, so that those threads are
34   // still surfaced in embedder UIs.
35   tables::ThreadTable::Row thread_row;
36   thread_row.tid = 0u;
37   thread_row.upid = 0u;
38   thread_row.is_main_thread = true;
39   context_->storage->mutable_thread_table()->Insert(thread_row);
40 
41   tables::ProcessTable::Row process_row;
42   process_row.pid = 0u;
43   context_->storage->mutable_process_table()->Insert(process_row);
44 
45   // An element to match the reserved tid = 0.
46   thread_name_priorities_.push_back(ThreadNamePriority::kOther);
47 }
48 
49 ProcessTracker::~ProcessTracker() = default;
50 
StartNewThread(std::optional<int64_t> timestamp,uint32_t tid)51 UniqueTid ProcessTracker::StartNewThread(std::optional<int64_t> timestamp,
52                                          uint32_t tid) {
53   tables::ThreadTable::Row row;
54   row.tid = tid;
55   row.start_ts = timestamp;
56 
57   auto* thread_table = context_->storage->mutable_thread_table();
58   UniqueTid new_utid = thread_table->Insert(row).row;
59   tids_[tid].emplace_back(new_utid);
60   PERFETTO_DCHECK(thread_name_priorities_.size() == new_utid);
61   thread_name_priorities_.push_back(ThreadNamePriority::kOther);
62   return new_utid;
63 }
64 
EndThread(int64_t timestamp,uint32_t tid)65 void ProcessTracker::EndThread(int64_t timestamp, uint32_t tid) {
66   auto* thread_table = context_->storage->mutable_thread_table();
67   auto* process_table = context_->storage->mutable_process_table();
68 
69   // Don't bother creating a new thread if we're just going to
70   // end it straight away.
71   //
72   // This is useful in situations where we get a sched_process_free event for a
73   // worker thread in a process *after* the main thread finishes - in that case
74   // we would have already ended the process and we don't want to
75   // create a new thread here (see b/193520421 for an example of a trace
76   // where this happens in practice).
77   std::optional<UniqueTid> opt_utid = GetThreadOrNull(tid);
78   if (!opt_utid)
79     return;
80 
81   UniqueTid utid = *opt_utid;
82   thread_table->mutable_end_ts()->Set(utid, timestamp);
83 
84   // Remove the thread from the list of threads being tracked as any event after
85   // this one should be ignored.
86   auto& vector = tids_[tid];
87   vector.erase(std::remove(vector.begin(), vector.end(), utid));
88 
89   auto opt_upid = thread_table->upid()[utid];
90   if (!opt_upid.has_value() || process_table->pid()[*opt_upid] != tid)
91     return;
92 
93   // If the process pid and thread tid are equal then, as is the main thread
94   // of the process, we should also finish the process itself.
95   PERFETTO_DCHECK(thread_table->is_main_thread()[utid].value());
96   process_table->mutable_end_ts()->Set(*opt_upid, timestamp);
97   pids_.Erase(tid);
98 }
99 
GetThreadOrNull(uint32_t tid)100 std::optional<UniqueTid> ProcessTracker::GetThreadOrNull(uint32_t tid) {
101   auto opt_utid = GetThreadOrNull(tid, std::nullopt);
102   if (!opt_utid)
103     return std::nullopt;
104 
105   auto* threads = context_->storage->mutable_thread_table();
106   UniqueTid utid = *opt_utid;
107 
108   // Ensure that the tid matches the tid we were looking for.
109   PERFETTO_DCHECK(threads->tid()[utid] == tid);
110 
111   // If the thread is being tracked by the process tracker, it should not be
112   // known to have ended.
113   PERFETTO_DCHECK(!threads->end_ts()[utid].has_value());
114 
115   return utid;
116 }
117 
GetOrCreateThread(uint32_t tid)118 UniqueTid ProcessTracker::GetOrCreateThread(uint32_t tid) {
119   auto utid = GetThreadOrNull(tid);
120   return utid ? *utid : StartNewThread(std::nullopt, tid);
121 }
122 
UpdateThreadName(uint32_t tid,StringId thread_name_id,ThreadNamePriority priority)123 UniqueTid ProcessTracker::UpdateThreadName(uint32_t tid,
124                                            StringId thread_name_id,
125                                            ThreadNamePriority priority) {
126   auto utid = GetOrCreateThread(tid);
127   UpdateThreadNameByUtid(utid, thread_name_id, priority);
128   return utid;
129 }
130 
UpdateThreadNameByUtid(UniqueTid utid,StringId thread_name_id,ThreadNamePriority priority)131 void ProcessTracker::UpdateThreadNameByUtid(UniqueTid utid,
132                                             StringId thread_name_id,
133                                             ThreadNamePriority priority) {
134   if (thread_name_id.is_null())
135     return;
136 
137   auto* thread_table = context_->storage->mutable_thread_table();
138   if (priority >= thread_name_priorities_[utid]) {
139     thread_table->mutable_name()->Set(utid, thread_name_id);
140     thread_name_priorities_[utid] = priority;
141   }
142 }
143 
IsThreadAlive(UniqueTid utid)144 bool ProcessTracker::IsThreadAlive(UniqueTid utid) {
145   auto* threads = context_->storage->mutable_thread_table();
146   auto* processes = context_->storage->mutable_process_table();
147 
148   // If the thread has an end ts, it's certainly dead.
149   if (threads->end_ts()[utid].has_value())
150     return false;
151 
152   // If we don't know the parent process, we have to consider this thread alive.
153   auto opt_current_upid = threads->upid()[utid];
154   if (!opt_current_upid)
155     return true;
156 
157   // If the process is already dead, the thread can't be alive.
158   UniquePid current_upid = *opt_current_upid;
159   if (processes->end_ts()[current_upid].has_value())
160     return false;
161 
162   // If the process has been replaced in |pids_|, this thread is dead.
163   uint32_t current_pid = processes->pid()[current_upid];
164   auto pid_it = pids_.Find(current_pid);
165   if (pid_it && *pid_it != current_upid)
166     return false;
167 
168   return true;
169 }
170 
GetThreadOrNull(uint32_t tid,std::optional<uint32_t> pid)171 std::optional<UniqueTid> ProcessTracker::GetThreadOrNull(
172     uint32_t tid,
173     std::optional<uint32_t> pid) {
174   auto* threads = context_->storage->mutable_thread_table();
175   auto* processes = context_->storage->mutable_process_table();
176 
177   auto vector_it = tids_.Find(tid);
178   if (!vector_it)
179     return std::nullopt;
180 
181   // Iterate backwards through the threads so ones later in the trace are more
182   // likely to be picked.
183   const auto& vector = *vector_it;
184   for (auto it = vector.rbegin(); it != vector.rend(); it++) {
185     UniqueTid current_utid = *it;
186 
187     // If we finished this thread, we should have removed it from the vector
188     // entirely.
189     PERFETTO_DCHECK(!threads->end_ts()[current_utid].has_value());
190 
191     // If the thread is dead, ignore it.
192     if (!IsThreadAlive(current_utid))
193       continue;
194 
195     // If we don't know the parent process, we have to choose this thread.
196     auto opt_current_upid = threads->upid()[current_utid];
197     if (!opt_current_upid)
198       return current_utid;
199 
200     // We found a thread that matches both the tid and its parent pid.
201     uint32_t current_pid = processes->pid()[*opt_current_upid];
202     if (!pid || current_pid == *pid)
203       return current_utid;
204   }
205   return std::nullopt;
206 }
207 
UpdateThread(uint32_t tid,uint32_t pid)208 UniqueTid ProcessTracker::UpdateThread(uint32_t tid, uint32_t pid) {
209   auto* thread_table = context_->storage->mutable_thread_table();
210 
211   // Try looking for a thread that matches both tid and thread group id (pid).
212   std::optional<UniqueTid> opt_utid = GetThreadOrNull(tid, pid);
213 
214   // If no matching thread was found, create a new one.
215   UniqueTid utid = opt_utid ? *opt_utid : StartNewThread(std::nullopt, tid);
216   PERFETTO_DCHECK(thread_table->tid()[utid] == tid);
217 
218   // Find matching process or create new one.
219   if (!thread_table->upid()[utid].has_value()) {
220     AssociateThreadToProcess(utid, GetOrCreateProcess(pid));
221   }
222 
223   ResolvePendingAssociations(utid, *thread_table->upid()[utid]);
224 
225   return utid;
226 }
227 
UpdateTrustedPid(uint32_t trusted_pid,uint64_t uuid)228 void ProcessTracker::UpdateTrustedPid(uint32_t trusted_pid, uint64_t uuid) {
229   trusted_pids_[uuid] = trusted_pid;
230 }
231 
GetTrustedPid(uint64_t uuid)232 std::optional<uint32_t> ProcessTracker::GetTrustedPid(uint64_t uuid) {
233   if (trusted_pids_.find(uuid) == trusted_pids_.end())
234     return std::nullopt;
235   return trusted_pids_[uuid];
236 }
237 
ResolveNamespacedTid(uint32_t root_level_pid,uint32_t tid)238 std::optional<uint32_t> ProcessTracker::ResolveNamespacedTid(
239     uint32_t root_level_pid,
240     uint32_t tid) {
241   if (root_level_pid <= 0)  // Not a valid pid.
242     return std::nullopt;
243 
244   // If the process doesn't run in a namespace (or traced_probes doesn't observe
245   // that), return std::nullopt as failure to resolve.
246   auto process_it = namespaced_processes_.find(root_level_pid);
247   if (process_it == namespaced_processes_.end())
248     return std::nullopt;
249 
250   // Check if it's the main thread.
251   const auto& process = process_it->second;
252   auto ns_level = process.nspid.size() - 1;
253   auto pid_local = process.nspid.back();
254   if (pid_local == tid)
255     return root_level_pid;
256 
257   // Check if any non-main thread has a matching ns-local thread ID.
258   for (const auto& root_level_tid : process.threads) {
259     const auto& thread = namespaced_threads_[root_level_tid];
260     PERFETTO_DCHECK(thread.nstid.size() > ns_level);
261     auto tid_ns_local = thread.nstid[ns_level];
262     if (tid_ns_local == tid)
263       return thread.tid;
264   }
265 
266   // Failed to resolve or the thread isn't namespaced
267   return std::nullopt;
268 }
269 
StartNewProcess(std::optional<int64_t> timestamp,std::optional<uint32_t> parent_tid,uint32_t pid,StringId main_thread_name,ThreadNamePriority priority)270 UniquePid ProcessTracker::StartNewProcess(std::optional<int64_t> timestamp,
271                                           std::optional<uint32_t> parent_tid,
272                                           uint32_t pid,
273                                           StringId main_thread_name,
274                                           ThreadNamePriority priority) {
275   pids_.Erase(pid);
276   // TODO(eseckler): Consider erasing all old entries in |tids_| that match the
277   // |pid| (those would be for an older process with the same pid). Right now,
278   // we keep them in |tids_| (if they weren't erased by EndThread()), but ignore
279   // them in GetThreadOrNull().
280 
281   // Create a new UTID for the main thread, so we don't end up reusing an old
282   // entry in case of TID recycling.
283   UniqueTid utid = StartNewThread(timestamp, /*tid=*/pid);
284   UpdateThreadNameByUtid(utid, main_thread_name, priority);
285 
286   // Note that we erased the pid above so this should always return a new
287   // process.
288   UniquePid upid = GetOrCreateProcess(pid);
289 
290   auto* process_table = context_->storage->mutable_process_table();
291   auto* thread_table = context_->storage->mutable_thread_table();
292 
293   PERFETTO_DCHECK(!process_table->name()[upid].has_value());
294   PERFETTO_DCHECK(!process_table->start_ts()[upid].has_value());
295 
296   if (timestamp) {
297     process_table->mutable_start_ts()->Set(upid, *timestamp);
298   }
299   process_table->mutable_name()->Set(upid, main_thread_name);
300 
301   if (parent_tid) {
302     UniqueTid parent_utid = GetOrCreateThread(*parent_tid);
303     auto opt_parent_upid = thread_table->upid()[parent_utid];
304     if (opt_parent_upid.has_value()) {
305       process_table->mutable_parent_upid()->Set(upid, *opt_parent_upid);
306     } else {
307       pending_parent_assocs_.emplace_back(parent_utid, upid);
308     }
309   }
310   return upid;
311 }
312 
SetProcessMetadata(uint32_t pid,std::optional<uint32_t> ppid,base::StringView name,base::StringView cmdline)313 UniquePid ProcessTracker::SetProcessMetadata(uint32_t pid,
314                                              std::optional<uint32_t> ppid,
315                                              base::StringView name,
316                                              base::StringView cmdline) {
317   std::optional<UniquePid> pupid;
318   if (ppid.has_value()) {
319     pupid = GetOrCreateProcess(ppid.value());
320   }
321 
322   UniquePid upid = GetOrCreateProcess(pid);
323   auto* process_table = context_->storage->mutable_process_table();
324 
325   StringId proc_name_id = context_->storage->InternString(name);
326   process_table->mutable_name()->Set(upid, proc_name_id);
327   process_table->mutable_cmdline()->Set(
328       upid, context_->storage->InternString(cmdline));
329   if (pupid)
330     process_table->mutable_parent_upid()->Set(upid, *pupid);
331 
332   return upid;
333 }
334 
SetProcessUid(UniquePid upid,uint32_t uid)335 void ProcessTracker::SetProcessUid(UniquePid upid, uint32_t uid) {
336   auto* process_table = context_->storage->mutable_process_table();
337   process_table->mutable_uid()->Set(upid, uid);
338 
339   // The notion of the app ID (as derived from the uid) is defined in
340   // frameworks/base/core/java/android/os/UserHandle.java
341   process_table->mutable_android_appid()->Set(upid, uid % 100000);
342 }
343 
SetProcessNameIfUnset(UniquePid upid,StringId process_name_id)344 void ProcessTracker::SetProcessNameIfUnset(UniquePid upid,
345                                            StringId process_name_id) {
346   auto* process_table = context_->storage->mutable_process_table();
347   if (!process_table->name()[upid].has_value())
348     process_table->mutable_name()->Set(upid, process_name_id);
349 }
350 
SetStartTsIfUnset(UniquePid upid,int64_t start_ts_nanoseconds)351 void ProcessTracker::SetStartTsIfUnset(UniquePid upid,
352                                        int64_t start_ts_nanoseconds) {
353   auto* process_table = context_->storage->mutable_process_table();
354   if (!process_table->start_ts()[upid].has_value())
355     process_table->mutable_start_ts()->Set(upid, start_ts_nanoseconds);
356 }
357 
UpdateThreadNameAndMaybeProcessName(uint32_t tid,StringId thread_name,ThreadNamePriority priority)358 void ProcessTracker::UpdateThreadNameAndMaybeProcessName(
359     uint32_t tid,
360     StringId thread_name,
361     ThreadNamePriority priority) {
362   auto* thread_table = context_->storage->mutable_thread_table();
363   auto* process_table = context_->storage->mutable_process_table();
364 
365   UniqueTid utid = UpdateThreadName(tid, thread_name, priority);
366   std::optional<UniquePid> opt_upid = thread_table->upid()[utid];
367   if (opt_upid.has_value() && process_table->pid()[*opt_upid] == tid) {
368     PERFETTO_DCHECK(thread_table->is_main_thread()[utid]);
369     process_table->mutable_name()->Set(*opt_upid, thread_name);
370   }
371 }
372 
GetOrCreateProcess(uint32_t pid)373 UniquePid ProcessTracker::GetOrCreateProcess(uint32_t pid) {
374   auto* process_table = context_->storage->mutable_process_table();
375 
376   // If the insertion succeeds, we'll fill the upid below.
377   auto it_and_ins = pids_.Insert(pid, UniquePid{0});
378   if (!it_and_ins.second) {
379     // Ensure that the process has not ended.
380     PERFETTO_DCHECK(!process_table->end_ts()[*it_and_ins.first].has_value());
381     return *it_and_ins.first;
382   }
383 
384   tables::ProcessTable::Row row;
385   row.pid = pid;
386 
387   UniquePid upid = process_table->Insert(row).row;
388   *it_and_ins.first = upid;  // Update the newly inserted hashmap entry.
389 
390   // Create an entry for the main thread.
391   // We cannot call StartNewThread() here, because threads for this process
392   // (including the main thread) might have been seen already prior to this
393   // call. This call usually comes from the ProcessTree dump which is delayed.
394   UpdateThread(/*tid=*/pid, pid);
395   return upid;
396 }
397 
AssociateThreads(UniqueTid utid1,UniqueTid utid2)398 void ProcessTracker::AssociateThreads(UniqueTid utid1, UniqueTid utid2) {
399   auto* tt = context_->storage->mutable_thread_table();
400 
401   // First of all check if one of the two threads is already bound to a process.
402   // If that is the case, map the other thread to the same process and resolve
403   // recursively any associations pending on the other thread.
404 
405   auto opt_upid1 = tt->upid()[utid1];
406   auto opt_upid2 = tt->upid()[utid2];
407 
408   if (opt_upid1.has_value() && !opt_upid2.has_value()) {
409     AssociateThreadToProcess(utid2, *opt_upid1);
410     ResolvePendingAssociations(utid2, *opt_upid1);
411     return;
412   }
413 
414   if (opt_upid2.has_value() && !opt_upid1.has_value()) {
415     AssociateThreadToProcess(utid1, *opt_upid2);
416     ResolvePendingAssociations(utid1, *opt_upid2);
417     return;
418   }
419 
420   if (opt_upid1.has_value() && opt_upid1 != opt_upid2) {
421     // Cannot associate two threads that belong to two different processes.
422     PERFETTO_ELOG("Process tracker failure. Cannot associate threads %u, %u",
423                   tt->tid()[utid1], tt->tid()[utid2]);
424     context_->storage->IncrementStats(stats::process_tracker_errors);
425     return;
426   }
427 
428   pending_assocs_.emplace_back(utid1, utid2);
429 }
430 
ResolvePendingAssociations(UniqueTid utid_arg,UniquePid upid)431 void ProcessTracker::ResolvePendingAssociations(UniqueTid utid_arg,
432                                                 UniquePid upid) {
433   auto* tt = context_->storage->mutable_thread_table();
434   auto* pt = context_->storage->mutable_process_table();
435   PERFETTO_DCHECK(tt->upid()[utid_arg] == upid);
436 
437   std::vector<UniqueTid> resolved_utids;
438   resolved_utids.emplace_back(utid_arg);
439 
440   while (!resolved_utids.empty()) {
441     UniqueTid utid = resolved_utids.back();
442     resolved_utids.pop_back();
443     for (auto it = pending_parent_assocs_.begin();
444          it != pending_parent_assocs_.end();) {
445       UniqueTid parent_utid = it->first;
446       UniquePid child_upid = it->second;
447 
448       if (parent_utid != utid) {
449         ++it;
450         continue;
451       }
452       PERFETTO_DCHECK(child_upid != upid);
453 
454       // Set the parent pid of the other process
455       PERFETTO_DCHECK(!pt->parent_upid()[child_upid] ||
456                       pt->parent_upid()[child_upid] == upid);
457       pt->mutable_parent_upid()->Set(child_upid, upid);
458 
459       // Erase the pair. The |pending_parent_assocs_| vector is not sorted and
460       // swapping a std::pair<uint32_t, uint32_t> is cheap.
461       std::swap(*it, pending_parent_assocs_.back());
462       pending_parent_assocs_.pop_back();
463     }
464 
465     auto end = pending_assocs_.end();
466     for (auto it = pending_assocs_.begin(); it != end;) {
467       UniqueTid other_utid;
468       if (it->first == utid) {
469         other_utid = it->second;
470       } else if (it->second == utid) {
471         other_utid = it->first;
472       } else {
473         ++it;
474         continue;
475       }
476 
477       PERFETTO_DCHECK(other_utid != utid);
478 
479       // Update the other thread and associated it to the same process.
480       PERFETTO_DCHECK(!tt->upid()[other_utid] ||
481                       tt->upid()[other_utid] == upid);
482       AssociateThreadToProcess(other_utid, upid);
483 
484       // Swap the current element to the end of the list and move the end
485       // iterator back. This works because |pending_assocs_| is not sorted. We
486       // do it this way rather than modifying |pending_assocs_| directly to
487       // prevent undefined behaviour caused by modifying a vector while
488       // iterating through it.
489       std::swap(*it, *(--end));
490 
491       // Recurse into the newly resolved thread. Some other threads might have
492       // been bound to that.
493       resolved_utids.emplace_back(other_utid);
494     }
495 
496     // Make sure to actually erase the utids which have been resolved.
497     pending_assocs_.erase(end, pending_assocs_.end());
498   }  // while (!resolved_utids.empty())
499 }
500 
AssociateThreadToProcess(UniqueTid utid,UniquePid upid)501 void ProcessTracker::AssociateThreadToProcess(UniqueTid utid, UniquePid upid) {
502   auto* thread_table = context_->storage->mutable_thread_table();
503   thread_table->mutable_upid()->Set(utid, upid);
504   auto* process_table = context_->storage->mutable_process_table();
505   bool main_thread = thread_table->tid()[utid] == process_table->pid()[upid];
506   thread_table->mutable_is_main_thread()->Set(utid, main_thread);
507 }
508 
SetPidZeroIsUpidZeroIdleProcess()509 void ProcessTracker::SetPidZeroIsUpidZeroIdleProcess() {
510   // Create a mapping from (t|p)id 0 -> u(t|p)id 0 for the idle process.
511   tids_.Insert(0, std::vector<UniqueTid>{0});
512   pids_.Insert(0, UniquePid{0});
513 
514   auto swapper_id = context_->storage->InternString("swapper");
515   UpdateThreadName(0, swapper_id, ThreadNamePriority::kTraceProcessorConstant);
516 }
517 
AddArgsTo(UniquePid upid)518 ArgsTracker::BoundInserter ProcessTracker::AddArgsTo(UniquePid upid) {
519   return args_tracker_.AddArgsTo(upid);
520 }
521 
NotifyEndOfFile()522 void ProcessTracker::NotifyEndOfFile() {
523   args_tracker_.Flush();
524   tids_.Clear();
525   pids_.Clear();
526   pending_assocs_.clear();
527   pending_parent_assocs_.clear();
528   thread_name_priorities_.clear();
529   trusted_pids_.clear();
530   namespaced_threads_.clear();
531   namespaced_processes_.clear();
532 }
533 
UpdateNamespacedProcess(uint32_t pid,std::vector<uint32_t> nspid)534 void ProcessTracker::UpdateNamespacedProcess(uint32_t pid,
535                                              std::vector<uint32_t> nspid) {
536   namespaced_processes_[pid] = {pid, std::move(nspid), {}};
537 }
538 
UpdateNamespacedThread(uint32_t pid,uint32_t tid,std::vector<uint32_t> nstid)539 void ProcessTracker::UpdateNamespacedThread(uint32_t pid,
540                                             uint32_t tid,
541                                             std::vector<uint32_t> nstid) {
542   PERFETTO_DCHECK(namespaced_processes_.find(pid) !=
543                   namespaced_processes_.end());
544   auto& process = namespaced_processes_[pid];
545   process.threads.emplace(tid);
546 
547   namespaced_threads_[tid] = {pid, tid, std::move(nstid)};
548 }
549 
550 }  // namespace trace_processor
551 }  // namespace perfetto
552