| // 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); |
| } |