blob: ebf340c166dcdb3fbbde17a097e34e4d9467c058 [file] [edit]
// Copyright 2026 Google Inc. All Rights Reserved.
//
// 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 "process_tree.h"
#include "util.h"
namespace {
/// Format a command line vector into a single string for display.
std::string FormatCommandLine(const std::vector<std::string>& args) {
std::string result;
for (size_t i = 0; i < args.size(); ++i) {
if (i > 0)
result += " ";
result += args[i];
}
return result;
}
} // namespace
ProcessTree::ProcessTree(Pid root_pid, const SystemSnapshot& snapshot)
: root_pid_(root_pid) {
// Recursively find and store only the descendants of root_pid using the
// pre-built snapshot indexes.
BuildFrom(root_pid, snapshot.ppid_to_children, snapshot.pid_to_info);
}
// static
ProcessTree::SystemSnapshot ProcessTree::TakeSystemSnapshot() {
SystemSnapshot snapshot;
// Platform-specific gathering of the flat process list.
snapshot.processes = GetSystemProcesses();
// Build the indexes once for all subsequent ProcessTree constructions.
for (const auto& proc : snapshot.processes) {
snapshot.ppid_to_children[proc.ppid].push_back(proc.pid);
snapshot.pid_to_info[proc.pid] = &proc;
}
return snapshot;
}
void ProcessTree::BuildFrom(
Pid parent_pid,
const std::unordered_map<Pid, std::vector<Pid>>& ppid_to_children,
const std::unordered_map<Pid, const ProcessInfo*>& pid_to_info) {
auto it = ppid_to_children.find(parent_pid);
if (it == ppid_to_children.end())
return;
for (Pid child_pid : it->second) {
auto proc_it = pid_to_info.find(child_pid);
if (proc_it != pid_to_info.end()) {
all_processes_[child_pid] = *proc_it->second;
ppid_to_children_[parent_pid].push_back(child_pid);
BuildFrom(child_pid, ppid_to_children, pid_to_info);
}
}
}
void ProcessTree::CollectDetails() {
for (auto& pair : all_processes_) {
if (!pair.second.details.collected) {
GetSystemProcessDetails(pair.first, &pair.second.details);
}
}
}
void ProcessTree::Print(
int indent, const std::unordered_map<Pid, std::string>& known_descriptions,
std::string* out) const {
// Internal recursive printer.
auto print_recursive = [&](Pid parent_pid, int current_indent,
auto& self) -> void {
auto it = ppid_to_children_.find(parent_pid);
if (it == ppid_to_children_.end())
return;
for (Pid child_pid : it->second) {
auto proc_it = all_processes_.find(child_pid);
if (proc_it == all_processes_.end())
continue;
const ProcessInfo& child = proc_it->second;
std::string indent_str(current_indent * 2, ' ');
std::string description;
// Priority 1: Use Ninja's own description if available.
auto desc_it = known_descriptions.find(child.pid);
if (desc_it != known_descriptions.end()) {
description = desc_it->second;
}
// Priority 2: Use collected command line if available.
else if (child.details.collected && !child.details.command_line.empty()) {
description = FormatCommandLine(child.details.command_line);
}
// Priority 3: Fallback to short executable name.
else {
description = "[" + child.name + "]";
}
*out += StringFormat("%s- [PID %6d]: %s\n", indent_str.c_str(),
(int)child.pid, description.c_str());
self(child.pid, current_indent + 1, self);
}
};
print_recursive(root_pid_, indent, print_recursive);
}