/*
|
* Copyright (C) 2018 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
#include "src/trace_processor/process_tracker.h"
|
#include "src/trace_processor/stats.h"
|
|
#include <utility>
|
|
#include <inttypes.h>
|
|
namespace perfetto {
|
namespace trace_processor {
|
|
ProcessTracker::ProcessTracker(TraceProcessorContext* context)
|
: context_(context) {
|
// Create a mapping from (t|p)id 0 -> u(t|p)id 0 for the idle process.
|
tids_.emplace(0, 0);
|
pids_.emplace(0, 0);
|
}
|
|
ProcessTracker::~ProcessTracker() = default;
|
|
UniqueTid ProcessTracker::StartNewThread(int64_t timestamp,
|
uint32_t tid,
|
StringId thread_name_id) {
|
UniqueTid new_utid = context_->storage->AddEmptyThread(tid);
|
TraceStorage::Thread* thread = context_->storage->GetMutableThread(new_utid);
|
thread->name_id = thread_name_id;
|
thread->start_ns = timestamp;
|
tids_.emplace(tid, new_utid);
|
return new_utid;
|
}
|
|
UniqueTid ProcessTracker::GetOrCreateThread(uint32_t tid) {
|
auto pair_it = tids_.equal_range(tid);
|
if (pair_it.first != pair_it.second) {
|
return std::prev(pair_it.second)->second;
|
}
|
return StartNewThread(0, tid, 0);
|
}
|
|
UniqueTid ProcessTracker::UpdateThreadName(uint32_t tid,
|
StringId thread_name_id) {
|
auto pair_it = tids_.equal_range(tid);
|
|
// If a utid exists for the tid, find it and update the name.
|
if (pair_it.first != pair_it.second) {
|
auto prev_utid = std::prev(pair_it.second)->second;
|
TraceStorage::Thread* thread =
|
context_->storage->GetMutableThread(prev_utid);
|
if (thread_name_id)
|
thread->name_id = thread_name_id;
|
return prev_utid;
|
}
|
|
// If none exist, assign a new utid and store it.
|
return StartNewThread(0, tid, thread_name_id);
|
}
|
|
UniqueTid ProcessTracker::UpdateThread(uint32_t tid, uint32_t pid) {
|
auto tids_pair = tids_.equal_range(tid);
|
|
// Try looking for a thread that matches both tid and thread group id (pid).
|
TraceStorage::Thread* thread = nullptr;
|
UniqueTid utid = 0;
|
for (auto it = tids_pair.first; it != tids_pair.second; it++) {
|
UniqueTid iter_utid = it->second;
|
auto* iter_thread = context_->storage->GetMutableThread(iter_utid);
|
if (!iter_thread->upid.has_value()) {
|
// We haven't discovered the parent process for the thread. Assign it
|
// now and use this thread.
|
thread = iter_thread;
|
utid = iter_utid;
|
break;
|
}
|
const auto& iter_process =
|
context_->storage->GetProcess(iter_thread->upid.value());
|
if (iter_process.pid == pid) {
|
// We found a thread that matches both the tid and its parent pid.
|
thread = iter_thread;
|
utid = iter_utid;
|
break;
|
}
|
} // for(tids).
|
|
// If no matching thread was found, create a new one.
|
if (thread == nullptr) {
|
utid = context_->storage->AddEmptyThread(tid);
|
tids_.emplace(tid, utid);
|
thread = context_->storage->GetMutableThread(utid);
|
}
|
|
// Find matching process or create new one.
|
if (!thread->upid.has_value()) {
|
thread->upid = GetOrCreateProcess(pid);
|
}
|
|
ResolvePendingAssociations(utid, *thread->upid);
|
|
return utid;
|
}
|
|
UniquePid ProcessTracker::StartNewProcess(int64_t timestamp, uint32_t pid) {
|
pids_.erase(pid);
|
|
// Create a new UTID for the main thread, so we don't end up reusing an old
|
// entry in case of TID recycling.
|
StartNewThread(timestamp, /*tid=*/pid, 0);
|
|
std::pair<UniquePid, TraceStorage::Process*> process =
|
GetOrCreateProcessPtr(pid);
|
process.second->start_ns = timestamp;
|
return process.first;
|
}
|
|
UniquePid ProcessTracker::UpdateProcess(uint32_t pid,
|
base::Optional<uint32_t> ppid,
|
base::StringView name) {
|
auto proc_name_id = context_->storage->InternString(name);
|
|
base::Optional<UniquePid> pupid;
|
if (ppid.has_value()) {
|
pupid = GetOrCreateProcess(ppid.value());
|
}
|
UniquePid upid;
|
TraceStorage::Process* process;
|
std::tie(upid, process) = GetOrCreateProcessPtr(pid);
|
process->name_id = proc_name_id;
|
process->pupid = pupid;
|
return upid;
|
}
|
|
UniquePid ProcessTracker::GetOrCreateProcess(uint32_t pid) {
|
return GetOrCreateProcessPtr(pid).first;
|
}
|
|
std::pair<UniquePid, TraceStorage::Process*>
|
ProcessTracker::GetOrCreateProcessPtr(uint32_t pid) {
|
UniquePid upid;
|
auto it = pids_.find(pid);
|
if (it != pids_.end()) {
|
upid = it->second;
|
} else {
|
upid = context_->storage->AddEmptyProcess(pid);
|
pids_.emplace(pid, upid);
|
|
// Create an entry for the main thread.
|
// We cannot call StartNewThread() here, because threads for this process
|
// (including the main thread) might have been seen already prior to this
|
// call. This call usually comes from the ProcessTree dump which is delayed.
|
UpdateThread(/*tid=*/pid, pid);
|
}
|
return std::make_pair(upid, context_->storage->GetMutableProcess(upid));
|
}
|
|
void ProcessTracker::AssociateThreads(UniqueTid utid1, UniqueTid utid2) {
|
TraceStorage::Thread* thd1 = context_->storage->GetMutableThread(utid1);
|
TraceStorage::Thread* thd2 = context_->storage->GetMutableThread(utid2);
|
|
// First of all check if one of the two threads is already bound to a process.
|
// If that is the case, map the other thread to the same process and resolve
|
// recursively any associations pending on the other thread.
|
|
if (thd1->upid.has_value() && !thd2->upid.has_value()) {
|
thd2->upid = *thd1->upid;
|
ResolvePendingAssociations(utid2, *thd1->upid);
|
return;
|
}
|
|
if (thd2->upid.has_value() && !thd1->upid.has_value()) {
|
thd1->upid = *thd2->upid;
|
ResolvePendingAssociations(utid1, *thd2->upid);
|
return;
|
}
|
|
if (thd1->upid.has_value() && thd1->upid != thd2->upid) {
|
// Cannot associate two threads that belong to two different processes.
|
PERFETTO_ELOG("Process tracker failure. Cannot associate threads %u, %u",
|
thd1->tid, thd2->tid);
|
context_->storage->IncrementStats(stats::process_tracker_errors);
|
return;
|
}
|
|
pending_assocs_.emplace_back(utid1, utid2);
|
}
|
|
void ProcessTracker::ResolvePendingAssociations(UniqueTid utid_arg,
|
UniquePid upid) {
|
PERFETTO_DCHECK(context_->storage->GetMutableThread(utid_arg)->upid == upid);
|
std::vector<UniqueTid> resolved_utids;
|
resolved_utids.emplace_back(utid_arg);
|
|
while (!resolved_utids.empty()) {
|
UniqueTid utid = resolved_utids.back();
|
resolved_utids.pop_back();
|
for (auto it = pending_assocs_.begin(); it != pending_assocs_.end();) {
|
UniqueTid other_utid;
|
if (it->first == utid) {
|
other_utid = it->second;
|
} else if (it->second == utid) {
|
other_utid = it->first;
|
} else {
|
++it;
|
continue;
|
}
|
|
PERFETTO_DCHECK(other_utid != utid);
|
|
// Update the other thread and associated it to the same process.
|
auto* other_thd = context_->storage->GetMutableThread(other_utid);
|
PERFETTO_DCHECK(!other_thd->upid || other_thd->upid == upid);
|
other_thd->upid = upid;
|
|
// Erase the pair. The |pending_assocs_| vector is not sorted and swapping
|
// a std::pair<uint32_t, uint32_t> is cheap.
|
std::swap(*it, pending_assocs_.back());
|
pending_assocs_.pop_back();
|
|
// Recurse into the newly resolved thread. Some other threads might have
|
// been bound to that.
|
resolved_utids.emplace_back(other_utid);
|
}
|
} // while (!resolved_utids.empty())
|
}
|
|
} // namespace trace_processor
|
} // namespace perfetto
|