blob: e9c0335c358e342038181ca231bf7a956fdabc0e [file] [log] [blame]
/*
* 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;
}
void ProcessTracker::EndThread(int64_t timestamp, uint32_t tid) {
UniqueTid utid = GetOrCreateThread(tid);
TraceStorage::Thread* thread = context_->storage->GetMutableThread(utid);
thread->end_ns = timestamp;
if (thread->upid.has_value()) {
TraceStorage::Process* process =
context_->storage->GetMutableProcess(thread->upid.value());
// If the process pid and thread tid are equal, then this is the main thread
// of the process.
if (process->pid == thread->tid) {
process->end_ns = timestamp;
}
}
}
base::Optional<UniqueTid> ProcessTracker::GetThreadOrNull(uint32_t tid) {
auto pair_it = tids_.equal_range(tid);
if (pair_it.first != pair_it.second) {
auto prev_utid = std::prev(pair_it.second)->second;
TraceStorage::Thread* thread =
context_->storage->GetMutableThread(prev_utid);
// Only return alive threads.
if (thread->end_ns == 0)
return prev_utid;
}
return base::nullopt;
}
UniqueTid ProcessTracker::GetOrCreateThread(uint32_t tid) {
auto utid = GetThreadOrNull(tid);
return utid ? utid.value() : StartNewThread(0, tid, 0);
}
UniqueTid ProcessTracker::UpdateThreadName(uint32_t tid,
StringId thread_name_id) {
auto utid = GetOrCreateThread(tid);
if (!thread_name_id.is_null()) {
TraceStorage::Thread* thread = context_->storage->GetMutableThread(utid);
thread->name_id = thread_name_id;
}
return utid;
}
void ProcessTracker::SetThreadNameIfUnset(UniqueTid utid,
StringId thread_name_id) {
TraceStorage::Thread* thread = context_->storage->GetMutableThread(utid);
if (thread->name_id == kNullStringId)
thread->name_id = 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->end_ns != 0) {
// If the thread is already dead, don't bother choosing it as the
// thread for this process.
continue;
}
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.end_ns != 0) {
// If the process is already dead, don't bother choosing the associated
// thread.
continue;
}
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 parent_tid,
uint32_t pid,
StringId main_thread_name) {
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);
// Note that we erased the pid above so this should always return a new
// process.
std::pair<UniquePid, TraceStorage::Process*> process =
GetOrCreateProcessPtr(pid);
PERFETTO_DCHECK(process.second->name_id == 0);
process.second->start_ns = timestamp;
process.second->name_id = main_thread_name;
UniqueTid parent_utid = GetOrCreateThread(parent_tid);
auto* parent_thread = context_->storage->GetMutableThread(parent_utid);
if (parent_thread->upid.has_value()) {
process.second->parent_upid = parent_thread->upid.value();
} else {
pending_parent_assocs_.emplace_back(parent_utid, process.first);
}
return process.first;
}
UniquePid ProcessTracker::SetProcessMetadata(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->parent_upid = pupid;
return upid;
}
void ProcessTracker::SetProcessNameIfUnset(UniquePid upid,
StringId process_name_id) {
TraceStorage::Process* process = context_->storage->GetMutableProcess(upid);
if (process->name_id == kNullStringId)
process->name_id = process_name_id;
}
void ProcessTracker::UpdateProcessNameFromThreadName(uint32_t tid,
StringId thread_name) {
auto utid = GetOrCreateThread(tid);
TraceStorage::Thread* thread = context_->storage->GetMutableThread(utid);
if (thread->upid.has_value()) {
auto* process = context_->storage->GetMutableProcess(thread->upid.value());
if (process->pid == tid) {
process->name_id = thread_name;
}
}
}
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_parent_assocs_.begin();
it != pending_parent_assocs_.end();) {
UniqueTid parent_utid = it->first;
UniquePid child_upid = it->second;
if (parent_utid != utid) {
++it;
continue;
}
PERFETTO_DCHECK(child_upid != upid);
// Set the parent pid of the other process
auto* child_proc = context_->storage->GetMutableProcess(child_upid);
PERFETTO_DCHECK(!child_proc->parent_upid ||
child_proc->parent_upid == upid);
child_proc->parent_upid = upid;
// Erase the pair. The |pending_parent_assocs_| vector is not sorted and
// swapping a std::pair<uint32_t, uint32_t> is cheap.
std::swap(*it, pending_parent_assocs_.back());
pending_parent_assocs_.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