forked from NixOS/nix
-
Notifications
You must be signed in to change notification settings - Fork 7
Add nix ps command
#282
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Add nix ps command
#282
Changes from all commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
4ddb8b3
AutoDelete: Add move constructor
edolstra 8b57a52
Add basic `nix ps` command
edolstra 0f7b5b1
nix ps: Use cgroups to get all processes of a build
edolstra b5e4544
nix ps: Render the processes in a cgroup as a tree
edolstra 367b81d
Move terminal width calculation to libutil
edolstra d0daa4c
nix ps: Adapt to terminal width
edolstra 5b8fb29
nix ps: Support RemoteStore
edolstra 274b793
coderabbit review
edolstra 77e6c09
Move code
edolstra 33eab7f
Add worker protocol feature for queryActiveBuilds
edolstra c7e4be8
nix ps: Show how long a build has been running
edolstra b3aef46
Add getCgroupStats() function
edolstra 56824b8
nix ps: Show cgroup CPU stats
edolstra ada2fd4
Return per-process CPU time
edolstra de7101c
TODO
edolstra ec265c5
nix ps: Improve output formatting
edolstra 45b0ab5
Get per-process uid
edolstra 8cb1de9
Return user names if they exist
edolstra 0e604bc
When not using cgroups, use /proc to enumerate the children of a pid
edolstra c10a68f
Move table stuff into libutil
edolstra 2416099
Table: Use std::vectors
edolstra d4e5956
printTable(): Make destination stream explicit
edolstra 09fa833
Code review
edolstra 0910a53
Improve /proc/pid/stat parsing
edolstra c744ac3
nix ps: Use printTable()
edolstra ef95463
Rename cpuUser, cpuSystem to utime, stime
edolstra d60890d
Return cutime and cstime
edolstra d22ad22
nix ps: Add --json flag
edolstra b6336cc
nix ps: Update example
edolstra 906cd9d
nix ps: Improve formatting
edolstra c02a018
Serialize durations as floating-point seconds
edolstra 3771d17
nix ps: macOS support
edolstra 33a856f
Review comments
edolstra 72abd2a
nix ps: Check whether stdout is a tty
edolstra 383aec2
Remove questionable code
edolstra File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,145 @@ | ||
| #include "nix/store/active-builds.hh" | ||
| #include "nix/util/json-utils.hh" | ||
|
|
||
| #include <nlohmann/json.hpp> | ||
|
|
||
| #ifndef _WIN32 | ||
| # include <pwd.h> | ||
| #endif | ||
|
|
||
| namespace nix { | ||
|
|
||
| UserInfo UserInfo::fromUid(uid_t uid) | ||
| { | ||
| UserInfo info; | ||
| info.uid = uid; | ||
|
|
||
| #ifndef _WIN32 | ||
| // Look up the user name for the UID (thread-safe) | ||
| struct passwd pwd; | ||
| struct passwd * result; | ||
| std::vector<char> buf(16384); | ||
| if (getpwuid_r(uid, &pwd, buf.data(), buf.size(), &result) == 0 && result) | ||
| info.name = result->pw_name; | ||
| #endif | ||
|
|
||
| return info; | ||
| } | ||
|
|
||
| } // namespace nix | ||
|
|
||
| namespace nlohmann { | ||
|
|
||
| using namespace nix; | ||
|
|
||
| UserInfo adl_serializer<UserInfo>::from_json(const json & j) | ||
| { | ||
| return UserInfo{ | ||
| .uid = j.at("uid").get<uid_t>(), | ||
| .name = j.contains("name") && !j.at("name").is_null() | ||
| ? std::optional<std::string>(j.at("name").get<std::string>()) | ||
| : std::nullopt, | ||
| }; | ||
| } | ||
|
|
||
| void adl_serializer<UserInfo>::to_json(json & j, const UserInfo & info) | ||
| { | ||
| j = nlohmann::json{ | ||
| {"uid", info.uid}, | ||
| {"name", info.name}, | ||
| }; | ||
| } | ||
|
|
||
| // Durations are serialized as floats representing seconds. | ||
| static std::optional<std::chrono::microseconds> parseDuration(const json & j, const char * key) | ||
| { | ||
| if (j.contains(key) && !j.at(key).is_null()) | ||
| return std::chrono::duration_cast<std::chrono::microseconds>( | ||
| std::chrono::duration<float, std::chrono::seconds::period>(j.at(key).get<double>())); | ||
| else | ||
| return std::nullopt; | ||
| } | ||
|
|
||
| static nlohmann::json printDuration(const std::optional<std::chrono::microseconds> & duration) | ||
| { | ||
| return duration | ||
| ? nlohmann::json( | ||
| std::chrono::duration_cast<std::chrono::duration<float, std::chrono::seconds::period>>(*duration) | ||
| .count()) | ||
| : nullptr; | ||
| } | ||
|
|
||
| ActiveBuildInfo::ProcessInfo adl_serializer<ActiveBuildInfo::ProcessInfo>::from_json(const json & j) | ||
| { | ||
| return ActiveBuildInfo::ProcessInfo{ | ||
| .pid = j.at("pid").get<pid_t>(), | ||
| .parentPid = j.at("parentPid").get<pid_t>(), | ||
| .user = j.at("user").get<UserInfo>(), | ||
| .argv = j.at("argv").get<std::vector<std::string>>(), | ||
| .utime = parseDuration(j, "utime"), | ||
| .stime = parseDuration(j, "stime"), | ||
| .cutime = parseDuration(j, "cutime"), | ||
| .cstime = parseDuration(j, "cstime"), | ||
| }; | ||
| } | ||
|
|
||
| void adl_serializer<ActiveBuildInfo::ProcessInfo>::to_json(json & j, const ActiveBuildInfo::ProcessInfo & process) | ||
| { | ||
| j = nlohmann::json{ | ||
| {"pid", process.pid}, | ||
| {"parentPid", process.parentPid}, | ||
| {"user", process.user}, | ||
| {"argv", process.argv}, | ||
| {"utime", printDuration(process.utime)}, | ||
| {"stime", printDuration(process.stime)}, | ||
| {"cutime", printDuration(process.cutime)}, | ||
| {"cstime", printDuration(process.cstime)}, | ||
| }; | ||
| } | ||
|
|
||
| ActiveBuild adl_serializer<ActiveBuild>::from_json(const json & j) | ||
| { | ||
| return ActiveBuild{ | ||
| .nixPid = j.at("nixPid").get<pid_t>(), | ||
| .clientPid = j.at("clientPid").get<std::optional<pid_t>>(), | ||
| .clientUid = j.at("clientUid").get<std::optional<uid_t>>(), | ||
| .mainPid = j.at("mainPid").get<pid_t>(), | ||
| .mainUser = j.at("mainUser").get<UserInfo>(), | ||
| .cgroup = j.at("cgroup").get<std::optional<Path>>(), | ||
| .startTime = (time_t) j.at("startTime").get<double>(), | ||
| .derivation = StorePath{getString(j.at("derivation"))}, | ||
| }; | ||
| } | ||
|
|
||
| void adl_serializer<ActiveBuild>::to_json(json & j, const ActiveBuild & build) | ||
| { | ||
| j = nlohmann::json{ | ||
| {"nixPid", build.nixPid}, | ||
| {"clientPid", build.clientPid}, | ||
| {"clientUid", build.clientUid}, | ||
| {"mainPid", build.mainPid}, | ||
| {"mainUser", build.mainUser}, | ||
| {"cgroup", build.cgroup}, | ||
| {"startTime", (double) build.startTime}, | ||
| {"derivation", build.derivation.to_string()}, | ||
| }; | ||
| } | ||
|
|
||
| ActiveBuildInfo adl_serializer<ActiveBuildInfo>::from_json(const json & j) | ||
| { | ||
| ActiveBuildInfo info(adl_serializer<ActiveBuild>::from_json(j)); | ||
| info.processes = j.at("processes").get<std::vector<ActiveBuildInfo::ProcessInfo>>(); | ||
| info.utime = parseDuration(j, "utime"); | ||
| info.stime = parseDuration(j, "stime"); | ||
| return info; | ||
| } | ||
|
|
||
| void adl_serializer<ActiveBuildInfo>::to_json(json & j, const ActiveBuildInfo & build) | ||
| { | ||
| adl_serializer<ActiveBuild>::to_json(j, build); | ||
| j["processes"] = build.processes; | ||
| j["utime"] = printDuration(build.utime); | ||
| j["stime"] = printDuration(build.stime); | ||
| } | ||
|
|
||
| } // namespace nlohmann |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,108 @@ | ||
| #pragma once | ||
|
|
||
| #include "nix/util/util.hh" | ||
| #include "nix/util/json-impls.hh" | ||
| #include "nix/store/path.hh" | ||
|
|
||
| #include <chrono> | ||
| #include <sys/types.h> | ||
|
|
||
| namespace nix { | ||
|
|
||
| /** | ||
| * A uid and optional corresponding user name. | ||
| */ | ||
| struct UserInfo | ||
| { | ||
| uid_t uid = -1; | ||
| std::optional<std::string> name; | ||
|
|
||
| /** | ||
| * Create a UserInfo from a UID, looking up the username if possible. | ||
| */ | ||
| static UserInfo fromUid(uid_t uid); | ||
| }; | ||
|
|
||
| struct ActiveBuild | ||
| { | ||
| pid_t nixPid; | ||
|
|
||
| std::optional<pid_t> clientPid; | ||
| std::optional<uid_t> clientUid; | ||
|
|
||
| pid_t mainPid; | ||
| UserInfo mainUser; | ||
| std::optional<Path> cgroup; | ||
|
|
||
| time_t startTime; | ||
|
|
||
| StorePath derivation; | ||
| }; | ||
|
|
||
| struct ActiveBuildInfo : ActiveBuild | ||
| { | ||
| struct ProcessInfo | ||
| { | ||
| pid_t pid = 0; | ||
| pid_t parentPid = 0; | ||
| UserInfo user; | ||
| std::vector<std::string> argv; | ||
| std::optional<std::chrono::microseconds> utime, stime, cutime, cstime; | ||
| }; | ||
|
|
||
| // User/system CPU time for the entire cgroup, if available. | ||
| std::optional<std::chrono::microseconds> utime, stime; | ||
|
|
||
| std::vector<ProcessInfo> processes; | ||
| }; | ||
|
|
||
| struct TrackActiveBuildsStore | ||
| { | ||
| struct BuildHandle | ||
| { | ||
| TrackActiveBuildsStore & tracker; | ||
| uint64_t id; | ||
|
|
||
| BuildHandle(TrackActiveBuildsStore & tracker, uint64_t id) | ||
| : tracker(tracker) | ||
| , id(id) | ||
| { | ||
| } | ||
|
|
||
| BuildHandle(BuildHandle && other) noexcept | ||
| : tracker(other.tracker) | ||
| , id(other.id) | ||
| { | ||
| other.id = 0; | ||
| } | ||
|
|
||
| ~BuildHandle() | ||
| { | ||
| if (id) { | ||
| try { | ||
| tracker.buildFinished(*this); | ||
| } catch (...) { | ||
| ignoreExceptionInDestructor(); | ||
| } | ||
| } | ||
| } | ||
| }; | ||
|
|
||
| virtual BuildHandle buildStarted(const ActiveBuild & build) = 0; | ||
|
|
||
| virtual void buildFinished(const BuildHandle & handle) = 0; | ||
| }; | ||
|
|
||
| struct QueryActiveBuildsStore | ||
| { | ||
| inline static std::string operationName = "Querying active builds"; | ||
|
|
||
| virtual std::vector<ActiveBuildInfo> queryActiveBuilds() = 0; | ||
| }; | ||
|
|
||
| } // namespace nix | ||
|
|
||
| JSON_IMPL(UserInfo) | ||
| JSON_IMPL(ActiveBuild) | ||
| JSON_IMPL(ActiveBuildInfo) | ||
| JSON_IMPL(ActiveBuildInfo::ProcessInfo) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.