• 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/process_tracker.h"
18 #include "src/trace_processor/stats.h"
19 
20 #include <utility>
21 
22 #include <inttypes.h>
23 
24 namespace perfetto {
25 namespace trace_processor {
26 
ProcessTracker(TraceProcessorContext * context)27 ProcessTracker::ProcessTracker(TraceProcessorContext* context)
28     : context_(context) {
29   // Create a mapping from (t|p)id 0 -> u(t|p)id 0 for the idle process.
30   tids_.emplace(0, 0);
31   pids_.emplace(0, 0);
32 }
33 
34 ProcessTracker::~ProcessTracker() = default;
35 
StartNewThread(int64_t timestamp,uint32_t tid,StringId thread_name_id)36 UniqueTid ProcessTracker::StartNewThread(int64_t timestamp,
37                                          uint32_t tid,
38                                          StringId thread_name_id) {
39   UniqueTid new_utid = context_->storage->AddEmptyThread(tid);
40   TraceStorage::Thread* thread = context_->storage->GetMutableThread(new_utid);
41   thread->name_id = thread_name_id;
42   thread->start_ns = timestamp;
43   tids_.emplace(tid, new_utid);
44   return new_utid;
45 }
46 
GetOrCreateThread(uint32_t tid)47 UniqueTid ProcessTracker::GetOrCreateThread(uint32_t tid) {
48   auto pair_it = tids_.equal_range(tid);
49   if (pair_it.first != pair_it.second) {
50     return std::prev(pair_it.second)->second;
51   }
52   return StartNewThread(0, tid, 0);
53 }
54 
UpdateThreadName(uint32_t tid,StringId thread_name_id)55 UniqueTid ProcessTracker::UpdateThreadName(uint32_t tid,
56                                            StringId thread_name_id) {
57   auto pair_it = tids_.equal_range(tid);
58 
59   // If a utid exists for the tid, find it and update the name.
60   if (pair_it.first != pair_it.second) {
61     auto prev_utid = std::prev(pair_it.second)->second;
62     TraceStorage::Thread* thread =
63         context_->storage->GetMutableThread(prev_utid);
64     if (thread_name_id)
65       thread->name_id = thread_name_id;
66     return prev_utid;
67   }
68 
69   // If none exist, assign a new utid and store it.
70   return StartNewThread(0, tid, thread_name_id);
71 }
72 
UpdateThread(uint32_t tid,uint32_t pid)73 UniqueTid ProcessTracker::UpdateThread(uint32_t tid, uint32_t pid) {
74   auto tids_pair = tids_.equal_range(tid);
75 
76   // Try looking for a thread that matches both tid and thread group id (pid).
77   TraceStorage::Thread* thread = nullptr;
78   UniqueTid utid = 0;
79   for (auto it = tids_pair.first; it != tids_pair.second; it++) {
80     UniqueTid iter_utid = it->second;
81     auto* iter_thread = context_->storage->GetMutableThread(iter_utid);
82     if (!iter_thread->upid.has_value()) {
83       // We haven't discovered the parent process for the thread. Assign it
84       // now and use this thread.
85       thread = iter_thread;
86       utid = iter_utid;
87       break;
88     }
89     const auto& iter_process =
90         context_->storage->GetProcess(iter_thread->upid.value());
91     if (iter_process.pid == pid) {
92       // We found a thread that matches both the tid and its parent pid.
93       thread = iter_thread;
94       utid = iter_utid;
95       break;
96     }
97   }  // for(tids).
98 
99   // If no matching thread was found, create a new one.
100   if (thread == nullptr) {
101     utid = context_->storage->AddEmptyThread(tid);
102     tids_.emplace(tid, utid);
103     thread = context_->storage->GetMutableThread(utid);
104   }
105 
106   // Find matching process or create new one.
107   if (!thread->upid.has_value()) {
108     thread->upid = GetOrCreateProcess(pid);
109   }
110 
111   ResolvePendingAssociations(utid, *thread->upid);
112 
113   return utid;
114 }
115 
StartNewProcess(int64_t timestamp,uint32_t pid)116 UniquePid ProcessTracker::StartNewProcess(int64_t timestamp, uint32_t pid) {
117   pids_.erase(pid);
118 
119   // Create a new UTID for the main thread, so we don't end up reusing an old
120   // entry in case of TID recycling.
121   StartNewThread(timestamp, /*tid=*/pid, 0);
122 
123   std::pair<UniquePid, TraceStorage::Process*> process =
124       GetOrCreateProcessPtr(pid);
125   process.second->start_ns = timestamp;
126   return process.first;
127 }
128 
UpdateProcess(uint32_t pid,base::Optional<uint32_t> ppid,base::StringView name)129 UniquePid ProcessTracker::UpdateProcess(uint32_t pid,
130                                         base::Optional<uint32_t> ppid,
131                                         base::StringView name) {
132   auto proc_name_id = context_->storage->InternString(name);
133 
134   base::Optional<UniquePid> pupid;
135   if (ppid.has_value()) {
136     pupid = GetOrCreateProcess(ppid.value());
137   }
138   UniquePid upid;
139   TraceStorage::Process* process;
140   std::tie(upid, process) = GetOrCreateProcessPtr(pid);
141   process->name_id = proc_name_id;
142   process->pupid = pupid;
143   return upid;
144 }
145 
GetOrCreateProcess(uint32_t pid)146 UniquePid ProcessTracker::GetOrCreateProcess(uint32_t pid) {
147   return GetOrCreateProcessPtr(pid).first;
148 }
149 
150 std::pair<UniquePid, TraceStorage::Process*>
GetOrCreateProcessPtr(uint32_t pid)151 ProcessTracker::GetOrCreateProcessPtr(uint32_t pid) {
152   UniquePid upid;
153   auto it = pids_.find(pid);
154   if (it != pids_.end()) {
155     upid = it->second;
156   } else {
157     upid = context_->storage->AddEmptyProcess(pid);
158     pids_.emplace(pid, upid);
159 
160     // Create an entry for the main thread.
161     // We cannot call StartNewThread() here, because threads for this process
162     // (including the main thread) might have been seen already prior to this
163     // call. This call usually comes from the ProcessTree dump which is delayed.
164     UpdateThread(/*tid=*/pid, pid);
165   }
166   return std::make_pair(upid, context_->storage->GetMutableProcess(upid));
167 }
168 
AssociateThreads(UniqueTid utid1,UniqueTid utid2)169 void ProcessTracker::AssociateThreads(UniqueTid utid1, UniqueTid utid2) {
170   TraceStorage::Thread* thd1 = context_->storage->GetMutableThread(utid1);
171   TraceStorage::Thread* thd2 = context_->storage->GetMutableThread(utid2);
172 
173   // First of all check if one of the two threads is already bound to a process.
174   // If that is the case, map the other thread to the same process and resolve
175   // recursively any associations pending on the other thread.
176 
177   if (thd1->upid.has_value() && !thd2->upid.has_value()) {
178     thd2->upid = *thd1->upid;
179     ResolvePendingAssociations(utid2, *thd1->upid);
180     return;
181   }
182 
183   if (thd2->upid.has_value() && !thd1->upid.has_value()) {
184     thd1->upid = *thd2->upid;
185     ResolvePendingAssociations(utid1, *thd2->upid);
186     return;
187   }
188 
189   if (thd1->upid.has_value() && thd1->upid != thd2->upid) {
190     // Cannot associate two threads that belong to two different processes.
191     PERFETTO_ELOG("Process tracker failure. Cannot associate threads %u, %u",
192                   thd1->tid, thd2->tid);
193     context_->storage->IncrementStats(stats::process_tracker_errors);
194     return;
195   }
196 
197   pending_assocs_.emplace_back(utid1, utid2);
198 }
199 
ResolvePendingAssociations(UniqueTid utid_arg,UniquePid upid)200 void ProcessTracker::ResolvePendingAssociations(UniqueTid utid_arg,
201                                                 UniquePid upid) {
202   PERFETTO_DCHECK(context_->storage->GetMutableThread(utid_arg)->upid == upid);
203   std::vector<UniqueTid> resolved_utids;
204   resolved_utids.emplace_back(utid_arg);
205 
206   while (!resolved_utids.empty()) {
207     UniqueTid utid = resolved_utids.back();
208     resolved_utids.pop_back();
209     for (auto it = pending_assocs_.begin(); it != pending_assocs_.end();) {
210       UniqueTid other_utid;
211       if (it->first == utid) {
212         other_utid = it->second;
213       } else if (it->second == utid) {
214         other_utid = it->first;
215       } else {
216         ++it;
217         continue;
218       }
219 
220       PERFETTO_DCHECK(other_utid != utid);
221 
222       // Update the other thread and associated it to the same process.
223       auto* other_thd = context_->storage->GetMutableThread(other_utid);
224       PERFETTO_DCHECK(!other_thd->upid || other_thd->upid == upid);
225       other_thd->upid = upid;
226 
227       // Erase the pair. The |pending_assocs_| vector is not sorted and swapping
228       // a std::pair<uint32_t, uint32_t> is cheap.
229       std::swap(*it, pending_assocs_.back());
230       pending_assocs_.pop_back();
231 
232       // Recurse into the newly resolved thread. Some other threads might have
233       // been bound to that.
234       resolved_utids.emplace_back(other_utid);
235     }
236   }  // while (!resolved_utids.empty())
237 }
238 
239 }  // namespace trace_processor
240 }  // namespace perfetto
241