├── .github ├── FUNDING.yml ├── dependabot.yml └── workflows │ ├── dependabot_merge.yml │ ├── periodic.yml │ ├── regression.yml │ ├── release.yml │ └── winget.yml ├── .gitignore ├── .rpm └── procs.spec ├── CHANGELOG.md ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── Makefile ├── README.md ├── build.rs ├── config ├── large.toml └── small.toml ├── img ├── procs.png ├── procs_docker.png ├── procs_port.png ├── procs_sort.png ├── procs_tree.png └── procs_zsh.png ├── man └── procs.1.adoc ├── snapcraft.yaml └── src ├── column.rs ├── columns.rs ├── columns ├── arch.rs ├── ccgroup.rs ├── cgroup.rs ├── command.rs ├── context_sw.rs ├── cpu_time.rs ├── docker.rs ├── eip.rs ├── elapsed_time.rs ├── empty.rs ├── env.rs ├── esp.rs ├── file_name.rs ├── gid.rs ├── gid_fs.rs ├── gid_real.rs ├── gid_saved.rs ├── group.rs ├── group_fs.rs ├── group_real.rs ├── group_saved.rs ├── maj_flt.rs ├── min_flt.rs ├── multi_slot.rs ├── nice.rs ├── os_freebsd.rs ├── os_linux.rs ├── os_macos.rs ├── os_windows.rs ├── pgid.rs ├── pid.rs ├── policy.rs ├── ppid.rs ├── priority.rs ├── processor.rs ├── read_bytes.rs ├── rt_priority.rs ├── sec_context.rs ├── separator.rs ├── session.rs ├── shd_pnd.rs ├── sig_blk.rs ├── sig_cgt.rs ├── sig_ign.rs ├── sig_pnd.rs ├── slot.rs ├── ssb.rs ├── start_time.rs ├── state.rs ├── tcp_port.rs ├── threads.rs ├── tree.rs ├── tree_slot.rs ├── tty.rs ├── udp_port.rs ├── uid.rs ├── uid_fs.rs ├── uid_login.rs ├── uid_real.rs ├── uid_saved.rs ├── usage_cpu.rs ├── usage_mem.rs ├── user.rs ├── user_fs.rs ├── user_login.rs ├── user_real.rs ├── user_saved.rs ├── vm_data.rs ├── vm_exe.rs ├── vm_hwm.rs ├── vm_lib.rs ├── vm_lock.rs ├── vm_peak.rs ├── vm_pin.rs ├── vm_pte.rs ├── vm_rss.rs ├── vm_size.rs ├── vm_stack.rs ├── vm_swap.rs ├── wchan.rs ├── work_dir.rs └── write_bytes.rs ├── config.rs ├── main.rs ├── opt.rs ├── process.rs ├── process ├── freebsd.rs ├── linux.rs ├── macos.rs └── windows.rs ├── style.rs ├── term_info.rs ├── util.rs ├── view.rs └── watcher.rs /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: dalance 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: cargo 4 | directory: "/" 5 | schedule: 6 | interval: daily 7 | time: "20:00" 8 | open-pull-requests-limit: 10 9 | -------------------------------------------------------------------------------- /.github/workflows/dependabot_merge.yml: -------------------------------------------------------------------------------- 1 | name: Dependabot auto-merge 2 | on: pull_request_target 3 | 4 | permissions: 5 | pull-requests: write 6 | contents: write 7 | 8 | jobs: 9 | dependabot: 10 | runs-on: ubuntu-latest 11 | if: ${{ github.actor == 'dependabot[bot]' }} 12 | steps: 13 | - name: Dependabot metadata 14 | id: metadata 15 | uses: dependabot/fetch-metadata@v2.2.0 16 | with: 17 | github-token: '${{ secrets.GITHUB_TOKEN }}' 18 | - name: Enable auto-merge for Dependabot PRs 19 | if: ${{ steps.metadata.outputs.update-type == 'version-update:semver-patch' || ( !startsWith( steps.metadata.outputs.new-version, '0.' ) && steps.metadata.outputs.update-type == 'version-update:semver-minor' ) }} 20 | run: gh pr merge --auto --merge "$PR_URL" 21 | env: 22 | PR_URL: ${{github.event.pull_request.html_url}} 23 | GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} 24 | -------------------------------------------------------------------------------- /.github/workflows/periodic.yml: -------------------------------------------------------------------------------- 1 | name: Periodic 2 | 3 | on: 4 | schedule: 5 | - cron: 0 0 * * SUN 6 | 7 | jobs: 8 | build: 9 | 10 | strategy: 11 | matrix: 12 | os: [ubuntu-latest] 13 | rust: [stable, beta, nightly] 14 | 15 | runs-on: ${{ matrix.os }} 16 | 17 | steps: 18 | - name: Setup Rust 19 | uses: dtolnay/rust-toolchain@v1 20 | with: 21 | toolchain: ${{ matrix.rust }} 22 | - name: Checkout 23 | uses: actions/checkout@v3 24 | - name: Run tests 25 | run: | 26 | cargo update 27 | cargo test 28 | -------------------------------------------------------------------------------- /.github/workflows/regression.yml: -------------------------------------------------------------------------------- 1 | name: Regression 2 | 3 | on: 4 | push: 5 | branches: 6 | - master 7 | pull_request: 8 | 9 | jobs: 10 | build: 11 | strategy: 12 | matrix: 13 | include: 14 | - os: ubuntu-latest 15 | rust: stable 16 | target: x86_64-unknown-linux-gnu 17 | - os: ubuntu-24.04-arm 18 | rust: stable 19 | target: aarch64-unknown-linux-gnu 20 | - os: macOS-latest 21 | rust: stable 22 | target: x86_64-apple-darwin 23 | - os: windows-latest 24 | rust: stable 25 | target: x86_64-pc-windows-msvc 26 | 27 | runs-on: ${{ matrix.os }} 28 | steps: 29 | - uses: actions/checkout@v3 30 | - uses: dtolnay/rust-toolchain@v1 31 | with: 32 | toolchain: ${{ matrix.rust }} 33 | targets: ${{ matrix.target }} 34 | - name: Run tests 35 | run: cargo test --locked --target ${{ matrix.target }} 36 | - name: Run tests feature variation 37 | run: cargo test --locked --target ${{ matrix.target }} --no-default-features 38 | 39 | build-freebsd: 40 | runs-on: ubuntu-latest 41 | steps: 42 | - uses: actions/checkout@v4 43 | - uses: vmactions/freebsd-vm@v1 44 | with: 45 | usesh: true 46 | prepare: | 47 | pkg install -y rust 48 | pkg install -y llvm15 49 | run: | 50 | cargo test --locked 51 | 52 | msrv: 53 | runs-on: ubuntu-latest 54 | steps: 55 | - uses: actions/checkout@v3 56 | - uses: dtolnay/rust-toolchain@v1 57 | with: 58 | toolchain: 1.74.0 59 | targets: x86_64-unknown-linux-gnu 60 | - name: Run build 61 | run: cargo build 62 | 63 | rustfmt: 64 | runs-on: ubuntu-latest 65 | steps: 66 | - uses: actions/checkout@v3 67 | - uses: dtolnay/rust-toolchain@v1 68 | with: 69 | toolchain: stable 70 | components: rustfmt 71 | - name: Run rustfmt 72 | run: cargo fmt -- --check 73 | 74 | clippy: 75 | runs-on: ubuntu-latest 76 | steps: 77 | - uses: actions/checkout@v3 78 | - uses: dtolnay/rust-toolchain@v1 79 | with: 80 | toolchain: stable 81 | components: clippy 82 | - name: Run clippy 83 | run: cargo clippy -- -D warnings 84 | - name: Run clippy feature variation 85 | run: cargo clippy --no-default-features -- -D warnings 86 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | name: Release 2 | 3 | on: 4 | push: 5 | tags: 6 | - 'v*.*.*' 7 | 8 | jobs: 9 | build: 10 | 11 | strategy: 12 | matrix: 13 | os: [ubuntu-latest, ubuntu-24.04-arm, macOS-latest, windows-latest] 14 | rust: [stable] 15 | 16 | runs-on: ${{ matrix.os }} 17 | 18 | steps: 19 | - name: Setup Rust 20 | uses: dtolnay/rust-toolchain@v1 21 | with: 22 | toolchain: ${{ matrix.rust }} 23 | - name: Checkout 24 | uses: actions/checkout@v3 25 | - name: Setup MUSL 26 | if: matrix.os == 'ubuntu-latest' 27 | run: | 28 | rustup target add x86_64-unknown-linux-musl 29 | sudo apt-get -qq install musl-tools 30 | - name: Setup MUSL aarch64 31 | if: matrix.os == 'ubuntu-24.04-arm' 32 | run: | 33 | rustup target add aarch64-unknown-linux-musl 34 | sudo apt-get -qq install musl-tools 35 | - name: Setup aarch64 mac 36 | if: matrix.os == 'macOS-latest' 37 | run: | 38 | rustup target add aarch64-apple-darwin 39 | rustup target add x86_64-apple-darwin 40 | - name: Build for linux 41 | if: matrix.os == 'ubuntu-latest' 42 | run: | 43 | make release_lnx 44 | cargo install --locked cargo-rpm 45 | make release_rpm 46 | - name: Build for linux aarch64 47 | if: matrix.os == 'ubuntu-24.04-arm' 48 | run: make release_lnx_aarch64 49 | - name: Build for macOS 50 | if: matrix.os == 'macOS-latest' 51 | run: make release_mac 52 | - name: Build for Windows 53 | if: matrix.os == 'windows-latest' 54 | run: make release_win 55 | - name: Release 56 | uses: softprops/action-gh-release@v1 57 | with: 58 | body: '[Changelog](https://github.com/dalance/procs/blob/master/CHANGELOG.md)' 59 | files: "*.zip\n*.rpm" 60 | env: 61 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 62 | -------------------------------------------------------------------------------- /.github/workflows/winget.yml: -------------------------------------------------------------------------------- 1 | name: Publish to WinGet 2 | on: 3 | release: 4 | types: [released] 5 | workflow_dispatch: 6 | jobs: 7 | publish: 8 | runs-on: windows-latest 9 | steps: 10 | - uses: vedantmgoyal2009/winget-releaser@v2 11 | with: 12 | identifier: dalance.procs 13 | max-versions-to-keep: 5 # keep only latest 5 versions 14 | installers-regex: 'x86_64-windows\.zip$' 15 | token: ${{ secrets.WINGET_TOKEN }} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | **/*.rs.bk 3 | -------------------------------------------------------------------------------- /.rpm/procs.spec: -------------------------------------------------------------------------------- 1 | %define __spec_install_post %{nil} 2 | %define __os_install_post %{_dbpath}/brp-compress 3 | %define debug_package %{nil} 4 | 5 | Name: procs 6 | Summary: A modern replacement for ps 7 | Version: @@VERSION@@ 8 | Release: @@RELEASE@@ 9 | License: MIT 10 | Group: Applications/System 11 | Source0: %{name}-%{version}.tar.gz 12 | 13 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root 14 | 15 | %description 16 | %{summary} 17 | 18 | %prep 19 | %setup -q 20 | 21 | %install 22 | rm -rf %{buildroot} 23 | mkdir -p %{buildroot} 24 | cp -a * %{buildroot} 25 | 26 | %clean 27 | rm -rf %{buildroot} 28 | 29 | %files 30 | %defattr(-,root,root,-) 31 | %{_bindir}/* 32 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "procs" 3 | version = "0.14.10" 4 | authors = ["dalance@gmail.com"] 5 | repository = "https://github.com/dalance/procs" 6 | keywords = ["process"] 7 | categories = ["command-line-utilities"] 8 | license = "MIT" 9 | readme = "README.md" 10 | description = "A modern replacement for ps" 11 | edition = "2021" 12 | exclude = ["img/*", "config/*"] 13 | rust-version = "1.74" 14 | 15 | [package.metadata.release] 16 | pre-release-commit-message = "Prepare to v{{version}}" 17 | tag-message = "Bump version to {{version}}" 18 | tag-prefix = "" 19 | pre-release-replacements = [ 20 | {file="CHANGELOG.md", search="Unreleased", replace="v{{version}}"}, 21 | {file="CHANGELOG.md", search="ReleaseDate", replace="{{date}}"}, 22 | {file="CHANGELOG.md", search="Change Log", replace="Change Log\n\n## [Unreleased](https://github.com/dalance/procs/compare/v{{version}}...Unreleased) - ReleaseDate"}, 23 | {file="README.md", search = "changelog-v[0-9\\.]+", replace = "changelog-v{{version}}" }, 24 | {file="README.md", search = "v[0-9\\.]+/procs-[0-9\\.]+", replace = "v{{version}}/procs-{{version}}" }, 25 | {file="snapcraft.yaml", search="version v[0-9\\.]+", replace="version v{{version}}"}, 26 | ] 27 | 28 | [features] 29 | default = ["docker"] 30 | docker = ["dockworker", "tokio"] 31 | 32 | [badges] 33 | travis-ci = { repository = "dalance/procs" } 34 | codecov = { repository = "dalance/procs", branch = "master", service = "github" } 35 | 36 | [dependencies] 37 | anyhow = "1.0" 38 | byte-unit = "5.1" 39 | clap = {version = "4.4", features = ["derive"]} 40 | clap_complete = "4.4" 41 | console = "0.15.11" 42 | chrono = {version = "0.4.41", default-features = false, features = ["clock"]} 43 | directories = "6.0.0" 44 | dockworker = { version = "0.7.0", optional = true } 45 | getch = "0.3.1" 46 | libc = "0.2" 47 | minus = { version = "5.6", features = ["static_output", "search"] } 48 | once_cell = "1.21.3" 49 | serde = "1.0" 50 | serde_derive = "1.0" 51 | termbg = "0.6.2" 52 | tokio = { version = "1.45", optional = true, features = ["rt"] } 53 | toml = "0.8" 54 | unicode-width = "0.2" 55 | 56 | [build-dependencies] 57 | anyhow = "1.0" 58 | clap = {version = "4.4", features = ["derive"]} 59 | clap_complete = "4.4" 60 | 61 | [target.'cfg(any(target_os = "linux", target_os = "android"))'.dependencies] 62 | pager = "0.16.1" 63 | procfs = "0.17.0" 64 | regex = "1.11" 65 | uzers = "0.12" 66 | which = "7" 67 | 68 | [target.'cfg(target_os = "macos")'.dependencies] 69 | libproc = "0.14" 70 | nix = {version = "0.30.1", features = ["process"]} 71 | errno = "0.3" 72 | pager = "0.16" 73 | uzers = "0.12" 74 | which = "7" 75 | mach2 = "0.4.2" 76 | 77 | [target.'cfg(target_os = "windows")'.dependencies] 78 | windows-sys = { version = "0.59", features = ["Win32_Foundation", "Win32_Networking_WinSock", "Win32_NetworkManagement_IpHelper", "Win32_Security", "Win32_System_Diagnostics_ToolHelp", "Win32_System_ProcessStatus", "Win32_System_Threading"] } 79 | 80 | [target.'cfg(target_os = "freebsd")'.dependencies] 81 | bsd-kvm = "0.1.5" 82 | bsd-kvm-sys = "0.2.0" 83 | pager = "0.16.1" 84 | uzers = "0.12" 85 | which = "7" 86 | 87 | [package.metadata.rpm.cargo] 88 | buildflags = ["--release"] 89 | target = "x86_64-unknown-linux-musl" 90 | 91 | [package.metadata.rpm.targets] 92 | procs = { path = "/usr/bin/procs" } 93 | 94 | [profile.release] 95 | lto = true 96 | codegen-units = 1 97 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | VERSION = $(patsubst "%",%, $(word 3, $(shell grep version Cargo.toml))) 2 | BUILD_TIME = $(shell date +"%Y/%m/%d %H:%M:%S") 3 | GIT_REVISION = $(shell git log -1 --format="%h") 4 | RUST_VERSION = $(word 2, $(shell rustc -V)) 5 | LONG_VERSION = "$(VERSION) ( rev: $(GIT_REVISION), rustc: $(RUST_VERSION), build at: $(BUILD_TIME) )" 6 | BIN_NAME = procs 7 | 8 | export LONG_VERSION 9 | 10 | .PHONY: all test clean release_lnx release_win release_mac 11 | 12 | all: test 13 | 14 | test: 15 | cargo test --locked 16 | 17 | watch: 18 | cargo watch test --locked 19 | 20 | clean: 21 | cargo clean 22 | 23 | release_lnx: 24 | cargo build --locked --release --target=x86_64-unknown-linux-musl 25 | zip -j ${BIN_NAME}-v${VERSION}-x86_64-linux.zip target/x86_64-unknown-linux-musl/release/${BIN_NAME} 26 | 27 | release_lnx_aarch64: 28 | cargo build --locked --release --target=aarch64-unknown-linux-musl 29 | zip -j ${BIN_NAME}-v${VERSION}-aarch64-linux.zip target/aarch64-unknown-linux-musl/release/${BIN_NAME} 30 | 31 | release_win: 32 | cargo build --locked --release --target=x86_64-pc-windows-msvc 33 | mv -v target/x86_64-pc-windows-msvc/release/${BIN_NAME}.exe ./ 34 | 7z a ${BIN_NAME}-v${VERSION}-x86_64-windows.zip ${BIN_NAME}.exe 35 | 36 | release_mac: 37 | cargo build --locked --release --target=x86_64-apple-darwin 38 | cargo build --locked --release --target=aarch64-apple-darwin 39 | zip -j ${BIN_NAME}-v${VERSION}-x86_64-mac.zip target/x86_64-apple-darwin/release/${BIN_NAME} 40 | zip -j ${BIN_NAME}-v${VERSION}-aarch64-mac.zip target/aarch64-apple-darwin/release/${BIN_NAME} 41 | 42 | release_rpm: 43 | mkdir -p target 44 | cargo rpm build 45 | cp target/x86_64-unknown-linux-musl/release/rpmbuild/RPMS/x86_64/* ./ 46 | -------------------------------------------------------------------------------- /build.rs: -------------------------------------------------------------------------------- 1 | include!("src/opt.rs"); 2 | 3 | fn main() { 4 | if let Ok(path) = std::env::var("COMPLETION_PATH") { 5 | let out_dir = PathBuf::from(path); 6 | 7 | gen_completion(Shell::Bash, &out_dir).unwrap(); 8 | gen_completion(Shell::Elvish, &out_dir).unwrap(); 9 | gen_completion(Shell::Fish, &out_dir).unwrap(); 10 | gen_completion(Shell::PowerShell, &out_dir).unwrap(); 11 | gen_completion(Shell::Zsh, &out_dir).unwrap(); 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /config/large.toml: -------------------------------------------------------------------------------- 1 | [[columns]] 2 | kind = "Pid" 3 | style = "BrightYellow|Yellow" 4 | numeric_search = true 5 | nonnumeric_search = false 6 | align = "Left" 7 | 8 | [[columns]] 9 | kind = "User" 10 | style = "BrightGreen|Green" 11 | numeric_search = false 12 | nonnumeric_search = true 13 | align = "Left" 14 | 15 | [[columns]] 16 | kind = "Separator" 17 | style = "White|BrightBlack" 18 | numeric_search = false 19 | nonnumeric_search = false 20 | align = "Left" 21 | 22 | [[columns]] 23 | kind = "State" 24 | style = "ByState" 25 | numeric_search = false 26 | nonnumeric_search = false 27 | align = "Left" 28 | 29 | [[columns]] 30 | kind = "Nice" 31 | style = "BrightMagenta|Magenta" 32 | numeric_search = false 33 | nonnumeric_search = false 34 | align = "Right" 35 | 36 | [[columns]] 37 | kind = "Tty" 38 | style = "BrightWhite|Black" 39 | numeric_search = false 40 | nonnumeric_search = false 41 | align = "Left" 42 | 43 | [[columns]] 44 | kind = "UsageCpu" 45 | style = "ByPercentage" 46 | numeric_search = false 47 | nonnumeric_search = false 48 | align = "Right" 49 | 50 | [[columns]] 51 | kind = "UsageMem" 52 | style = "ByPercentage" 53 | numeric_search = false 54 | nonnumeric_search = false 55 | align = "Right" 56 | 57 | [[columns]] 58 | kind = "VmSize" 59 | style = "ByUnit" 60 | numeric_search = false 61 | nonnumeric_search = false 62 | align = "Right" 63 | 64 | [[columns]] 65 | kind = "VmRss" 66 | style = "ByUnit" 67 | numeric_search = false 68 | nonnumeric_search = false 69 | align = "Right" 70 | 71 | [[columns]] 72 | kind = "TcpPort" 73 | style = "BrightCyan|Cyan" 74 | numeric_search = true 75 | nonnumeric_search = false 76 | align = "Left" 77 | max_width = 20 78 | 79 | [[columns]] 80 | kind = "UdpPort" 81 | style = "BrightCyan|Cyan" 82 | numeric_search = true 83 | nonnumeric_search = false 84 | align = "Left" 85 | max_width = 20 86 | 87 | [[columns]] 88 | kind = "ReadBytes" 89 | style = "ByUnit" 90 | numeric_search = false 91 | nonnumeric_search = false 92 | align = "Right" 93 | 94 | [[columns]] 95 | kind = "WriteBytes" 96 | style = "ByUnit" 97 | numeric_search = false 98 | nonnumeric_search = false 99 | align = "Right" 100 | 101 | [[columns]] 102 | kind = "Slot" 103 | style = "ByUnit" 104 | numeric_search = false 105 | nonnumeric_search = false 106 | align = "Right" 107 | 108 | [[columns]] 109 | kind = "Separator" 110 | style = "White|BrightBlack" 111 | numeric_search = false 112 | nonnumeric_search = false 113 | align = "Left" 114 | 115 | [[columns]] 116 | kind = "CpuTime" 117 | style = "BrightCyan|Cyan" 118 | numeric_search = false 119 | nonnumeric_search = false 120 | align = "Left" 121 | 122 | [[columns]] 123 | kind = "StartTime" 124 | style = "BrightMagenta|Magenta" 125 | numeric_search = false 126 | nonnumeric_search = false 127 | align = "Left" 128 | 129 | [[columns]] 130 | kind = "Docker" 131 | style = "BrightGreen|Green" 132 | numeric_search = false 133 | nonnumeric_search = true 134 | align = "Left" 135 | 136 | [[columns]] 137 | kind = "Separator" 138 | style = "White|BrightBlack" 139 | numeric_search = false 140 | nonnumeric_search = false 141 | align = "Left" 142 | 143 | [[columns]] 144 | kind = "Command" 145 | style = "BrightWhite|Black" 146 | numeric_search = false 147 | nonnumeric_search = true 148 | align = "Left" 149 | 150 | [style] 151 | header = "BrightWhite|Black" 152 | unit = "BrightWhite|Black" 153 | tree = "BrightWhite|Black" 154 | 155 | [style.by_percentage] 156 | color_000 = "BrightBlue|Blue" 157 | color_025 = "BrightGreen|Green" 158 | color_050 = "BrightYellow|Yellow" 159 | color_075 = "BrightRed|Red" 160 | color_100 = "BrightRed|Red" 161 | 162 | [style.by_state] 163 | color_d = "BrightRed|Red" 164 | color_r = "BrightGreen|Green" 165 | color_s = "BrightBlue|Blue" 166 | color_t = "BrightCyan|Cyan" 167 | color_z = "BrightMagenta|Magenta" 168 | color_x = "BrightMagenta|Magenta" 169 | color_k = "BrightYellow|Yellow" 170 | color_w = "BrightYellow|Yellow" 171 | color_p = "BrightYellow|Yellow" 172 | 173 | [style.by_unit] 174 | color_k = "BrightBlue|Blue" 175 | color_m = "BrightGreen|Green" 176 | color_g = "BrightYellow|Yellow" 177 | color_t = "BrightRed|Red" 178 | color_p = "BrightRed|Red" 179 | color_x = "BrightBlue|Blue" 180 | 181 | [search] 182 | numeric_search = "Exact" 183 | nonnumeric_search = "Partial" 184 | logic = "And" 185 | 186 | [display] 187 | show_self = false 188 | cut_to_terminal = true 189 | cut_to_pager = false 190 | cut_to_pipe = false 191 | color_mode = "Auto" 192 | separator = "│" 193 | ascending = "▲" 194 | descending = "▼" 195 | tree_symbols = ["│", "─", "┬", "├", "└"] 196 | abbr_sid = true 197 | 198 | [sort] 199 | column = 0 200 | order = "Ascending" 201 | 202 | [docker] 203 | path = "unix:///var/run/docker.sock" 204 | 205 | [pager] 206 | mode = "Auto" 207 | 208 | -------------------------------------------------------------------------------- /config/small.toml: -------------------------------------------------------------------------------- 1 | [[columns]] 2 | kind = "Pid" 3 | style = "BrightYellow|Yellow" 4 | numeric_search = true 5 | nonnumeric_search = false 6 | align = "Left" 7 | 8 | [[columns]] 9 | kind = "User" 10 | style = "BrightGreen|Green" 11 | numeric_search = false 12 | nonnumeric_search = true 13 | align = "Left" 14 | 15 | [[columns]] 16 | kind = "Separator" 17 | style = "White|BrightBlack" 18 | numeric_search = false 19 | nonnumeric_search = false 20 | align = "Left" 21 | 22 | [[columns]] 23 | kind = "Tty" 24 | style = "BrightWhite|Black" 25 | numeric_search = false 26 | nonnumeric_search = false 27 | align = "Left" 28 | 29 | [[columns]] 30 | kind = "UsageCpu" 31 | style = "ByPercentage" 32 | numeric_search = false 33 | nonnumeric_search = false 34 | align = "Right" 35 | 36 | [[columns]] 37 | kind = "UsageMem" 38 | style = "ByPercentage" 39 | numeric_search = false 40 | nonnumeric_search = false 41 | align = "Right" 42 | 43 | [[columns]] 44 | kind = "CpuTime" 45 | style = "BrightCyan|Cyan" 46 | numeric_search = false 47 | nonnumeric_search = false 48 | align = "Left" 49 | 50 | [[columns]] 51 | kind = "Separator" 52 | style = "White|BrightBlack" 53 | numeric_search = false 54 | nonnumeric_search = false 55 | align = "Left" 56 | 57 | [[columns]] 58 | kind = "Command" 59 | style = "BrightWhite|Black" 60 | numeric_search = false 61 | nonnumeric_search = true 62 | align = "Left" 63 | 64 | [style] 65 | header = "BrightWhite|Black" 66 | unit = "BrightWhite|Black" 67 | tree = "BrightWhite|Black" 68 | 69 | [style.by_percentage] 70 | color_000 = "BrightBlue|Blue" 71 | color_025 = "BrightGreen|Green" 72 | color_050 = "BrightYellow|Yellow" 73 | color_075 = "BrightRed|Red" 74 | color_100 = "BrightRed|Red" 75 | 76 | [style.by_state] 77 | color_d = "BrightRed|Red" 78 | color_r = "BrightGreen|Green" 79 | color_s = "BrightBlue|Blue" 80 | color_t = "BrightCyan|Cyan" 81 | color_z = "BrightMagenta|Magenta" 82 | color_x = "BrightMagenta|Magenta" 83 | color_k = "BrightYellow|Yellow" 84 | color_w = "BrightYellow|Yellow" 85 | color_p = "BrightYellow|Yellow" 86 | 87 | [style.by_unit] 88 | color_k = "BrightBlue|Blue" 89 | color_m = "BrightGreen|Green" 90 | color_g = "BrightYellow|Yellow" 91 | color_t = "BrightRed|Red" 92 | color_p = "BrightRed|Red" 93 | color_x = "BrightBlue|Blue" 94 | 95 | [search] 96 | numeric_search = "Exact" 97 | nonnumeric_search = "Partial" 98 | logic = "And" 99 | 100 | [display] 101 | show_self = false 102 | cut_to_terminal = true 103 | cut_to_pager = false 104 | cut_to_pipe = false 105 | color_mode = "Auto" 106 | separator = "│" 107 | ascending = "▲" 108 | descending = "▼" 109 | tree_symbols = ["│", "─", "┬", "├", "└"] 110 | abbr_sid = true 111 | 112 | [sort] 113 | column = 0 114 | order = "Ascending" 115 | 116 | [docker] 117 | path = "unix:///var/run/docker.sock" 118 | 119 | [pager] 120 | mode = "Auto" 121 | 122 | -------------------------------------------------------------------------------- /img/procs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalance/procs/ce2ac953e5a2cef91ec1cc8acdbf65a487943d4e/img/procs.png -------------------------------------------------------------------------------- /img/procs_docker.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalance/procs/ce2ac953e5a2cef91ec1cc8acdbf65a487943d4e/img/procs_docker.png -------------------------------------------------------------------------------- /img/procs_port.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalance/procs/ce2ac953e5a2cef91ec1cc8acdbf65a487943d4e/img/procs_port.png -------------------------------------------------------------------------------- /img/procs_sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalance/procs/ce2ac953e5a2cef91ec1cc8acdbf65a487943d4e/img/procs_sort.png -------------------------------------------------------------------------------- /img/procs_tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalance/procs/ce2ac953e5a2cef91ec1cc8acdbf65a487943d4e/img/procs_tree.png -------------------------------------------------------------------------------- /img/procs_zsh.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dalance/procs/ce2ac953e5a2cef91ec1cc8acdbf65a487943d4e/img/procs_zsh.png -------------------------------------------------------------------------------- /man/procs.1.adoc: -------------------------------------------------------------------------------- 1 | = procs(1) 2 | :doctype: manpage 3 | :manmanual: User Commands 4 | :mansource: procs 5 | :man-linkstyle: pass:[blue R < >] 6 | 7 | == NAME 8 | 9 | procs - a replacement for `ps` written in Rust 10 | 11 | == SYNOPSIS 12 | 13 | *procs* [OPTIONS] [KEYWORDS] 14 | 15 | == DESCRIPTION 16 | 17 | *procs* is a command-line tool that provides an alternative to the `ps` command. 18 | 19 | == OPTIONS 20 | 21 | *--and*:: Show processes that match all keywords. 22 | *--or*:: Show processes that match any keyword. 23 | *--nand*:: Hide processes that match all keywords. 24 | *--nor*:: Hide processes that match any keyword. 25 | *--watch*:: Enable watch mode for real-time updates. 26 | *--watch-interval *:: Set the update interval for watch mode. 27 | *--tree*:: Display processes in a tree view. 28 | *--sorta *:: Sort processes in ascending order by the specified column. 29 | *--sortd *:: Sort processes in descending order by the specified column. 30 | *--insert *:: Insert a new column at the position of `Slot` or `MultiSlot`. 31 | *--gen-completion*:: Generate shell completion files for supported shells. 32 | 33 | == EXAMPLES 34 | 35 | *Show all processes*:: procs 36 | 37 | *Search by non-numeric keyword*:: procs zsh 38 | 39 | *Search by numeric keyword*:: procs --or 6000 60000 60001 16723 40 | 41 | *Show Docker container name*:: procs growi 42 | 43 | == PAGER 44 | 45 | On Linux and macOS, `less` is the default pager. If not available, `more` is used. Built-in pager can be used by configuring `use_builtin`. On Windows, the built-in pager is always used. 46 | 47 | == WATCH MODE SHORTCUTS 48 | 49 | If `--watch` or `--watch-interval ` option is used, procs automatically updates output like `top`. 50 | Keyboard shortcuts are available for control. 51 | 52 | *n*:: Change the sort column to the next column 53 | *p*:: Change the sort column to the previous column 54 | *a*:: Change the sort order to ascending 55 | *d*:: Change the sort order to descending 56 | *q*:: Quit 57 | 58 | == RESOURCES 59 | *Project source code:* https://github.com/dalance/procs 60 | -------------------------------------------------------------------------------- /snapcraft.yaml: -------------------------------------------------------------------------------- 1 | name: procs 2 | version: &version v0.14.10 3 | summary: A modern replacement for ps written in Rust 4 | description: | 5 | procs is a tool to display process information. 6 | base: core18 7 | license: MIT 8 | 9 | confinement: strict 10 | 11 | architectures: 12 | - build-on: amd64 13 | - build-on: i386 14 | - build-on: ppc64el 15 | - build-on: arm64 16 | - build-on: armhf 17 | 18 | apps: 19 | procs: 20 | command: procs 21 | 22 | parts: 23 | procs: 24 | source: https://github.com/dalance/procs.git 25 | source-tag: *version 26 | plugin: rust 27 | stage-packages: 28 | - libc6 29 | - libgcc1 30 | - libstdc++6 31 | - zlib1g 32 | -------------------------------------------------------------------------------- /src/columns.rs: -------------------------------------------------------------------------------- 1 | #[cfg(any(target_os = "linux", target_os = "android"))] 2 | include!("./columns/os_linux.rs"); 3 | #[cfg(target_os = "macos")] 4 | include!("./columns/os_macos.rs"); 5 | #[cfg(target_os = "windows")] 6 | include!("./columns/os_windows.rs"); 7 | #[cfg(target_os = "freebsd")] 8 | include!("./columns/os_freebsd.rs"); 9 | -------------------------------------------------------------------------------- /src/columns/arch.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | const CTL_MAXNAME: i32 = 12; 7 | const P_TRANSLATED: i32 = 131072; 8 | const CPU_TYPE_X86_64: i32 = 16777223; 9 | const CPU_TYPE_ARM64: i32 = 16777228; 10 | 11 | pub struct Arch { 12 | header: String, 13 | unit: String, 14 | fmt_contents: HashMap, 15 | raw_contents: HashMap, 16 | width: usize, 17 | } 18 | 19 | #[cfg(target_os = "macos")] 20 | impl Arch { 21 | pub fn new(header: Option) -> Self { 22 | let header = header.unwrap_or_else(|| String::from("Arch")); 23 | let unit = String::new(); 24 | Self { 25 | fmt_contents: HashMap::new(), 26 | raw_contents: HashMap::new(), 27 | width: 0, 28 | header, 29 | unit 30 | } 31 | } 32 | } 33 | 34 | #[cfg(target_os = "macos")] 35 | impl Column for Arch { 36 | fn add(&mut self, proc: &ProcessInfo) { 37 | let pid = proc.pid; 38 | let arch = arch_from_pid(pid); 39 | 40 | let fmt_content = format!("{}", arch); 41 | let raw_content = fmt_content.clone(); 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(String, false); 48 | } 49 | 50 | #[cfg(target_os = "macos")] 51 | pub fn arch_from_pid(pid: i32) -> &'static str { 52 | use { 53 | libc::{sysctl, sysctlnametomib, cpu_type_t, size_t, CTL_KERN, KERN_PROC, KERN_PROC_PID}, 54 | std::{mem, ffi::CString}, 55 | crate::process::kinfo_proc, 56 | }; 57 | 58 | let mut mib = [0; CTL_MAXNAME as usize]; 59 | let mut length = CTL_MAXNAME as size_t; 60 | let mut cpu_type: cpu_type_t = -1; 61 | let mut size = mem::size_of::(); 62 | 63 | let sysctl_name = CString::new("sysctl.proc_cputype").unwrap(); 64 | if unsafe { sysctlnametomib(sysctl_name.as_ptr(), mib.as_mut_ptr(), &mut length) } != 0 { 65 | return "unknown"; 66 | } 67 | 68 | mib[length as usize] = pid; 69 | length += 1; 70 | 71 | if unsafe { sysctl(mib.as_mut_ptr(), length as u32, &mut cpu_type as *mut _ as *mut _, &mut size, core::ptr::null_mut(), 0) } != 0 { 72 | return "unknown"; 73 | } 74 | 75 | if cpu_type == CPU_TYPE_X86_64 { 76 | return "x86_64"; 77 | } 78 | 79 | if cpu_type == CPU_TYPE_ARM64 { 80 | let mut proc_info: kinfo_proc = unsafe { mem::zeroed() }; 81 | mib[0] = CTL_KERN; 82 | mib[1] = KERN_PROC; 83 | mib[2] = KERN_PROC_PID; 84 | mib[3] = pid; 85 | 86 | length = 4; 87 | size = mem::size_of::(); 88 | 89 | if unsafe { sysctl(mib.as_mut_ptr(), length as u32, &mut proc_info as *mut _ as *mut _, &mut size, core::ptr::null_mut(), 0) } != 0 { 90 | return "arm64"; 91 | } 92 | 93 | if (proc_info.kp_proc.p_flag & P_TRANSLATED) != 0 { 94 | return "x86_64"; 95 | } 96 | return "arm64"; 97 | } 98 | 99 | "unknown" 100 | } 101 | -------------------------------------------------------------------------------- /src/columns/ccgroup.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use regex::Regex; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct Ccgroup { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | pat_user: Regex, 14 | pat_machine: Regex, 15 | pat_lxc_monitor: Regex, 16 | pat_lxc_payload: Regex, 17 | pat_scope: Regex, 18 | pat_service: Regex, 19 | pat_slice: Regex, 20 | } 21 | 22 | impl Ccgroup { 23 | pub fn new(header: Option) -> Self { 24 | let header = header.unwrap_or_else(|| String::from("Cgroup (compressed)")); 25 | let unit = String::new(); 26 | Self { 27 | fmt_contents: HashMap::new(), 28 | raw_contents: HashMap::new(), 29 | width: 0, 30 | header, 31 | unit, 32 | pat_user: Regex::new(r"/user-([^/]*)\.slice").unwrap(), 33 | pat_machine: Regex::new(r"/machine-([^/]*)\.scope").unwrap(), 34 | pat_lxc_monitor: Regex::new(r"/lxc\.monitor\.([^/]*)").unwrap(), 35 | pat_lxc_payload: Regex::new(r"/lxc\.payload\.([^/]*)").unwrap(), 36 | pat_scope: Regex::new(r"/([^/]*)\.scope").unwrap(), 37 | pat_service: Regex::new(r"/([^/]*)\.service").unwrap(), 38 | pat_slice: Regex::new(r"/([^/]*)\.slice").unwrap(), 39 | } 40 | } 41 | } 42 | 43 | macro_rules! replace { 44 | ( $x: ident, $pat: expr, $fmt: literal) => { 45 | if let Some(x) = $pat.captures(&$x) { 46 | $pat.replace(&$x, &format!($fmt, &x[1])).to_string() 47 | } else { 48 | $x 49 | } 50 | }; 51 | } 52 | 53 | #[cfg(any(target_os = "linux", target_os = "android"))] 54 | impl Column for Ccgroup { 55 | fn add(&mut self, proc: &ProcessInfo) { 56 | let fmt_content = if let Ok(cgroups) = &proc.curr_proc.cgroups() { 57 | let name = cgroups 58 | .last() 59 | .map_or_else(|| "".to_string(), |x| x.pathname.to_string()); 60 | let name = name.replace("/system.slice", "/[S]"); 61 | let name = name.replace("/user.slice", "/[U]"); 62 | let name = replace!(name, self.pat_user, "/[U:{}]"); 63 | let name = name.replace("/machine.slice", "/[M]"); 64 | let name = replace!(name, self.pat_machine, "/[SNC:{}]"); 65 | let name = replace!(name, self.pat_lxc_monitor, "/[LXC:{}]"); 66 | let name = replace!(name, self.pat_lxc_payload, "/[lxc:{}]"); 67 | let name = replace!(name, self.pat_scope, "/!{}"); 68 | let name = replace!(name, self.pat_service, "/{}"); 69 | replace!(name, self.pat_slice, "/[{}]") 70 | } else { 71 | "".to_string() 72 | }; 73 | let raw_content = fmt_content.clone(); 74 | 75 | self.fmt_contents.insert(proc.pid, fmt_content); 76 | self.raw_contents.insert(proc.pid, raw_content); 77 | } 78 | 79 | column_default!(String, false); 80 | } 81 | -------------------------------------------------------------------------------- /src/columns/cgroup.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Cgroup { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Cgroup { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("Cgroup")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for Cgroup { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let fmt_content = if let Ok(cgroups) = &proc.curr_proc.cgroups() { 32 | cgroups 33 | .last() 34 | .map_or_else(|| "".to_string(), |x| x.pathname.to_string()) 35 | } else { 36 | "".to_string() 37 | }; 38 | let raw_content = fmt_content.clone(); 39 | 40 | self.fmt_contents.insert(proc.pid, fmt_content); 41 | self.raw_contents.insert(proc.pid, raw_content); 42 | } 43 | 44 | column_default!(String, false); 45 | } 46 | -------------------------------------------------------------------------------- /src/columns/command.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Command { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Command { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("Command")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for Command { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let fmt_content = if let Ok(cmd) = &proc.curr_proc.cmdline() { 32 | if !cmd.is_empty() { 33 | let mut cmd = cmd 34 | .iter() 35 | .cloned() 36 | .map(|mut x| { 37 | x.push(' '); 38 | x 39 | }) 40 | .collect::(); 41 | cmd.pop(); 42 | cmd = cmd.replace(['\n', '\t'], " "); 43 | cmd 44 | } else { 45 | format!("[{}]", proc.curr_proc.stat().comm) 46 | } 47 | } else { 48 | proc.curr_proc.stat().comm.clone() 49 | }; 50 | let raw_content = fmt_content.clone(); 51 | 52 | self.fmt_contents.insert(proc.pid, fmt_content); 53 | self.raw_contents.insert(proc.pid, raw_content); 54 | } 55 | 56 | column_default!(String, false); 57 | } 58 | 59 | #[cfg(target_os = "macos")] 60 | impl Column for Command { 61 | fn add(&mut self, proc: &ProcessInfo) { 62 | let fmt_content = if let Some(path) = &proc.curr_path { 63 | if !path.cmd.is_empty() { 64 | let mut cmd = path 65 | .cmd 66 | .iter() 67 | .cloned() 68 | .map(|mut x| { 69 | x.push(' '); 70 | x 71 | }) 72 | .collect::(); 73 | cmd.pop(); 74 | cmd = cmd.replace(['\n', '\t'], " "); 75 | cmd 76 | } else { 77 | String::from("") 78 | } 79 | } else { 80 | String::from("") 81 | }; 82 | let raw_content = fmt_content.clone(); 83 | 84 | self.fmt_contents.insert(proc.pid, fmt_content); 85 | self.raw_contents.insert(proc.pid, raw_content); 86 | } 87 | 88 | column_default!(String, false); 89 | } 90 | 91 | #[cfg(target_os = "windows")] 92 | impl Column for Command { 93 | fn add(&mut self, proc: &ProcessInfo) { 94 | let fmt_content = proc.command.clone(); 95 | let raw_content = fmt_content.clone(); 96 | 97 | self.fmt_contents.insert(proc.pid, fmt_content); 98 | self.raw_contents.insert(proc.pid, raw_content); 99 | } 100 | 101 | column_default!(String, false); 102 | } 103 | 104 | #[cfg(target_os = "freebsd")] 105 | impl Column for Command { 106 | fn add(&mut self, proc: &ProcessInfo) { 107 | let command = if proc.curr_proc.arg.is_empty() { 108 | let comm = crate::util::ptr_to_cstr(proc.curr_proc.info.comm.as_ref()); 109 | if let Ok(comm) = comm { 110 | format!("[{}]", comm.to_string_lossy()) 111 | } else { 112 | String::from("") 113 | } 114 | } else { 115 | let mut x = String::from(""); 116 | for arg in &proc.curr_proc.arg { 117 | x.push_str(&arg); 118 | x.push_str(" "); 119 | } 120 | x 121 | }; 122 | let fmt_content = command; 123 | let raw_content = fmt_content.clone(); 124 | 125 | self.fmt_contents.insert(proc.pid, fmt_content); 126 | self.raw_contents.insert(proc.pid, raw_content); 127 | } 128 | 129 | column_default!(String, false); 130 | } 131 | -------------------------------------------------------------------------------- /src/columns/context_sw.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct ContextSw { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl ContextSw { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("ContextSw")); 18 | let unit = String::new(); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for ContextSw { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 33 | if status.voluntary_ctxt_switches.is_some() 34 | && status.nonvoluntary_ctxt_switches.is_some() 35 | { 36 | let sw = status.voluntary_ctxt_switches.unwrap() 37 | + status.nonvoluntary_ctxt_switches.unwrap(); 38 | (bytify(sw), sw) 39 | } else { 40 | (String::new(), 0) 41 | } 42 | } else { 43 | (String::new(), 0) 44 | }; 45 | 46 | self.fmt_contents.insert(proc.pid, fmt_content); 47 | self.raw_contents.insert(proc.pid, raw_content); 48 | } 49 | 50 | column_default!(u64, true); 51 | } 52 | 53 | #[cfg(target_os = "macos")] 54 | impl Column for ContextSw { 55 | fn add(&mut self, proc: &ProcessInfo) { 56 | let raw_content = proc.curr_task.ptinfo.pti_csw as u64; 57 | let fmt_content = bytify(raw_content); 58 | 59 | self.fmt_contents.insert(proc.pid, fmt_content); 60 | self.raw_contents.insert(proc.pid, raw_content); 61 | } 62 | 63 | column_default!(u64, true); 64 | } 65 | 66 | #[cfg(target_os = "freebsd")] 67 | impl Column for ContextSw { 68 | fn add(&mut self, proc: &ProcessInfo) { 69 | let raw_content = 70 | (proc.curr_proc.info.rusage.nvcsw + proc.curr_proc.info.rusage.nivcsw) as u64; 71 | let fmt_content = bytify(raw_content); 72 | 73 | self.fmt_contents.insert(proc.pid, fmt_content); 74 | self.raw_contents.insert(proc.pid, raw_content); 75 | } 76 | 77 | column_default!(u64, true); 78 | } 79 | -------------------------------------------------------------------------------- /src/columns/cpu_time.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, util, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct CpuTime { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl CpuTime { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("CPU Time")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for CpuTime { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let time_sec = (proc.curr_proc.stat().utime + proc.curr_proc.stat().stime) 32 | / procfs::ticks_per_second(); 33 | 34 | let fmt_content = util::parse_time(time_sec); 35 | let raw_content = time_sec; 36 | 37 | self.fmt_contents.insert(proc.pid, fmt_content); 38 | self.raw_contents.insert(proc.pid, raw_content); 39 | } 40 | 41 | column_default!(u64, true); 42 | } 43 | 44 | #[cfg(target_os = "macos")] 45 | impl Column for CpuTime { 46 | fn add(&mut self, proc: &ProcessInfo) { 47 | let time_sec = (proc.curr_task.ptinfo.pti_total_user 48 | + proc.curr_task.ptinfo.pti_total_system) 49 | / 1_000_000_000u64; 50 | 51 | let fmt_content = util::parse_time(time_sec); 52 | let raw_content = time_sec; 53 | 54 | self.fmt_contents.insert(proc.pid, fmt_content); 55 | self.raw_contents.insert(proc.pid, raw_content); 56 | } 57 | 58 | column_default!(u64, true); 59 | } 60 | 61 | #[cfg(target_os = "windows")] 62 | impl Column for CpuTime { 63 | fn add(&mut self, proc: &ProcessInfo) { 64 | let time_sec = (proc.cpu_info.curr_sys + proc.cpu_info.curr_user) / 10_000_000u64; 65 | 66 | let fmt_content = util::parse_time(time_sec); 67 | let raw_content = time_sec; 68 | 69 | self.fmt_contents.insert(proc.pid, fmt_content); 70 | self.raw_contents.insert(proc.pid, raw_content); 71 | } 72 | 73 | column_default!(u64, true); 74 | } 75 | 76 | #[cfg(target_os = "freebsd")] 77 | impl Column for CpuTime { 78 | fn add(&mut self, proc: &ProcessInfo) { 79 | let time_sec = ((proc.curr_proc.info.rusage.utime.sec * 1_000_000i64 80 | + proc.curr_proc.info.rusage.utime.usec 81 | + proc.curr_proc.info.rusage.stime.sec * 1_000_000i64 82 | + proc.curr_proc.info.rusage.stime.usec) 83 | / 1_000_000) as u64; 84 | 85 | let fmt_content = util::parse_time(time_sec); 86 | let raw_content = time_sec; 87 | 88 | self.fmt_contents.insert(proc.pid, fmt_content); 89 | self.raw_contents.insert(proc.pid, raw_content); 90 | } 91 | 92 | column_default!(u64, true); 93 | } 94 | -------------------------------------------------------------------------------- /src/columns/docker.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use dockworker::container::ContainerFilters; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | use tokio::runtime::Runtime; 7 | 8 | pub struct Docker { 9 | header: String, 10 | unit: String, 11 | fmt_contents: HashMap, 12 | raw_contents: HashMap, 13 | width: usize, 14 | #[cfg(any(target_os = "linux", target_os = "android"))] 15 | containers: HashMap, 16 | #[cfg(target_os = "macos")] 17 | containers: HashMap, 18 | available: bool, 19 | } 20 | 21 | #[cfg(any(target_os = "linux", target_os = "android"))] 22 | impl Docker { 23 | pub fn new(header: Option, path: &str) -> Self { 24 | let header = header.unwrap_or_else(|| String::from("Docker")); 25 | let unit = String::new(); 26 | let mut containers = HashMap::new(); 27 | let mut available = true; 28 | if let Ok(docker) = dockworker::Docker::connect_with_unix(path) { 29 | let rt = Runtime::new().unwrap(); 30 | if let Ok(cont) = 31 | rt.block_on(docker.list_containers(None, None, None, ContainerFilters::new())) 32 | { 33 | for c in cont { 34 | // remove the first letter '/' from container name 35 | let name = String::from(&c.Names[0][1..]); 36 | containers.insert(c.Id, name); 37 | } 38 | } else { 39 | available = false; 40 | } 41 | } else { 42 | available = false; 43 | } 44 | Self { 45 | fmt_contents: HashMap::new(), 46 | raw_contents: HashMap::new(), 47 | width: 0, 48 | header, 49 | unit, 50 | containers, 51 | available, 52 | } 53 | } 54 | } 55 | 56 | #[cfg(target_os = "macos")] 57 | impl Docker { 58 | pub fn new(header: Option, path: &str) -> Self { 59 | let header = header.unwrap_or_else(|| String::from("Docker")); 60 | let unit = String::from(""); 61 | let mut containers = HashMap::new(); 62 | let mut available = true; 63 | if let Ok(docker) = dockworker::Docker::connect_with_unix(path) { 64 | let rt = Runtime::new().unwrap(); 65 | if let Ok(cont) = 66 | rt.block_on(docker.list_containers(None, None, None, ContainerFilters::new())) 67 | { 68 | for c in cont { 69 | // remove the first letter '/' from container name 70 | let name = String::from(&c.Names[0][1..]); 71 | if let Ok(processes) = rt.block_on(docker.processes(c.Id.as_str())) { 72 | for p in processes { 73 | if let Ok(pid) = p.pid.parse::() { 74 | containers.insert(pid, name.clone()); 75 | } 76 | } 77 | } 78 | } 79 | } else { 80 | available = false; 81 | } 82 | } else { 83 | available = false; 84 | } 85 | Docker { 86 | fmt_contents: HashMap::new(), 87 | raw_contents: HashMap::new(), 88 | width: 0, 89 | header, 90 | unit, 91 | containers, 92 | available, 93 | } 94 | } 95 | } 96 | 97 | #[cfg(any(target_os = "linux", target_os = "android"))] 98 | impl Column for Docker { 99 | fn add(&mut self, proc: &ProcessInfo) { 100 | let fmt_content = if let Ok(cgroups) = proc.curr_proc.cgroups() { 101 | let mut ret = String::new(); 102 | for cgroup in cgroups { 103 | let cgroup_name = cgroup.pathname.clone(); 104 | if cgroup_name.starts_with("/docker") { 105 | let container_id = cgroup_name.replace("/docker/", ""); 106 | if let Some(name) = self.containers.get(&container_id) { 107 | ret = name.to_string(); 108 | break; 109 | } else { 110 | ret = String::from("?"); 111 | break; 112 | } 113 | } else if cgroup_name.starts_with("/system.slice/docker-") { 114 | let container_id = cgroup_name 115 | .replace("/system.slice/docker-", "") 116 | .replace(".scope", ""); 117 | if let Some(name) = self.containers.get(&container_id) { 118 | ret = name.to_string(); 119 | break; 120 | } else { 121 | ret = String::from("?"); 122 | break; 123 | } 124 | } 125 | } 126 | ret 127 | } else { 128 | String::new() 129 | }; 130 | let raw_content = fmt_content.clone(); 131 | 132 | self.fmt_contents.insert(proc.pid, fmt_content); 133 | self.raw_contents.insert(proc.pid, raw_content); 134 | } 135 | 136 | fn available(&self) -> bool { 137 | self.available 138 | } 139 | 140 | column_default!(String, false); 141 | } 142 | 143 | #[cfg(target_os = "macos")] 144 | impl Column for Docker { 145 | fn add(&mut self, proc: &ProcessInfo) { 146 | let fmt_content = if let Some(name) = self.containers.get(&proc.pid) { 147 | name.to_string() 148 | } else { 149 | String::from("") 150 | }; 151 | let raw_content = fmt_content.clone(); 152 | 153 | self.fmt_contents.insert(proc.pid, fmt_content); 154 | self.raw_contents.insert(proc.pid, raw_content); 155 | } 156 | 157 | fn available(&self) -> bool { 158 | self.available 159 | } 160 | 161 | column_default!(String, false); 162 | } 163 | -------------------------------------------------------------------------------- /src/columns/eip.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Eip { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Eip { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("EIP")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for Eip { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let raw_content = proc.curr_proc.stat().kstkeip; 31 | let fmt_content = format!("{raw_content:016x}"); 32 | 33 | self.fmt_contents.insert(proc.pid, fmt_content); 34 | self.raw_contents.insert(proc.pid, raw_content); 35 | } 36 | 37 | column_default!(u64, true); 38 | } 39 | -------------------------------------------------------------------------------- /src/columns/elapsed_time.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | #[cfg(not(target_os = "windows"))] 4 | use chrono::offset::TimeZone; 5 | #[cfg(any(target_os = "linux", target_os = "android"))] 6 | use chrono::DateTime; 7 | use chrono::{Duration, Local}; 8 | #[cfg(any(target_os = "linux", target_os = "android"))] 9 | use once_cell::sync::Lazy; 10 | use std::cmp; 11 | use std::collections::HashMap; 12 | 13 | #[cfg(any(target_os = "linux", target_os = "android"))] 14 | static TICKS_PER_SECOND: Lazy = Lazy::new(procfs::ticks_per_second); 15 | 16 | pub struct ElapsedTime { 17 | header: String, 18 | unit: String, 19 | fmt_contents: HashMap, 20 | raw_contents: HashMap, 21 | width: usize, 22 | #[cfg(any(target_os = "linux", target_os = "android"))] 23 | boot_time: DateTime, 24 | } 25 | 26 | impl ElapsedTime { 27 | pub fn new(header: Option) -> Self { 28 | let header = header.unwrap_or_else(|| String::from("Elapsed")); 29 | let unit = String::new(); 30 | Self { 31 | fmt_contents: HashMap::new(), 32 | raw_contents: HashMap::new(), 33 | width: 0, 34 | header, 35 | unit, 36 | #[cfg(any(target_os = "linux", target_os = "android"))] 37 | boot_time: procfs::boot_time().unwrap_or_else(|_| Local.timestamp_opt(0, 0).unwrap()), 38 | } 39 | } 40 | } 41 | 42 | fn format_duration(duration: Duration) -> String { 43 | let years = duration.num_weeks() as f64 / 52.0; 44 | let weeks = duration.num_days() as f64 / 7.0; 45 | let days = duration.num_hours() as f64 / 24.0; 46 | let hours = duration.num_minutes() as f64 / 60.0; 47 | let minutes = duration.num_seconds() as f64 / 60.0; 48 | let seconds = duration.num_seconds(); 49 | 50 | if years > 1.0 { 51 | format!("{years:.1}years") 52 | } else if weeks > 1.0 { 53 | format!("{weeks:.1}weeks") 54 | } else if days > 1.0 { 55 | format!("{days:.1}days") 56 | } else if hours > 1.0 { 57 | format!("{hours:.1}hours") 58 | } else if minutes > 1.0 { 59 | format!("{minutes:.1}minutes") 60 | } else { 61 | format!("{seconds:.1}seconds") 62 | } 63 | } 64 | 65 | #[cfg(any(target_os = "linux", target_os = "android"))] 66 | impl Column for ElapsedTime { 67 | fn add(&mut self, proc: &ProcessInfo) { 68 | let starttime = proc.curr_proc.stat().starttime; 69 | let seconds_since_boot = starttime as f32 / *TICKS_PER_SECOND as f32; 70 | let start_time = self.boot_time 71 | + Duration::try_milliseconds((seconds_since_boot * 1000.0) as i64).unwrap_or_default(); 72 | let raw_content = Local::now().signed_duration_since(start_time); 73 | let fmt_content = format_duration(raw_content); 74 | 75 | self.fmt_contents.insert(proc.pid, fmt_content); 76 | self.raw_contents.insert(proc.pid, raw_content); 77 | } 78 | 79 | column_default!(Duration, false); 80 | } 81 | 82 | #[cfg(target_os = "macos")] 83 | impl Column for ElapsedTime { 84 | fn add(&mut self, proc: &ProcessInfo) { 85 | let start_time = Local 86 | .timestamp_opt(proc.curr_task.pbsd.pbi_start_tvsec as i64, 0) 87 | .unwrap(); 88 | let raw_content = Local::now().signed_duration_since(start_time); 89 | let fmt_content = format_duration(raw_content); 90 | 91 | self.fmt_contents.insert(proc.pid, fmt_content); 92 | self.raw_contents.insert(proc.pid, raw_content); 93 | } 94 | 95 | column_default!(Duration, false); 96 | } 97 | 98 | #[cfg(target_os = "windows")] 99 | impl Column for ElapsedTime { 100 | fn add(&mut self, proc: &ProcessInfo) { 101 | let raw_content = Local::now().signed_duration_since(proc.start_time); 102 | let fmt_content = format_duration(raw_content); 103 | 104 | self.fmt_contents.insert(proc.pid, fmt_content); 105 | self.raw_contents.insert(proc.pid, raw_content); 106 | } 107 | 108 | column_default!(Duration, false); 109 | } 110 | 111 | #[cfg(target_os = "freebsd")] 112 | impl Column for ElapsedTime { 113 | fn add(&mut self, proc: &ProcessInfo) { 114 | let start_time = Local 115 | .timestamp_opt(proc.curr_proc.info.start.sec as i64, 0) 116 | .unwrap(); 117 | let raw_content = Local::now().signed_duration_since(start_time); 118 | let fmt_content = format_duration(raw_content); 119 | 120 | self.fmt_contents.insert(proc.pid, fmt_content); 121 | self.raw_contents.insert(proc.pid, raw_content); 122 | } 123 | 124 | column_default!(Duration, false); 125 | } 126 | -------------------------------------------------------------------------------- /src/columns/empty.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Empty { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Empty { 15 | pub fn new() -> Self { 16 | let header = String::new(); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for Empty { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let raw_content = String::new(); 31 | let fmt_content = String::new(); 32 | 33 | self.fmt_contents.insert(proc.pid, fmt_content); 34 | self.raw_contents.insert(proc.pid, raw_content); 35 | } 36 | 37 | column_default!(String, false); 38 | } 39 | -------------------------------------------------------------------------------- /src/columns/env.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | use std::path::PathBuf; 6 | 7 | pub struct Env { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | #[allow(dead_code)] 14 | procfs: Option, 15 | } 16 | 17 | impl Env { 18 | pub fn new(header: Option, procfs: Option) -> Self { 19 | let header = header.unwrap_or_else(|| String::from("Env")); 20 | let unit = String::new(); 21 | Self { 22 | fmt_contents: HashMap::new(), 23 | raw_contents: HashMap::new(), 24 | width: 0, 25 | header, 26 | unit, 27 | procfs, 28 | } 29 | } 30 | } 31 | 32 | #[cfg(any(target_os = "linux", target_os = "android"))] 33 | impl Column for Env { 34 | fn add(&mut self, proc: &ProcessInfo) { 35 | let mut fmt_content = String::new(); 36 | if let Ok(proc) = crate::util::process_new(proc.pid, &self.procfs) { 37 | if let Ok(envs) = proc.environ() { 38 | for (k, v) in envs { 39 | fmt_content.push_str(&format!( 40 | "{}=\"{}\" ", 41 | k.to_string_lossy(), 42 | v.to_string_lossy().replace('\"', "\\\"") 43 | )); 44 | } 45 | } 46 | } 47 | let raw_content = fmt_content.clone(); 48 | 49 | self.fmt_contents.insert(proc.pid, fmt_content); 50 | self.raw_contents.insert(proc.pid, raw_content); 51 | } 52 | 53 | column_default!(String, false); 54 | } 55 | 56 | #[cfg(target_os = "freebsd")] 57 | impl Column for Env { 58 | fn add(&mut self, proc: &ProcessInfo) { 59 | let mut fmt_content = String::new(); 60 | for env in &proc.curr_proc.env { 61 | fmt_content.push_str(&format!("{} ", env.replace('\"', "\\\""))); 62 | } 63 | let raw_content = fmt_content.clone(); 64 | 65 | self.fmt_contents.insert(proc.pid, fmt_content); 66 | self.raw_contents.insert(proc.pid, raw_content); 67 | } 68 | 69 | column_default!(String, false); 70 | } 71 | -------------------------------------------------------------------------------- /src/columns/esp.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Esp { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Esp { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("ESP")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for Esp { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let raw_content = proc.curr_proc.stat().kstkesp; 31 | let fmt_content = format!("{raw_content:016x}"); 32 | 33 | self.fmt_contents.insert(proc.pid, fmt_content); 34 | self.raw_contents.insert(proc.pid, raw_content); 35 | } 36 | 37 | column_default!(u64, true); 38 | } 39 | -------------------------------------------------------------------------------- /src/columns/file_name.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct FileName { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl FileName { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("FileName")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for FileName { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let raw_content = proc.curr_proc.stat().comm.clone(); 32 | let fmt_content = raw_content.clone(); 33 | 34 | self.fmt_contents.insert(proc.pid, fmt_content); 35 | self.raw_contents.insert(proc.pid, raw_content); 36 | } 37 | 38 | column_default!(String, false); 39 | } 40 | 41 | #[cfg(target_os = "freebsd")] 42 | impl Column for FileName { 43 | fn add(&mut self, proc: &ProcessInfo) { 44 | let comm = crate::util::ptr_to_cstr(proc.curr_proc.info.comm.as_ref()); 45 | let comm = if let Ok(comm) = comm { 46 | comm.to_string_lossy().into_owned() 47 | } else { 48 | String::from("") 49 | }; 50 | let fmt_content = comm; 51 | let raw_content = fmt_content.clone(); 52 | 53 | self.fmt_contents.insert(proc.pid, fmt_content); 54 | self.raw_contents.insert(proc.pid, raw_content); 55 | } 56 | 57 | column_default!(String, false); 58 | } 59 | -------------------------------------------------------------------------------- /src/columns/gid.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | #[cfg(target_os = "windows")] 3 | use crate::util::format_sid; 4 | use crate::{column_default, Column}; 5 | use std::cmp; 6 | use std::collections::HashMap; 7 | 8 | pub struct Gid { 9 | header: String, 10 | unit: String, 11 | fmt_contents: HashMap, 12 | raw_contents: HashMap, 13 | width: usize, 14 | #[allow(dead_code)] 15 | abbr_sid: bool, 16 | } 17 | 18 | impl Gid { 19 | pub fn new(header: Option, abbr_sid: bool) -> Self { 20 | let header = header.unwrap_or_else(|| String::from("GID")); 21 | let unit = String::new(); 22 | Self { 23 | fmt_contents: HashMap::new(), 24 | raw_contents: HashMap::new(), 25 | width: 0, 26 | header, 27 | unit, 28 | abbr_sid, 29 | } 30 | } 31 | } 32 | 33 | #[cfg(any(target_os = "linux", target_os = "android"))] 34 | impl Column for Gid { 35 | fn add(&mut self, proc: &ProcessInfo) { 36 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 37 | let gid = status.egid; 38 | (format!("{gid}"), gid) 39 | } else { 40 | (String::new(), 0) 41 | }; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u32, true); 48 | } 49 | 50 | #[cfg(target_os = "macos")] 51 | impl Column for Gid { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let gid = proc.curr_task.pbsd.pbi_gid; 54 | let fmt_content = format!("{}", gid); 55 | let raw_content = gid; 56 | 57 | self.fmt_contents.insert(proc.pid, fmt_content); 58 | self.raw_contents.insert(proc.pid, raw_content); 59 | } 60 | 61 | column_default!(u32, true); 62 | } 63 | 64 | #[cfg(target_os = "windows")] 65 | impl Column for Gid { 66 | fn add(&mut self, proc: &ProcessInfo) { 67 | let mut sid = &proc.groups[0].sid; 68 | let mut kind = u64::MAX; 69 | for g in &proc.groups { 70 | if g.sid.len() > 3 && g.sid[1] == 5 && g.sid[2] == 32 && kind > g.sid[3] { 71 | sid = &g.sid; 72 | kind = g.sid[3]; 73 | } 74 | } 75 | 76 | let fmt_content = format_sid(sid, self.abbr_sid); 77 | let raw_content = sid[sid.len() - 1] as u32; 78 | 79 | self.fmt_contents.insert(proc.pid, fmt_content); 80 | self.raw_contents.insert(proc.pid, raw_content); 81 | } 82 | 83 | column_default!(u32, true); 84 | } 85 | 86 | #[cfg(target_os = "freebsd")] 87 | impl Column for Gid { 88 | fn add(&mut self, proc: &ProcessInfo) { 89 | let gid = proc.curr_proc.info.svgid; 90 | let fmt_content = format!("{}", gid); 91 | let raw_content = gid; 92 | 93 | self.fmt_contents.insert(proc.pid, fmt_content); 94 | self.raw_contents.insert(proc.pid, raw_content); 95 | } 96 | 97 | column_default!(u32, true); 98 | } 99 | -------------------------------------------------------------------------------- /src/columns/gid_fs.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct GidFs { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl GidFs { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("FGID")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for GidFs { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 31 | let gid = status.fgid; 32 | (format!("{gid}"), gid) 33 | } else { 34 | (String::new(), 0) 35 | }; 36 | 37 | self.fmt_contents.insert(proc.pid, fmt_content); 38 | self.raw_contents.insert(proc.pid, raw_content); 39 | } 40 | 41 | column_default!(u32, true); 42 | } 43 | -------------------------------------------------------------------------------- /src/columns/gid_real.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct GidReal { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl GidReal { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("RGID")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for GidReal { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 32 | let gid = status.rgid; 33 | (format!("{gid}"), gid) 34 | } else { 35 | (String::new(), 0) 36 | }; 37 | 38 | self.fmt_contents.insert(proc.pid, fmt_content); 39 | self.raw_contents.insert(proc.pid, raw_content); 40 | } 41 | 42 | column_default!(u32, true); 43 | } 44 | 45 | #[cfg(target_os = "macos")] 46 | impl Column for GidReal { 47 | fn add(&mut self, proc: &ProcessInfo) { 48 | let gid = proc.curr_task.pbsd.pbi_rgid; 49 | let fmt_content = format!("{}", gid); 50 | let raw_content = gid; 51 | 52 | self.fmt_contents.insert(proc.pid, fmt_content); 53 | self.raw_contents.insert(proc.pid, raw_content); 54 | } 55 | 56 | column_default!(u32, true); 57 | } 58 | 59 | #[cfg(target_os = "freebsd")] 60 | impl Column for GidReal { 61 | fn add(&mut self, proc: &ProcessInfo) { 62 | let gid = proc.curr_proc.info.rgid; 63 | let fmt_content = format!("{}", gid); 64 | let raw_content = gid; 65 | 66 | self.fmt_contents.insert(proc.pid, fmt_content); 67 | self.raw_contents.insert(proc.pid, raw_content); 68 | } 69 | 70 | column_default!(u32, true); 71 | } 72 | -------------------------------------------------------------------------------- /src/columns/gid_saved.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct GidSaved { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl GidSaved { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("SGID")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for GidSaved { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 32 | let gid = status.sgid; 33 | (format!("{gid}"), gid) 34 | } else { 35 | (String::new(), 0) 36 | }; 37 | 38 | self.fmt_contents.insert(proc.pid, fmt_content); 39 | self.raw_contents.insert(proc.pid, raw_content); 40 | } 41 | 42 | column_default!(u32, true); 43 | } 44 | 45 | #[cfg(target_os = "macos")] 46 | impl Column for GidSaved { 47 | fn add(&mut self, proc: &ProcessInfo) { 48 | let gid = proc.curr_task.pbsd.pbi_svgid; 49 | let fmt_content = format!("{}", gid); 50 | let raw_content = gid; 51 | 52 | self.fmt_contents.insert(proc.pid, fmt_content); 53 | self.raw_contents.insert(proc.pid, raw_content); 54 | } 55 | 56 | column_default!(u32, true); 57 | } 58 | 59 | #[cfg(target_os = "freebsd")] 60 | impl Column for GidSaved { 61 | fn add(&mut self, proc: &ProcessInfo) { 62 | let gid = proc.curr_proc.info.svgid; 63 | let fmt_content = format!("{}", gid); 64 | let raw_content = gid; 65 | 66 | self.fmt_contents.insert(proc.pid, fmt_content); 67 | self.raw_contents.insert(proc.pid, raw_content); 68 | } 69 | 70 | column_default!(u32, true); 71 | } 72 | -------------------------------------------------------------------------------- /src/columns/group.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | #[cfg(target_os = "windows")] 3 | use crate::util::format_sid; 4 | #[cfg(not(target_os = "windows"))] 5 | use crate::util::USERS_CACHE; 6 | use crate::{column_default, Column}; 7 | use std::cmp; 8 | use std::collections::HashMap; 9 | #[cfg(not(target_os = "windows"))] 10 | use uzers::Groups; 11 | 12 | pub struct Group { 13 | header: String, 14 | unit: String, 15 | fmt_contents: HashMap, 16 | raw_contents: HashMap, 17 | width: usize, 18 | #[allow(dead_code)] 19 | abbr_sid: bool, 20 | } 21 | 22 | impl Group { 23 | pub fn new(header: Option, abbr_sid: bool) -> Self { 24 | let header = header.unwrap_or_else(|| String::from("Group")); 25 | let unit = String::new(); 26 | Self { 27 | fmt_contents: HashMap::new(), 28 | raw_contents: HashMap::new(), 29 | width: 0, 30 | header, 31 | unit, 32 | abbr_sid, 33 | } 34 | } 35 | } 36 | 37 | #[cfg(any(target_os = "linux", target_os = "android"))] 38 | impl Column for Group { 39 | fn add(&mut self, proc: &ProcessInfo) { 40 | let fmt_content = if let Some(ref status) = proc.curr_status { 41 | let gid = status.egid; 42 | if let Some(group) = USERS_CACHE.with(|x| x.borrow_mut().get_group_by_gid(gid)) { 43 | format!("{}", group.name().to_string_lossy()) 44 | } else { 45 | format!("{gid}") 46 | } 47 | } else { 48 | String::new() 49 | }; 50 | let raw_content = fmt_content.clone(); 51 | 52 | self.fmt_contents.insert(proc.pid, fmt_content); 53 | self.raw_contents.insert(proc.pid, raw_content); 54 | } 55 | 56 | column_default!(String, false); 57 | } 58 | 59 | #[cfg(target_os = "macos")] 60 | impl Column for Group { 61 | fn add(&mut self, proc: &ProcessInfo) { 62 | let gid = proc.curr_task.pbsd.pbi_gid; 63 | let fmt_content = 64 | if let Some(group) = USERS_CACHE.with(|x| x.borrow_mut().get_group_by_gid(gid)) { 65 | format!("{}", group.name().to_string_lossy()) 66 | } else { 67 | format!("{}", gid) 68 | }; 69 | let raw_content = fmt_content.clone(); 70 | 71 | self.fmt_contents.insert(proc.pid, fmt_content); 72 | self.raw_contents.insert(proc.pid, raw_content); 73 | } 74 | 75 | column_default!(String, false); 76 | } 77 | 78 | #[cfg(target_os = "windows")] 79 | impl Column for Group { 80 | fn add(&mut self, proc: &ProcessInfo) { 81 | let mut sid_name = &proc.groups[0]; 82 | let mut kind = u64::MAX; 83 | for g in &proc.groups { 84 | if g.sid.len() > 3 && g.sid[1] == 5 && g.sid[2] == 32 && kind > g.sid[3] { 85 | sid_name = g; 86 | kind = g.sid[3]; 87 | } 88 | } 89 | 90 | let fmt_content = if let Some(name) = &sid_name.name { 91 | name.clone() 92 | } else { 93 | format_sid(&sid_name.sid, self.abbr_sid) 94 | }; 95 | let raw_content = fmt_content.clone(); 96 | 97 | self.fmt_contents.insert(proc.pid, fmt_content); 98 | self.raw_contents.insert(proc.pid, raw_content); 99 | } 100 | 101 | column_default!(String, false); 102 | } 103 | 104 | #[cfg(target_os = "freebsd")] 105 | impl Column for Group { 106 | fn add(&mut self, proc: &ProcessInfo) { 107 | let gid = proc.curr_proc.info.svgid; 108 | let fmt_content = 109 | if let Some(group) = USERS_CACHE.with(|x| x.borrow_mut().get_group_by_gid(gid)) { 110 | format!("{}", group.name().to_string_lossy()) 111 | } else { 112 | format!("{gid}") 113 | }; 114 | let raw_content = fmt_content.clone(); 115 | 116 | self.fmt_contents.insert(proc.pid, fmt_content); 117 | self.raw_contents.insert(proc.pid, raw_content); 118 | } 119 | 120 | column_default!(String, false); 121 | } 122 | -------------------------------------------------------------------------------- /src/columns/group_fs.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::USERS_CACHE; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | use uzers::Groups; 7 | 8 | pub struct GroupFs { 9 | header: String, 10 | unit: String, 11 | fmt_contents: HashMap, 12 | raw_contents: HashMap, 13 | width: usize, 14 | } 15 | 16 | impl GroupFs { 17 | pub fn new(header: Option) -> Self { 18 | let header = header.unwrap_or_else(|| String::from("File System Group")); 19 | let unit = String::new(); 20 | Self { 21 | fmt_contents: HashMap::new(), 22 | raw_contents: HashMap::new(), 23 | width: 0, 24 | header, 25 | unit, 26 | } 27 | } 28 | } 29 | 30 | impl Column for GroupFs { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let fmt_content = if let Some(ref status) = proc.curr_status { 33 | let gid = status.fgid; 34 | if let Some(group) = USERS_CACHE.with(|x| x.borrow_mut().get_group_by_gid(gid)) { 35 | format!("{}", group.name().to_string_lossy()) 36 | } else { 37 | format!("{gid}") 38 | } 39 | } else { 40 | String::new() 41 | }; 42 | let raw_content = fmt_content.clone(); 43 | 44 | self.fmt_contents.insert(proc.pid, fmt_content); 45 | self.raw_contents.insert(proc.pid, raw_content); 46 | } 47 | 48 | column_default!(String, false); 49 | } 50 | -------------------------------------------------------------------------------- /src/columns/group_real.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::USERS_CACHE; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | use uzers::Groups; 7 | 8 | pub struct GroupReal { 9 | header: String, 10 | unit: String, 11 | fmt_contents: HashMap, 12 | raw_contents: HashMap, 13 | width: usize, 14 | } 15 | 16 | impl GroupReal { 17 | pub fn new(header: Option) -> Self { 18 | let header = header.unwrap_or_else(|| String::from("Real Group")); 19 | let unit = String::new(); 20 | Self { 21 | fmt_contents: HashMap::new(), 22 | raw_contents: HashMap::new(), 23 | width: 0, 24 | header, 25 | unit, 26 | } 27 | } 28 | } 29 | 30 | #[cfg(any(target_os = "linux", target_os = "android"))] 31 | impl Column for GroupReal { 32 | fn add(&mut self, proc: &ProcessInfo) { 33 | let fmt_content = if let Some(ref status) = proc.curr_status { 34 | let gid = status.rgid; 35 | if let Some(group) = USERS_CACHE.with(|x| x.borrow_mut().get_group_by_gid(gid)) { 36 | format!("{}", group.name().to_string_lossy()) 37 | } else { 38 | format!("{gid}") 39 | } 40 | } else { 41 | String::new() 42 | }; 43 | let raw_content = fmt_content.clone(); 44 | 45 | self.fmt_contents.insert(proc.pid, fmt_content); 46 | self.raw_contents.insert(proc.pid, raw_content); 47 | } 48 | 49 | column_default!(String, false); 50 | } 51 | 52 | #[cfg(target_os = "macos")] 53 | impl Column for GroupReal { 54 | fn add(&mut self, proc: &ProcessInfo) { 55 | let gid = proc.curr_task.pbsd.pbi_rgid; 56 | let fmt_content = 57 | if let Some(group) = USERS_CACHE.with(|x| x.borrow_mut().get_group_by_gid(gid)) { 58 | format!("{}", group.name().to_string_lossy()) 59 | } else { 60 | format!("{}", gid) 61 | }; 62 | let raw_content = fmt_content.clone(); 63 | 64 | self.fmt_contents.insert(proc.pid, fmt_content); 65 | self.raw_contents.insert(proc.pid, raw_content); 66 | } 67 | 68 | column_default!(String, false); 69 | } 70 | 71 | #[cfg(target_os = "freebsd")] 72 | impl Column for GroupReal { 73 | fn add(&mut self, proc: &ProcessInfo) { 74 | let gid = proc.curr_proc.info.rgid; 75 | let fmt_content = 76 | if let Some(group) = USERS_CACHE.with(|x| x.borrow_mut().get_group_by_gid(gid)) { 77 | format!("{}", group.name().to_string_lossy()) 78 | } else { 79 | format!("{gid}") 80 | }; 81 | let raw_content = fmt_content.clone(); 82 | 83 | self.fmt_contents.insert(proc.pid, fmt_content); 84 | self.raw_contents.insert(proc.pid, raw_content); 85 | } 86 | 87 | column_default!(String, false); 88 | } 89 | -------------------------------------------------------------------------------- /src/columns/group_saved.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::USERS_CACHE; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | use uzers::Groups; 7 | 8 | pub struct GroupSaved { 9 | header: String, 10 | unit: String, 11 | fmt_contents: HashMap, 12 | raw_contents: HashMap, 13 | width: usize, 14 | } 15 | 16 | impl GroupSaved { 17 | pub fn new(header: Option) -> Self { 18 | let header = header.unwrap_or_else(|| String::from("Saved Group")); 19 | let unit = String::new(); 20 | Self { 21 | fmt_contents: HashMap::new(), 22 | raw_contents: HashMap::new(), 23 | width: 0, 24 | header, 25 | unit, 26 | } 27 | } 28 | } 29 | 30 | #[cfg(any(target_os = "linux", target_os = "android"))] 31 | impl Column for GroupSaved { 32 | fn add(&mut self, proc: &ProcessInfo) { 33 | let fmt_content = if let Some(ref status) = proc.curr_status { 34 | let gid = status.sgid; 35 | if let Some(group) = USERS_CACHE.with(|x| x.borrow_mut().get_group_by_gid(gid)) { 36 | format!("{}", group.name().to_string_lossy()) 37 | } else { 38 | format!("{gid}") 39 | } 40 | } else { 41 | String::new() 42 | }; 43 | let raw_content = fmt_content.clone(); 44 | 45 | self.fmt_contents.insert(proc.pid, fmt_content); 46 | self.raw_contents.insert(proc.pid, raw_content); 47 | } 48 | 49 | column_default!(String, false); 50 | } 51 | 52 | #[cfg(target_os = "macos")] 53 | impl Column for GroupSaved { 54 | fn add(&mut self, proc: &ProcessInfo) { 55 | let gid = proc.curr_task.pbsd.pbi_svgid; 56 | let fmt_content = 57 | if let Some(group) = USERS_CACHE.with(|x| x.borrow_mut().get_group_by_gid(gid)) { 58 | format!("{}", group.name().to_string_lossy()) 59 | } else { 60 | format!("{}", gid) 61 | }; 62 | let raw_content = fmt_content.clone(); 63 | 64 | self.fmt_contents.insert(proc.pid, fmt_content); 65 | self.raw_contents.insert(proc.pid, raw_content); 66 | } 67 | 68 | column_default!(String, false); 69 | } 70 | 71 | #[cfg(target_os = "freebsd")] 72 | impl Column for GroupSaved { 73 | fn add(&mut self, proc: &ProcessInfo) { 74 | let gid = proc.curr_proc.info.svgid; 75 | let fmt_content = 76 | if let Some(group) = USERS_CACHE.with(|x| x.borrow_mut().get_group_by_gid(gid)) { 77 | format!("{}", group.name().to_string_lossy()) 78 | } else { 79 | format!("{gid}") 80 | }; 81 | let raw_content = fmt_content.clone(); 82 | 83 | self.fmt_contents.insert(proc.pid, fmt_content); 84 | self.raw_contents.insert(proc.pid, raw_content); 85 | } 86 | 87 | column_default!(String, false); 88 | } 89 | -------------------------------------------------------------------------------- /src/columns/maj_flt.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct MajFlt { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl MajFlt { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("MajorFaults")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for MajFlt { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let raw_content = proc.curr_proc.stat().majflt; 32 | let fmt_content = format!("{raw_content}"); 33 | 34 | self.fmt_contents.insert(proc.pid, fmt_content); 35 | self.raw_contents.insert(proc.pid, raw_content); 36 | } 37 | 38 | column_default!(u64, true); 39 | } 40 | 41 | #[cfg(target_os = "macos")] 42 | impl Column for MajFlt { 43 | fn add(&mut self, proc: &ProcessInfo) { 44 | let raw_content = proc.curr_task.ptinfo.pti_pageins as u64; 45 | let fmt_content = format!("{raw_content}"); 46 | 47 | self.fmt_contents.insert(proc.pid, fmt_content); 48 | self.raw_contents.insert(proc.pid, raw_content); 49 | } 50 | 51 | column_default!(u64, true); 52 | } 53 | 54 | #[cfg(target_os = "windows")] 55 | impl Column for MajFlt { 56 | fn add(&mut self, proc: &ProcessInfo) { 57 | let raw_content = proc.memory_info.page_fault_count; 58 | let fmt_content = format!("{raw_content}"); 59 | 60 | self.fmt_contents.insert(proc.pid, fmt_content); 61 | self.raw_contents.insert(proc.pid, raw_content); 62 | } 63 | 64 | column_default!(u64, true); 65 | } 66 | 67 | #[cfg(target_os = "freebsd")] 68 | impl Column for MajFlt { 69 | fn add(&mut self, proc: &ProcessInfo) { 70 | let raw_content = proc.curr_proc.info.rusage.majflt as u64; 71 | let fmt_content = format!("{raw_content}"); 72 | 73 | self.fmt_contents.insert(proc.pid, fmt_content); 74 | self.raw_contents.insert(proc.pid, raw_content); 75 | } 76 | 77 | column_default!(u64, true); 78 | } 79 | -------------------------------------------------------------------------------- /src/columns/min_flt.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct MinFlt { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl MinFlt { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("MinorFaults")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for MinFlt { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let raw_content = proc.curr_proc.stat().minflt; 32 | let fmt_content = format!("{raw_content}"); 33 | 34 | self.fmt_contents.insert(proc.pid, fmt_content); 35 | self.raw_contents.insert(proc.pid, raw_content); 36 | } 37 | 38 | column_default!(u64, true); 39 | } 40 | 41 | #[cfg(target_os = "macos")] 42 | impl Column for MinFlt { 43 | fn add(&mut self, proc: &ProcessInfo) { 44 | let raw_content = 45 | (proc.curr_task.ptinfo.pti_faults - proc.curr_task.ptinfo.pti_pageins) as u64; 46 | let fmt_content = format!("{}", raw_content); 47 | 48 | self.fmt_contents.insert(proc.pid, fmt_content); 49 | self.raw_contents.insert(proc.pid, raw_content); 50 | } 51 | 52 | column_default!(u64, true); 53 | } 54 | 55 | #[cfg(target_os = "freebsd")] 56 | impl Column for MinFlt { 57 | fn add(&mut self, proc: &ProcessInfo) { 58 | let raw_content = proc.curr_proc.info.rusage.minflt as u64; 59 | let fmt_content = format!("{raw_content}"); 60 | 61 | self.fmt_contents.insert(proc.pid, fmt_content); 62 | self.raw_contents.insert(proc.pid, raw_content); 63 | } 64 | 65 | column_default!(u64, true); 66 | } 67 | -------------------------------------------------------------------------------- /src/columns/multi_slot.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct MultiSlot { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl MultiSlot { 15 | pub fn new() -> Self { 16 | let header = String::new(); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for MultiSlot { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let raw_content = String::new(); 31 | let fmt_content = String::new(); 32 | 33 | self.fmt_contents.insert(proc.pid, fmt_content); 34 | self.raw_contents.insert(proc.pid, raw_content); 35 | } 36 | 37 | column_default!(String, false); 38 | } 39 | -------------------------------------------------------------------------------- /src/columns/nice.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Nice { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Nice { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("Nice")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for Nice { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let raw_content = proc.curr_proc.stat().nice; 32 | let fmt_content = format!("{raw_content}"); 33 | 34 | self.fmt_contents.insert(proc.pid, fmt_content); 35 | self.raw_contents.insert(proc.pid, raw_content); 36 | } 37 | 38 | column_default!(i64, true); 39 | } 40 | 41 | #[cfg(target_os = "macos")] 42 | impl Column for Nice { 43 | fn add(&mut self, proc: &ProcessInfo) { 44 | let raw_content = proc.curr_task.pbsd.pbi_nice as i64; 45 | let fmt_content = format!("{}", raw_content); 46 | 47 | self.fmt_contents.insert(proc.pid, fmt_content); 48 | self.raw_contents.insert(proc.pid, raw_content); 49 | } 50 | 51 | column_default!(i64, true); 52 | } 53 | 54 | #[cfg(target_os = "freebsd")] 55 | impl Column for Nice { 56 | fn add(&mut self, proc: &ProcessInfo) { 57 | let raw_content = proc.curr_proc.info.nice as i64; 58 | let fmt_content = format!("{raw_content}"); 59 | 60 | self.fmt_contents.insert(proc.pid, fmt_content); 61 | self.raw_contents.insert(proc.pid, raw_content); 62 | } 63 | 64 | column_default!(i64, true); 65 | } 66 | -------------------------------------------------------------------------------- /src/columns/pgid.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Pgid { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Pgid { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("PGID")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for Pgid { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let raw_content = proc.curr_proc.stat().pgrp; 32 | let fmt_content = match proc.curr_proc { 33 | crate::process::ProcessTask::Process { .. } => format!("{raw_content}"), 34 | _ => format!("[{raw_content}]"), 35 | }; 36 | 37 | self.fmt_contents.insert(proc.pid, fmt_content); 38 | self.raw_contents.insert(proc.pid, raw_content); 39 | } 40 | 41 | column_default!(i32, true); 42 | } 43 | 44 | #[cfg(target_os = "macos")] 45 | impl Column for Pgid { 46 | fn add(&mut self, proc: &ProcessInfo) { 47 | let raw_content = proc.curr_task.pbsd.pbi_pgid as i32; 48 | let fmt_content = format!("{}", raw_content); 49 | 50 | self.fmt_contents.insert(proc.pid, fmt_content); 51 | self.raw_contents.insert(proc.pid, raw_content); 52 | } 53 | 54 | column_default!(i32, true); 55 | } 56 | 57 | #[cfg(target_os = "freebsd")] 58 | impl Column for Pgid { 59 | fn add(&mut self, proc: &ProcessInfo) { 60 | let raw_content = proc.curr_proc.info.pgid as i32; 61 | let fmt_content = format!("{}", raw_content); 62 | 63 | self.fmt_contents.insert(proc.pid, fmt_content); 64 | self.raw_contents.insert(proc.pid, raw_content); 65 | } 66 | 67 | column_default!(i32, true); 68 | } 69 | -------------------------------------------------------------------------------- /src/columns/pid.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Pid { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Pid { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("PID")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for Pid { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let raw_content = proc.pid; 32 | let fmt_content = match proc.curr_proc { 33 | crate::process::ProcessTask::Process { .. } => format!("{raw_content}"), 34 | _ => format!("[{raw_content}]"), 35 | }; 36 | 37 | self.fmt_contents.insert(proc.pid, fmt_content); 38 | self.raw_contents.insert(proc.pid, raw_content); 39 | } 40 | 41 | column_default!(i32, true); 42 | } 43 | 44 | #[cfg(not(any(target_os = "linux", target_os = "android")))] 45 | impl Column for Pid { 46 | fn add(&mut self, proc: &ProcessInfo) { 47 | let raw_content = proc.pid; 48 | let fmt_content = format!("{raw_content}"); 49 | 50 | self.fmt_contents.insert(proc.pid, fmt_content); 51 | self.raw_contents.insert(proc.pid, raw_content); 52 | } 53 | 54 | column_default!(i32, true); 55 | } 56 | -------------------------------------------------------------------------------- /src/columns/policy.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Policy { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Policy { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("Policy")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(target_os = "linux")] 29 | impl Column for Policy { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let fmt_content = match proc.curr_proc.stat().policy.map(|x| x as i32) { 32 | Some(libc::SCHED_BATCH) => String::from("B"), 33 | Some(libc::SCHED_FIFO) => String::from("FF"), 34 | Some(libc::SCHED_IDLE) => String::from("IDL"), 35 | Some(libc::SCHED_OTHER) => String::from("TS"), 36 | Some(libc::SCHED_RR) => String::from("RR"), 37 | _ => String::new(), 38 | }; 39 | let raw_content = fmt_content.clone(); 40 | 41 | self.fmt_contents.insert(proc.pid, fmt_content); 42 | self.raw_contents.insert(proc.pid, raw_content); 43 | } 44 | 45 | column_default!(String, false); 46 | } 47 | 48 | #[cfg(target_os = "android")] 49 | impl Column for Policy { 50 | fn add(&mut self, proc: &ProcessInfo) { 51 | let fmt_content = match proc.curr_proc.stat().policy.map(|x| x as i32) { 52 | Some(libc::SCHED_NORMAL) => String::from("N"), 53 | Some(libc::SCHED_FIFO) => String::from("FF"), 54 | Some(libc::SCHED_RR) => String::from("RR"), 55 | Some(libc::SCHED_BATCH) => String::from("B"), 56 | Some(libc::SCHED_IDLE) => String::from("IDL"), 57 | Some(libc::SCHED_DEADLINE) => String::from("D"), 58 | _ => String::from(""), 59 | }; 60 | let raw_content = fmt_content.clone(); 61 | 62 | self.fmt_contents.insert(proc.pid, fmt_content); 63 | self.raw_contents.insert(proc.pid, raw_content); 64 | } 65 | 66 | column_default!(String, false); 67 | } 68 | 69 | #[cfg(target_os = "macos")] 70 | impl Column for Policy { 71 | fn add(&mut self, proc: &ProcessInfo) { 72 | let fmt_content = match proc.curr_task.ptinfo.pti_policy { 73 | 1 => String::from("TS"), 74 | 2 => String::from("RR"), 75 | 4 => String::from("FF"), 76 | _ => String::from(""), 77 | }; 78 | let raw_content = fmt_content.clone(); 79 | 80 | self.fmt_contents.insert(proc.pid, fmt_content); 81 | self.raw_contents.insert(proc.pid, raw_content); 82 | } 83 | 84 | column_default!(String, false); 85 | } 86 | -------------------------------------------------------------------------------- /src/columns/ppid.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Ppid { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Ppid { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("Parent PID")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for Ppid { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let raw_content = proc.ppid; 31 | let fmt_content = format!("{raw_content}"); 32 | 33 | self.fmt_contents.insert(proc.pid, fmt_content); 34 | self.raw_contents.insert(proc.pid, raw_content); 35 | } 36 | 37 | column_default!(i32, true); 38 | } 39 | -------------------------------------------------------------------------------- /src/columns/priority.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Priority { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Priority { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("Priority")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for Priority { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let raw_content = proc.curr_proc.stat().priority; 32 | let fmt_content = format!("{raw_content}"); 33 | 34 | self.fmt_contents.insert(proc.pid, fmt_content); 35 | self.raw_contents.insert(proc.pid, raw_content); 36 | } 37 | 38 | column_default!(i64, true); 39 | } 40 | 41 | #[cfg(target_os = "macos")] 42 | impl Column for Priority { 43 | fn add(&mut self, proc: &ProcessInfo) { 44 | let raw_content = proc.curr_task.ptinfo.pti_priority as i64; 45 | let fmt_content = format!("{}", raw_content); 46 | 47 | self.fmt_contents.insert(proc.pid, fmt_content); 48 | self.raw_contents.insert(proc.pid, raw_content); 49 | } 50 | 51 | column_default!(i64, true); 52 | } 53 | 54 | #[cfg(target_os = "windows")] 55 | impl Column for Priority { 56 | fn add(&mut self, proc: &ProcessInfo) { 57 | let raw_content = i64::from(proc.priority); 58 | let fmt_content = match raw_content { 59 | 0x0020 => String::from("Normal"), 60 | 0x0040 => String::from("Idle"), 61 | 0x0080 => String::from("High"), 62 | 0x0100 => String::from("Realtime"), 63 | 0x4000 => String::from("BelowNormal"), 64 | 0x8000 => String::from("AboveNormal"), 65 | _ => String::from("Unknown"), 66 | }; 67 | 68 | self.fmt_contents.insert(proc.pid, fmt_content); 69 | self.raw_contents.insert(proc.pid, raw_content); 70 | } 71 | 72 | column_default!(i64, true); 73 | } 74 | 75 | #[cfg(target_os = "freebsd")] 76 | impl Column for Priority { 77 | fn add(&mut self, proc: &ProcessInfo) { 78 | let raw_content = proc.curr_proc.info.pri.level as i64 - 100; 79 | let fmt_content = format!("{raw_content}"); 80 | 81 | self.fmt_contents.insert(proc.pid, fmt_content); 82 | self.raw_contents.insert(proc.pid, raw_content); 83 | } 84 | 85 | column_default!(i64, true); 86 | } 87 | -------------------------------------------------------------------------------- /src/columns/processor.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Processor { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Processor { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("Processor")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for Processor { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let raw_content = proc.curr_proc.stat().processor.unwrap_or_default(); 32 | let fmt_content = if let Some(p) = proc.curr_proc.stat().processor { 33 | format!("{p}") 34 | } else { 35 | String::new() 36 | }; 37 | 38 | self.fmt_contents.insert(proc.pid, fmt_content); 39 | self.raw_contents.insert(proc.pid, raw_content); 40 | } 41 | 42 | column_default!(i32, true); 43 | } 44 | 45 | #[cfg(target_os = "freebsd")] 46 | impl Column for Processor { 47 | fn add(&mut self, proc: &ProcessInfo) { 48 | let raw_content = proc.curr_proc.info.lastcpu; 49 | let fmt_content = format!("{raw_content}"); 50 | 51 | self.fmt_contents.insert(proc.pid, fmt_content); 52 | self.raw_contents.insert(proc.pid, raw_content); 53 | } 54 | 55 | column_default!(i32, true); 56 | } 57 | -------------------------------------------------------------------------------- /src/columns/read_bytes.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct ReadBytes { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl ReadBytes { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("Read")); 18 | let unit = String::from("[B/s]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for ReadBytes { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let (fmt_content, raw_content) = if proc.curr_io.is_some() && proc.prev_io.is_some() { 33 | let interval_ms = proc.interval.as_secs() + u64::from(proc.interval.subsec_millis()); 34 | let io = (proc.curr_io.as_ref().unwrap().read_bytes 35 | - proc.prev_io.as_ref().unwrap().read_bytes) 36 | * 1000 37 | / interval_ms; 38 | (bytify(io), io) 39 | } else { 40 | (String::new(), 0) 41 | }; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u64, true); 48 | } 49 | 50 | #[cfg(target_os = "macos")] 51 | impl Column for ReadBytes { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let (fmt_content, raw_content) = if proc.curr_res.is_some() && proc.prev_res.is_some() { 54 | let interval_ms = proc.interval.as_secs() + u64::from(proc.interval.subsec_millis()); 55 | let io = (proc.curr_res.as_ref().unwrap().ri_diskio_bytesread 56 | - proc.prev_res.as_ref().unwrap().ri_diskio_bytesread) 57 | * 1000 58 | / interval_ms; 59 | (bytify(io), io) 60 | } else { 61 | (String::from(""), 0) 62 | }; 63 | 64 | self.fmt_contents.insert(proc.pid, fmt_content); 65 | self.raw_contents.insert(proc.pid, raw_content); 66 | } 67 | 68 | column_default!(u64, true); 69 | } 70 | 71 | #[cfg(target_os = "windows")] 72 | impl Column for ReadBytes { 73 | fn add(&mut self, proc: &ProcessInfo) { 74 | let interval_ms = proc.interval.as_secs() + u64::from(proc.interval.subsec_millis()); 75 | let io = (proc.disk_info.curr_read - proc.disk_info.prev_read) * 1000 / interval_ms; 76 | 77 | let raw_content = io; 78 | let fmt_content = bytify(raw_content); 79 | 80 | self.fmt_contents.insert(proc.pid, fmt_content); 81 | self.raw_contents.insert(proc.pid, raw_content); 82 | } 83 | 84 | column_default!(u64, true); 85 | } 86 | 87 | #[cfg(target_os = "freebsd")] 88 | impl Column for ReadBytes { 89 | fn add(&mut self, proc: &ProcessInfo) { 90 | // io block size: 128KB 91 | let block_size = 128 * 1024; 92 | let interval_ms = proc.interval.as_secs() + u64::from(proc.interval.subsec_millis()); 93 | let io = (proc.curr_proc.info.rusage.inblock as u64 94 | - proc.prev_proc.info.rusage.inblock as u64) 95 | * block_size 96 | * 1000 97 | / interval_ms; 98 | 99 | let raw_content = io; 100 | let fmt_content = bytify(raw_content); 101 | 102 | self.fmt_contents.insert(proc.pid, fmt_content); 103 | self.raw_contents.insert(proc.pid, raw_content); 104 | } 105 | 106 | column_default!(u64, true); 107 | } 108 | -------------------------------------------------------------------------------- /src/columns/rt_priority.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct RtPriority { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl RtPriority { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("RT Priority")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for RtPriority { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let raw_content = proc.curr_proc.stat().rt_priority.unwrap_or_default(); 31 | let fmt_content = if let Some(p) = proc.curr_proc.stat().rt_priority { 32 | format!("{p}") 33 | } else { 34 | String::new() 35 | }; 36 | 37 | self.fmt_contents.insert(proc.pid, fmt_content); 38 | self.raw_contents.insert(proc.pid, raw_content); 39 | } 40 | 41 | column_default!(u32, true); 42 | } 43 | -------------------------------------------------------------------------------- /src/columns/sec_context.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | #[cfg(any(target_os = "linux", target_os = "android"))] 6 | use std::io::Read; 7 | use std::path::PathBuf; 8 | 9 | pub struct SecContext { 10 | header: String, 11 | unit: String, 12 | fmt_contents: HashMap, 13 | raw_contents: HashMap, 14 | width: usize, 15 | procfs: Option, 16 | } 17 | 18 | impl SecContext { 19 | pub fn new(header: Option, procfs: Option) -> Self { 20 | let header = header.unwrap_or_else(|| String::from("Context")); 21 | let unit = String::new(); 22 | Self { 23 | fmt_contents: HashMap::new(), 24 | raw_contents: HashMap::new(), 25 | width: 0, 26 | header, 27 | unit, 28 | procfs, 29 | } 30 | } 31 | } 32 | 33 | #[cfg(any(target_os = "linux", target_os = "android"))] 34 | impl Column for SecContext { 35 | fn add(&mut self, proc: &ProcessInfo) { 36 | let fmt_content = if let Ok(proc) = crate::util::process_new(proc.pid, &self.procfs) { 37 | if let Ok(mut file) = proc.open_relative("attr/current") { 38 | let mut ret = String::new(); 39 | let _ = file.read_to_string(&mut ret); 40 | ret.trim_end_matches('\x00').to_string() 41 | } else { 42 | String::from("") 43 | } 44 | } else { 45 | String::from("") 46 | }; 47 | let raw_content = fmt_content.clone(); 48 | 49 | self.fmt_contents.insert(proc.pid, fmt_content); 50 | self.raw_contents.insert(proc.pid, raw_content); 51 | } 52 | 53 | column_default!(String, false); 54 | } 55 | -------------------------------------------------------------------------------- /src/columns/separator.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Separator { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | separator: String, 13 | } 14 | 15 | impl Separator { 16 | pub fn new(separator: &str) -> Self { 17 | let header = String::from(separator); 18 | let unit = String::from(separator); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | separator: String::from(separator), 26 | } 27 | } 28 | } 29 | 30 | impl Column for Separator { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let raw_content = self.separator.clone(); 33 | let fmt_content = self.separator.clone(); 34 | 35 | self.fmt_contents.insert(proc.pid, fmt_content); 36 | self.raw_contents.insert(proc.pid, raw_content); 37 | } 38 | 39 | fn sortable(&self) -> bool { 40 | false 41 | } 42 | 43 | column_default!(String, false); 44 | } 45 | -------------------------------------------------------------------------------- /src/columns/session.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | #[cfg(target_os = "macos")] 4 | use nix::unistd::{self, Pid}; 5 | use std::cmp; 6 | use std::collections::HashMap; 7 | 8 | pub struct Session { 9 | header: String, 10 | unit: String, 11 | fmt_contents: HashMap, 12 | raw_contents: HashMap, 13 | width: usize, 14 | } 15 | 16 | impl Session { 17 | pub fn new(header: Option) -> Self { 18 | let header = header.unwrap_or_else(|| String::from("Session")); 19 | let unit = String::new(); 20 | Self { 21 | fmt_contents: HashMap::new(), 22 | raw_contents: HashMap::new(), 23 | width: 0, 24 | header, 25 | unit, 26 | } 27 | } 28 | } 29 | 30 | #[cfg(any(target_os = "linux", target_os = "android"))] 31 | impl Column for Session { 32 | fn add(&mut self, proc: &ProcessInfo) { 33 | let raw_content = proc.curr_proc.stat().session; 34 | let fmt_content = match proc.curr_proc { 35 | crate::process::ProcessTask::Process { .. } => format!("{raw_content}"), 36 | _ => format!("[{raw_content}]"), 37 | }; 38 | 39 | self.fmt_contents.insert(proc.pid, fmt_content); 40 | self.raw_contents.insert(proc.pid, raw_content); 41 | } 42 | 43 | column_default!(i32, true); 44 | } 45 | 46 | #[cfg(target_os = "macos")] 47 | impl Column for Session { 48 | fn add(&mut self, proc: &ProcessInfo) { 49 | let sid = unistd::getsid(Some(Pid::from_raw(proc.pid))) 50 | .map(|x| x.as_raw()) 51 | .unwrap_or(0); 52 | let raw_content = sid; 53 | let fmt_content = format!("{}", raw_content); 54 | 55 | self.fmt_contents.insert(proc.pid, fmt_content); 56 | self.raw_contents.insert(proc.pid, raw_content); 57 | } 58 | 59 | column_default!(i32, true); 60 | } 61 | 62 | #[cfg(target_os = "freebsd")] 63 | impl Column for Session { 64 | fn add(&mut self, proc: &ProcessInfo) { 65 | let raw_content = proc.curr_proc.info.sid; 66 | let fmt_content = format!("{}", raw_content); 67 | 68 | self.fmt_contents.insert(proc.pid, fmt_content); 69 | self.raw_contents.insert(proc.pid, raw_content); 70 | } 71 | 72 | column_default!(i32, true); 73 | } 74 | -------------------------------------------------------------------------------- /src/columns/shd_pnd.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct ShdPnd { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl ShdPnd { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("ShdPnd")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for ShdPnd { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 32 | let val = status.shdpnd; 33 | (format!("{val:016x}"), val) 34 | } else { 35 | (String::new(), 0) 36 | }; 37 | 38 | self.fmt_contents.insert(proc.pid, fmt_content); 39 | self.raw_contents.insert(proc.pid, raw_content); 40 | } 41 | 42 | column_default!(u64, true); 43 | } 44 | 45 | #[cfg(target_os = "freebsd")] 46 | impl Column for ShdPnd { 47 | fn add(&mut self, proc: &ProcessInfo) { 48 | let raw_content = proc.curr_proc.info.siglist.0[0] as u64; 49 | let fmt_content = format!("{raw_content:016x}"); 50 | 51 | self.fmt_contents.insert(proc.pid, fmt_content); 52 | self.raw_contents.insert(proc.pid, raw_content); 53 | } 54 | 55 | column_default!(u64, true); 56 | } 57 | -------------------------------------------------------------------------------- /src/columns/sig_blk.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct SigBlk { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl SigBlk { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("SigBlk")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for SigBlk { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 32 | let val = status.sigblk; 33 | (format!("{val:016x}"), val) 34 | } else { 35 | (String::new(), 0) 36 | }; 37 | 38 | self.fmt_contents.insert(proc.pid, fmt_content); 39 | self.raw_contents.insert(proc.pid, raw_content); 40 | } 41 | 42 | column_default!(u64, true); 43 | } 44 | 45 | #[cfg(target_os = "freebsd")] 46 | impl Column for SigBlk { 47 | fn add(&mut self, proc: &ProcessInfo) { 48 | let raw_content = proc.curr_proc.info.sigmask.0[0] as u64; 49 | let fmt_content = format!("{raw_content:016x}"); 50 | 51 | self.fmt_contents.insert(proc.pid, fmt_content); 52 | self.raw_contents.insert(proc.pid, raw_content); 53 | } 54 | 55 | column_default!(u64, true); 56 | } 57 | -------------------------------------------------------------------------------- /src/columns/sig_cgt.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct SigCgt { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl SigCgt { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("SigCgt")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for SigCgt { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 32 | let val = status.sigcgt; 33 | (format!("{val:016x}"), val) 34 | } else { 35 | (String::new(), 0) 36 | }; 37 | 38 | self.fmt_contents.insert(proc.pid, fmt_content); 39 | self.raw_contents.insert(proc.pid, raw_content); 40 | } 41 | 42 | column_default!(u64, true); 43 | } 44 | 45 | #[cfg(target_os = "freebsd")] 46 | impl Column for SigCgt { 47 | fn add(&mut self, proc: &ProcessInfo) { 48 | let raw_content = proc.curr_proc.info.sigcatch.0[0] as u64; 49 | let fmt_content = format!("{raw_content:016x}"); 50 | 51 | self.fmt_contents.insert(proc.pid, fmt_content); 52 | self.raw_contents.insert(proc.pid, raw_content); 53 | } 54 | 55 | column_default!(u64, true); 56 | } 57 | -------------------------------------------------------------------------------- /src/columns/sig_ign.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct SigIgn { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl SigIgn { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("SigIgn")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for SigIgn { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 32 | let val = status.sigign; 33 | (format!("{val:016x}"), val) 34 | } else { 35 | (String::new(), 0) 36 | }; 37 | 38 | self.fmt_contents.insert(proc.pid, fmt_content); 39 | self.raw_contents.insert(proc.pid, raw_content); 40 | } 41 | 42 | column_default!(u64, true); 43 | } 44 | 45 | #[cfg(target_os = "freebsd")] 46 | impl Column for SigIgn { 47 | fn add(&mut self, proc: &ProcessInfo) { 48 | let raw_content = proc.curr_proc.info.sigignore.0[0] as u64; 49 | let fmt_content = format!("{raw_content:016x}"); 50 | 51 | self.fmt_contents.insert(proc.pid, fmt_content); 52 | self.raw_contents.insert(proc.pid, raw_content); 53 | } 54 | 55 | column_default!(u64, true); 56 | } 57 | -------------------------------------------------------------------------------- /src/columns/sig_pnd.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct SigPnd { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl SigPnd { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("SigPnd")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for SigPnd { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 31 | let val = status.sigpnd; 32 | (format!("{val:016x}"), val) 33 | } else { 34 | (String::new(), 0) 35 | }; 36 | 37 | self.fmt_contents.insert(proc.pid, fmt_content); 38 | self.raw_contents.insert(proc.pid, raw_content); 39 | } 40 | 41 | column_default!(u64, true); 42 | } 43 | -------------------------------------------------------------------------------- /src/columns/slot.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Slot { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Slot { 15 | pub fn new() -> Self { 16 | let header = String::new(); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for Slot { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let raw_content = String::new(); 31 | let fmt_content = String::new(); 32 | 33 | self.fmt_contents.insert(proc.pid, fmt_content); 34 | self.raw_contents.insert(proc.pid, raw_content); 35 | } 36 | 37 | column_default!(String, false); 38 | } 39 | -------------------------------------------------------------------------------- /src/columns/ssb.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Ssb { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Ssb { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("Speculative Store Bypass")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for Ssb { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let fmt_content = if let Some(ref curr_status) = proc.curr_status { 31 | if let Some(ref val) = curr_status.speculation_store_bypass { 32 | val.clone() 33 | } else { 34 | String::new() 35 | } 36 | } else { 37 | String::new() 38 | }; 39 | let raw_content = fmt_content.clone(); 40 | 41 | self.fmt_contents.insert(proc.pid, fmt_content); 42 | self.raw_contents.insert(proc.pid, raw_content); 43 | } 44 | 45 | column_default!(String, false); 46 | } 47 | -------------------------------------------------------------------------------- /src/columns/start_time.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | #[cfg(not(target_os = "windows"))] 4 | use chrono::offset::TimeZone; 5 | #[cfg(any(target_os = "linux", target_os = "android"))] 6 | use chrono::Duration; 7 | use chrono::{DateTime, Local}; 8 | #[cfg(any(target_os = "linux", target_os = "android"))] 9 | use once_cell::sync::Lazy; 10 | use std::cmp; 11 | use std::collections::HashMap; 12 | 13 | #[cfg(any(target_os = "linux", target_os = "android"))] 14 | static TICKS_PER_SECOND: Lazy = Lazy::new(procfs::ticks_per_second); 15 | 16 | pub struct StartTime { 17 | header: String, 18 | unit: String, 19 | fmt_contents: HashMap, 20 | raw_contents: HashMap>, 21 | width: usize, 22 | #[cfg(any(target_os = "linux", target_os = "android"))] 23 | boot_time: DateTime, 24 | } 25 | 26 | impl StartTime { 27 | pub fn new(header: Option) -> Self { 28 | let header = header.unwrap_or_else(|| String::from("Start")); 29 | let unit = String::new(); 30 | Self { 31 | fmt_contents: HashMap::new(), 32 | raw_contents: HashMap::new(), 33 | width: 0, 34 | header, 35 | unit, 36 | #[cfg(any(target_os = "linux", target_os = "android"))] 37 | boot_time: procfs::boot_time().unwrap_or_else(|_| Local.timestamp_opt(0, 0).unwrap()), 38 | } 39 | } 40 | } 41 | 42 | #[cfg(any(target_os = "linux", target_os = "android"))] 43 | impl Column for StartTime { 44 | fn add(&mut self, proc: &ProcessInfo) { 45 | let starttime = proc.curr_proc.stat().starttime; 46 | let seconds_since_boot = starttime as f32 / *TICKS_PER_SECOND as f32; 47 | let raw_content = self.boot_time 48 | + Duration::try_milliseconds((seconds_since_boot * 1000.0) as i64).unwrap_or_default(); 49 | let fmt_content = format!("{}", raw_content.format("%Y/%m/%d %H:%M")); 50 | 51 | self.fmt_contents.insert(proc.pid, fmt_content); 52 | self.raw_contents.insert(proc.pid, raw_content); 53 | } 54 | 55 | column_default!(DateTime, false); 56 | } 57 | 58 | #[cfg(target_os = "macos")] 59 | impl Column for StartTime { 60 | fn add(&mut self, proc: &ProcessInfo) { 61 | let start_time = Local 62 | .timestamp_opt(proc.curr_task.pbsd.pbi_start_tvsec as i64, 0) 63 | .unwrap(); 64 | let raw_content = start_time; 65 | let fmt_content = format!("{}", start_time.format("%Y/%m/%d %H:%M")); 66 | 67 | self.fmt_contents.insert(proc.pid, fmt_content); 68 | self.raw_contents.insert(proc.pid, raw_content); 69 | } 70 | 71 | column_default!(DateTime, false); 72 | } 73 | 74 | #[cfg(target_os = "windows")] 75 | impl Column for StartTime { 76 | fn add(&mut self, proc: &ProcessInfo) { 77 | let raw_content = proc.start_time; 78 | let fmt_content = format!("{}", proc.start_time.format("%Y/%m/%d %H:%M")); 79 | 80 | self.fmt_contents.insert(proc.pid, fmt_content); 81 | self.raw_contents.insert(proc.pid, raw_content); 82 | } 83 | 84 | column_default!(DateTime, false); 85 | } 86 | 87 | #[cfg(target_os = "freebsd")] 88 | impl Column for StartTime { 89 | fn add(&mut self, proc: &ProcessInfo) { 90 | let start_time = Local 91 | .timestamp_opt(proc.curr_proc.info.start.sec as i64, 0) 92 | .unwrap(); 93 | let raw_content = start_time; 94 | let fmt_content = format!("{}", start_time.format("%Y/%m/%d %H:%M")); 95 | 96 | self.fmt_contents.insert(proc.pid, fmt_content); 97 | self.raw_contents.insert(proc.pid, raw_content); 98 | } 99 | 100 | column_default!(DateTime, false); 101 | } 102 | -------------------------------------------------------------------------------- /src/columns/state.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct State { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl State { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("State")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for State { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let fmt_content = format!("{}", proc.curr_proc.stat().state); 32 | let raw_content = fmt_content.clone(); 33 | 34 | self.fmt_contents.insert(proc.pid, fmt_content); 35 | self.raw_contents.insert(proc.pid, raw_content); 36 | } 37 | 38 | column_default!(String, false); 39 | } 40 | 41 | #[cfg(target_os = "macos")] 42 | impl Column for State { 43 | fn add(&mut self, proc: &ProcessInfo) { 44 | let mut state = 7; 45 | for t in &proc.curr_threads { 46 | let s = match t.pth_run_state { 47 | 1 => 1, // TH_STATE_RUNNING 48 | 2 => 5, // TH_STATE_STOPPED 49 | 3 => { 50 | if t.pth_sleep_time > 20 { 51 | 4 52 | } else { 53 | 3 54 | } 55 | } // TH_STATE_WAITING 56 | 4 => 2, // TH_STATE_UNINTERRUPTIBLE 57 | 5 => 6, // TH_STATE_HALTED 58 | _ => 7, 59 | }; 60 | state = cmp::min(s, state); 61 | } 62 | let state = match state { 63 | 0 => "", 64 | 1 => "R", 65 | 2 => "U", 66 | 3 => "S", 67 | 4 => "I", 68 | 5 => "T", 69 | 6 => "H", 70 | _ => "?", 71 | }; 72 | let fmt_content = state.to_string(); 73 | let raw_content = fmt_content.clone(); 74 | 75 | self.fmt_contents.insert(proc.pid, fmt_content); 76 | self.raw_contents.insert(proc.pid, raw_content); 77 | } 78 | 79 | column_default!(String, false); 80 | } 81 | 82 | #[cfg(target_os = "freebsd")] 83 | impl Column for State { 84 | fn add(&mut self, proc: &ProcessInfo) { 85 | let info = &proc.curr_proc.info; 86 | let flag = info.flag; 87 | let tdflags = info.tdflags; 88 | let cr_flags = info.cr_flags; 89 | let kiflag = info.kiflag; 90 | 91 | let mut state = match info.stat { 92 | libc::SSTOP => "T", 93 | libc::SSLEEP => { 94 | if (tdflags & libc::TDF_SINTR as i64) != 0 { 95 | if info.slptime >= 20 { 96 | "I" 97 | } else { 98 | "S" 99 | } 100 | } else { 101 | "D" 102 | } 103 | } 104 | libc::SRUN | libc::SIDL => "R", 105 | libc::SWAIT => "W", 106 | libc::SLOCK => "L", 107 | libc::SZOMB => "Z", 108 | _ => "?", 109 | } 110 | .to_string(); 111 | if (flag & libc::P_INMEM as i64) == 0 { 112 | state.push_str("W"); 113 | } 114 | if info.nice < libc::NZERO as i8 || info.pri.class == bsd_kvm_sys::PRI_REALTIME as u8 { 115 | state.push_str("<"); 116 | } 117 | if info.nice > libc::NZERO as i8 || info.pri.class == bsd_kvm_sys::PRI_IDLE as u8 { 118 | state.push_str("N"); 119 | } 120 | if (flag & libc::P_TRACED as i64) != 0 { 121 | state.push_str("X"); 122 | } 123 | if (flag & libc::P_WEXIT as i64) != 0 && info.stat != libc::SZOMB as std::os::raw::c_char { 124 | state.push_str("E"); 125 | } 126 | if (flag & libc::P_PPWAIT as i64) != 0 { 127 | state.push_str("V"); 128 | } 129 | if (flag & libc::P_SYSTEM as i64) != 0 || info.lock > 0 { 130 | state.push_str("L"); 131 | } 132 | if (cr_flags & libc::KI_CRF_CAPABILITY_MODE as u32) != 0 { 133 | state.push_str("C"); 134 | } 135 | if (kiflag & libc::KI_SLEADER as i64) != 0 { 136 | state.push_str("s"); 137 | } 138 | if (flag & libc::P_CONTROLT as i64) != 0 && info.pgid == info.tpgid { 139 | state.push_str("+"); 140 | } 141 | if (flag & libc::P_JAILED as i64) != 0 { 142 | state.push_str("J"); 143 | } 144 | let fmt_content = state; 145 | let raw_content = fmt_content.clone(); 146 | 147 | self.fmt_contents.insert(proc.pid, fmt_content); 148 | self.raw_contents.insert(proc.pid, raw_content); 149 | } 150 | 151 | column_default!(String, false); 152 | } 153 | -------------------------------------------------------------------------------- /src/columns/threads.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Threads { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Threads { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("Threads")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for Threads { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let raw_content = proc.curr_proc.stat().num_threads; 32 | let fmt_content = format!("{raw_content}"); 33 | 34 | self.fmt_contents.insert(proc.pid, fmt_content); 35 | self.raw_contents.insert(proc.pid, raw_content); 36 | } 37 | 38 | column_default!(i64, true); 39 | } 40 | 41 | #[cfg(target_os = "macos")] 42 | impl Column for Threads { 43 | fn add(&mut self, proc: &ProcessInfo) { 44 | let raw_content = proc.curr_task.ptinfo.pti_threadnum as i64; 45 | let fmt_content = format!("{}", raw_content); 46 | 47 | self.fmt_contents.insert(proc.pid, fmt_content); 48 | self.raw_contents.insert(proc.pid, raw_content); 49 | } 50 | 51 | column_default!(i64, true); 52 | } 53 | 54 | #[cfg(target_os = "windows")] 55 | impl Column for Threads { 56 | fn add(&mut self, proc: &ProcessInfo) { 57 | let raw_content = i64::from(proc.thread); 58 | let fmt_content = format!("{}", raw_content); 59 | 60 | self.fmt_contents.insert(proc.pid, fmt_content); 61 | self.raw_contents.insert(proc.pid, raw_content); 62 | } 63 | 64 | column_default!(i64, true); 65 | } 66 | 67 | #[cfg(target_os = "freebsd")] 68 | impl Column for Threads { 69 | fn add(&mut self, proc: &ProcessInfo) { 70 | let raw_content = proc.curr_proc.info.numthreads as i64; 71 | let fmt_content = format!("{}", raw_content); 72 | 73 | self.fmt_contents.insert(proc.pid, fmt_content); 74 | self.raw_contents.insert(proc.pid, raw_content); 75 | } 76 | 77 | column_default!(i64, true); 78 | } 79 | -------------------------------------------------------------------------------- /src/columns/tree_slot.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct TreeSlot { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl TreeSlot { 15 | pub fn new() -> Self { 16 | let header = String::new(); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for TreeSlot { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let raw_content = String::new(); 31 | let fmt_content = String::new(); 32 | 33 | self.fmt_contents.insert(proc.pid, fmt_content); 34 | self.raw_contents.insert(proc.pid, raw_content); 35 | } 36 | 37 | column_default!(String, false); 38 | } 39 | -------------------------------------------------------------------------------- /src/columns/tty.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Tty { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Tty { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("TTY")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for Tty { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (major, minor) = proc.curr_proc.stat().tty_nr(); 32 | let fmt_content = if major == 136 { 33 | format!("pts/{minor}") 34 | } else { 35 | String::new() 36 | }; 37 | let raw_content = fmt_content.clone(); 38 | 39 | self.fmt_contents.insert(proc.pid, fmt_content); 40 | self.raw_contents.insert(proc.pid, raw_content); 41 | } 42 | 43 | column_default!(String, false); 44 | } 45 | 46 | #[cfg(target_os = "macos")] 47 | impl Column for Tty { 48 | fn add(&mut self, proc: &ProcessInfo) { 49 | let dev = proc.curr_task.pbsd.e_tdev; 50 | let major = (dev >> 24) & 0xff; 51 | let minor = dev & 0xffffff; 52 | let fmt_content = if major == 16 { 53 | format!("s{:03}", minor) 54 | } else { 55 | String::from("") 56 | }; 57 | let raw_content = fmt_content.clone(); 58 | 59 | self.fmt_contents.insert(proc.pid, fmt_content); 60 | self.raw_contents.insert(proc.pid, raw_content); 61 | } 62 | 63 | column_default!(String, false); 64 | } 65 | 66 | #[cfg(target_os = "freebsd")] 67 | impl Column for Tty { 68 | fn add(&mut self, proc: &ProcessInfo) { 69 | let dev = proc.curr_proc.info.tdev; 70 | 71 | let mut buf = [0u8; 256]; 72 | 73 | let name = std::ffi::CString::new("kern.devname").unwrap(); 74 | let mut buf_size = std::mem::size_of::<[u8; 256]>(); 75 | let buf_ptr = buf.as_mut_ptr(); 76 | let dev_size = std::mem::size_of::(); 77 | let dev_ptr: *const u64 = &dev; 78 | 79 | unsafe { 80 | libc::sysctlbyname( 81 | name.as_ptr(), 82 | buf_ptr as *mut libc::c_void, 83 | &mut buf_size, 84 | dev_ptr as *const libc::c_void, 85 | dev_size, 86 | ); 87 | } 88 | 89 | let fmt_content = if let Ok(devname) = std::ffi::CStr::from_bytes_until_nul(&buf) { 90 | devname.to_string_lossy().into_owned() 91 | } else { 92 | String::from("") 93 | }; 94 | let raw_content = fmt_content.clone(); 95 | 96 | self.fmt_contents.insert(proc.pid, fmt_content); 97 | self.raw_contents.insert(proc.pid, raw_content); 98 | } 99 | 100 | column_default!(String, false); 101 | } 102 | -------------------------------------------------------------------------------- /src/columns/udp_port.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::Column; 3 | #[cfg(any(target_os = "linux", target_os = "android"))] 4 | use procfs::net::UdpNetEntry; 5 | #[cfg(any(target_os = "linux", target_os = "android"))] 6 | use procfs::process::FDTarget; 7 | use std::cmp; 8 | use std::collections::HashMap; 9 | 10 | pub struct UdpPort { 11 | header: String, 12 | unit: String, 13 | fmt_contents: HashMap, 14 | raw_contents: HashMap, 15 | width: usize, 16 | #[cfg(any(target_os = "linux", target_os = "android"))] 17 | udp_entry: Vec, 18 | #[cfg(any(target_os = "linux", target_os = "android"))] 19 | udp6_entry: Vec, 20 | } 21 | 22 | impl UdpPort { 23 | pub fn new(header: Option) -> Self { 24 | let header = header.unwrap_or_else(|| String::from("UDP")); 25 | let unit = String::new(); 26 | Self { 27 | fmt_contents: HashMap::new(), 28 | raw_contents: HashMap::new(), 29 | width: 0, 30 | header, 31 | unit, 32 | #[cfg(any(target_os = "linux", target_os = "android"))] 33 | udp_entry: procfs::net::udp().unwrap_or_default(), 34 | #[cfg(any(target_os = "linux", target_os = "android"))] 35 | udp6_entry: procfs::net::udp6().unwrap_or_default(), 36 | } 37 | } 38 | } 39 | 40 | #[cfg(any(target_os = "linux", target_os = "android"))] 41 | impl Column for UdpPort { 42 | fn add(&mut self, proc: &ProcessInfo) { 43 | let fmt_content = if let Ok(fds) = proc.curr_proc.fd() { 44 | let mut socks = Vec::new(); 45 | for fd in fds { 46 | if let FDTarget::Socket(x) = fd.target { 47 | socks.push(x) 48 | } 49 | } 50 | 51 | let mut ports = Vec::new(); 52 | for sock in &socks { 53 | let mut udp_iter = self.udp_entry.iter().chain(self.udp6_entry.iter()); 54 | let entry = udp_iter.find(|&x| x.inode == *sock); 55 | if let Some(entry) = entry { 56 | ports.push(entry.local_address.port()); 57 | } 58 | } 59 | ports.sort_unstable(); 60 | ports.dedup(); 61 | 62 | format!("{ports:?}") 63 | } else { 64 | String::new() 65 | }; 66 | let raw_content = fmt_content.clone(); 67 | 68 | self.fmt_contents.insert(proc.pid, fmt_content); 69 | self.raw_contents.insert(proc.pid, raw_content); 70 | } 71 | 72 | fn find_exact(&self, pid: i32, keyword: &str, _content_to_lowercase: bool) -> bool { 73 | if let Some(content) = self.fmt_contents.get(&pid) { 74 | let content = content.replace(['[', ']'], ""); 75 | let content = content.split(','); 76 | for c in content { 77 | if c == keyword { 78 | return true; 79 | } 80 | } 81 | false 82 | } else { 83 | false 84 | } 85 | } 86 | 87 | crate::column_default_display_header!(); 88 | crate::column_default_display_unit!(); 89 | crate::column_default_display_content!(); 90 | crate::column_default_display_json!(); 91 | crate::column_default_find_partial!(); 92 | crate::column_default_sorted_pid!(String); 93 | crate::column_default_apply_visible!(); 94 | crate::column_default_reset_width!(); 95 | crate::column_default_update_width!(); 96 | crate::column_default_get_width!(); 97 | crate::column_default_is_numeric!(false); 98 | } 99 | 100 | #[cfg(target_os = "macos")] 101 | impl Column for UdpPort { 102 | fn add(&mut self, proc: &ProcessInfo) { 103 | let mut ports = Vec::new(); 104 | for udp in &proc.curr_udps { 105 | let port = crate::util::change_endian(udp.insi_lport as u32) >> 16; 106 | if port != 0 { 107 | ports.push(port); 108 | } 109 | } 110 | ports.sort(); 111 | ports.dedup(); 112 | 113 | let fmt_content = format!("{:?}", ports); 114 | let raw_content = fmt_content.clone(); 115 | 116 | self.fmt_contents.insert(proc.pid, fmt_content); 117 | self.raw_contents.insert(proc.pid, raw_content); 118 | } 119 | 120 | fn find_exact(&self, pid: i32, keyword: &str, _content_to_lowercase: bool) -> bool { 121 | if let Some(content) = self.fmt_contents.get(&pid) { 122 | let content = content.replace(['[', ']'], ""); 123 | let content = content.split(','); 124 | for c in content { 125 | if c == keyword { 126 | return true; 127 | } 128 | } 129 | false 130 | } else { 131 | false 132 | } 133 | } 134 | 135 | crate::column_default_display_header!(); 136 | crate::column_default_display_unit!(); 137 | crate::column_default_display_content!(); 138 | crate::column_default_display_json!(); 139 | crate::column_default_find_partial!(); 140 | crate::column_default_sorted_pid!(String); 141 | crate::column_default_apply_visible!(); 142 | crate::column_default_reset_width!(); 143 | crate::column_default_update_width!(); 144 | crate::column_default_get_width!(); 145 | crate::column_default_is_numeric!(false); 146 | } 147 | -------------------------------------------------------------------------------- /src/columns/uid.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | #[cfg(target_os = "windows")] 3 | use crate::util::format_sid; 4 | use crate::{column_default, Column}; 5 | use std::cmp; 6 | use std::collections::HashMap; 7 | 8 | pub struct Uid { 9 | header: String, 10 | unit: String, 11 | fmt_contents: HashMap, 12 | raw_contents: HashMap, 13 | width: usize, 14 | #[allow(dead_code)] 15 | abbr_sid: bool, 16 | } 17 | 18 | impl Uid { 19 | pub fn new(header: Option, abbr_sid: bool) -> Self { 20 | let header = header.unwrap_or_else(|| String::from("UID")); 21 | let unit = String::new(); 22 | Self { 23 | fmt_contents: HashMap::new(), 24 | raw_contents: HashMap::new(), 25 | width: 0, 26 | header, 27 | unit, 28 | abbr_sid, 29 | } 30 | } 31 | } 32 | 33 | #[cfg(any(target_os = "linux", target_os = "android"))] 34 | impl Column for Uid { 35 | fn add(&mut self, proc: &ProcessInfo) { 36 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 37 | let uid = status.euid; 38 | (format!("{uid}"), uid) 39 | } else { 40 | (String::new(), 0) 41 | }; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u32, true); 48 | } 49 | 50 | #[cfg(target_os = "macos")] 51 | impl Column for Uid { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let uid = proc.curr_task.pbsd.pbi_uid; 54 | let fmt_content = format!("{}", uid); 55 | let raw_content = uid; 56 | 57 | self.fmt_contents.insert(proc.pid, fmt_content); 58 | self.raw_contents.insert(proc.pid, raw_content); 59 | } 60 | 61 | column_default!(u32, true); 62 | } 63 | 64 | #[cfg(target_os = "windows")] 65 | impl Column for Uid { 66 | fn add(&mut self, proc: &ProcessInfo) { 67 | let fmt_content = format_sid(&proc.user.sid, self.abbr_sid); 68 | let raw_content = proc.user.sid[proc.user.sid.len() - 1] as u32; 69 | 70 | self.fmt_contents.insert(proc.pid, fmt_content); 71 | self.raw_contents.insert(proc.pid, raw_content); 72 | } 73 | 74 | column_default!(u32, true); 75 | } 76 | 77 | #[cfg(target_os = "freebsd")] 78 | impl Column for Uid { 79 | fn add(&mut self, proc: &ProcessInfo) { 80 | let uid = proc.curr_proc.info.uid; 81 | let fmt_content = format!("{}", uid); 82 | let raw_content = uid; 83 | 84 | self.fmt_contents.insert(proc.pid, fmt_content); 85 | self.raw_contents.insert(proc.pid, raw_content); 86 | } 87 | 88 | column_default!(u32, true); 89 | } 90 | -------------------------------------------------------------------------------- /src/columns/uid_fs.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct UidFs { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl UidFs { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("FUID")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | impl Column for UidFs { 29 | fn add(&mut self, proc: &ProcessInfo) { 30 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 31 | let uid = status.fuid; 32 | (format!("{uid}"), uid) 33 | } else { 34 | (String::new(), 0) 35 | }; 36 | 37 | self.fmt_contents.insert(proc.pid, fmt_content); 38 | self.raw_contents.insert(proc.pid, raw_content); 39 | } 40 | 41 | column_default!(u32, true); 42 | } 43 | -------------------------------------------------------------------------------- /src/columns/uid_login.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | const UID_NOT_SET: u32 = 0xffffffff; 7 | 8 | pub struct UidLogin { 9 | header: String, 10 | unit: String, 11 | fmt_contents: HashMap, 12 | raw_contents: HashMap, 13 | width: usize, 14 | } 15 | 16 | impl UidLogin { 17 | pub fn new(header: Option) -> Self { 18 | let header = header.unwrap_or_else(|| String::from("LoginUID")); 19 | let unit = String::new(); 20 | Self { 21 | fmt_contents: HashMap::new(), 22 | raw_contents: HashMap::new(), 23 | width: 0, 24 | header, 25 | unit, 26 | } 27 | } 28 | } 29 | 30 | impl Column for UidLogin { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let (fmt_content, raw_content) = if let Ok(uid) = proc.curr_proc.loginuid() { 33 | if uid == UID_NOT_SET { 34 | (String::new(), uid) 35 | } else { 36 | (format!("{uid}"), uid) 37 | } 38 | } else { 39 | (String::new(), 0) 40 | }; 41 | 42 | self.fmt_contents.insert(proc.pid, fmt_content); 43 | self.raw_contents.insert(proc.pid, raw_content); 44 | } 45 | 46 | column_default!(u32, true); 47 | } 48 | -------------------------------------------------------------------------------- /src/columns/uid_real.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct UidReal { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl UidReal { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("RUID")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for UidReal { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 32 | let uid = status.ruid; 33 | (format!("{uid}"), uid) 34 | } else { 35 | (String::new(), 0) 36 | }; 37 | 38 | self.fmt_contents.insert(proc.pid, fmt_content); 39 | self.raw_contents.insert(proc.pid, raw_content); 40 | } 41 | 42 | column_default!(u32, true); 43 | } 44 | 45 | #[cfg(target_os = "macos")] 46 | impl Column for UidReal { 47 | fn add(&mut self, proc: &ProcessInfo) { 48 | let uid = proc.curr_task.pbsd.pbi_ruid; 49 | let fmt_content = format!("{}", uid); 50 | let raw_content = uid; 51 | 52 | self.fmt_contents.insert(proc.pid, fmt_content); 53 | self.raw_contents.insert(proc.pid, raw_content); 54 | } 55 | 56 | column_default!(u32, true); 57 | } 58 | 59 | #[cfg(target_os = "freebsd")] 60 | impl Column for UidReal { 61 | fn add(&mut self, proc: &ProcessInfo) { 62 | let uid = proc.curr_proc.info.ruid; 63 | let fmt_content = format!("{}", uid); 64 | let raw_content = uid; 65 | 66 | self.fmt_contents.insert(proc.pid, fmt_content); 67 | self.raw_contents.insert(proc.pid, raw_content); 68 | } 69 | 70 | column_default!(u32, true); 71 | } 72 | -------------------------------------------------------------------------------- /src/columns/uid_saved.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct UidSaved { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl UidSaved { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("SUID")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for UidSaved { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (fmt_content, raw_content) = if let Some(ref status) = proc.curr_status { 32 | let uid = status.suid; 33 | (format!("{uid}"), uid) 34 | } else { 35 | (String::new(), 0) 36 | }; 37 | 38 | self.fmt_contents.insert(proc.pid, fmt_content); 39 | self.raw_contents.insert(proc.pid, raw_content); 40 | } 41 | 42 | column_default!(u32, true); 43 | } 44 | 45 | #[cfg(target_os = "macos")] 46 | impl Column for UidSaved { 47 | fn add(&mut self, proc: &ProcessInfo) { 48 | let uid = proc.curr_task.pbsd.pbi_svuid; 49 | let fmt_content = format!("{}", uid); 50 | let raw_content = uid; 51 | 52 | self.fmt_contents.insert(proc.pid, fmt_content); 53 | self.raw_contents.insert(proc.pid, raw_content); 54 | } 55 | 56 | column_default!(u32, true); 57 | } 58 | 59 | #[cfg(target_os = "freebsd")] 60 | impl Column for UidSaved { 61 | fn add(&mut self, proc: &ProcessInfo) { 62 | let uid = proc.curr_proc.info.svuid; 63 | let fmt_content = format!("{}", uid); 64 | let raw_content = uid; 65 | 66 | self.fmt_contents.insert(proc.pid, fmt_content); 67 | self.raw_contents.insert(proc.pid, raw_content); 68 | } 69 | 70 | column_default!(u32, true); 71 | } 72 | -------------------------------------------------------------------------------- /src/columns/usage_cpu.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct UsageCpu { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl UsageCpu { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("CPU")); 17 | let unit = String::from("[%]"); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for UsageCpu { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let curr_stat = proc.curr_proc.stat(); 32 | let prev_stat = &proc.prev_stat; 33 | 34 | let curr_time = curr_stat.utime + curr_stat.stime; 35 | let prev_time = prev_stat.utime + prev_stat.stime; 36 | let usage_ms = (curr_time - prev_time) * 1000 / procfs::ticks_per_second(); 37 | let interval_ms = proc.interval.as_secs() * 1000 + u64::from(proc.interval.subsec_millis()); 38 | let usage = usage_ms as f64 * 100.0 / interval_ms as f64; 39 | 40 | let fmt_content = format!("{usage:.1}"); 41 | let raw_content = (usage * 1000.0) as u32; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u32, true); 48 | } 49 | 50 | #[cfg(target_os = "macos")] 51 | impl Column for UsageCpu { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let curr_time = 54 | proc.curr_task.ptinfo.pti_total_user + proc.curr_task.ptinfo.pti_total_system; 55 | let prev_time = 56 | proc.prev_task.ptinfo.pti_total_user + proc.prev_task.ptinfo.pti_total_system; 57 | let usage_ms = (curr_time - prev_time) / 1000000u64; 58 | let interval_ms = proc.interval.as_secs() * 1000 + u64::from(proc.interval.subsec_millis()); 59 | let usage = usage_ms as f64 * 100.0 / interval_ms as f64; 60 | 61 | let fmt_content = format!("{:.1}", usage); 62 | let raw_content = (usage * 1000.0) as u32; 63 | 64 | self.fmt_contents.insert(proc.pid, fmt_content); 65 | self.raw_contents.insert(proc.pid, raw_content); 66 | } 67 | 68 | column_default!(u32, true); 69 | } 70 | 71 | #[cfg(target_os = "windows")] 72 | impl Column for UsageCpu { 73 | fn add(&mut self, proc: &ProcessInfo) { 74 | let curr_time = proc.cpu_info.curr_sys + proc.cpu_info.curr_user; 75 | let prev_time = proc.cpu_info.prev_sys + proc.cpu_info.prev_user; 76 | 77 | let usage_ms = (curr_time - prev_time) / 10000u64; 78 | let interval_ms = proc.interval.as_secs() * 1000 + u64::from(proc.interval.subsec_millis()); 79 | let usage = usage_ms as f64 * 100.0 / interval_ms as f64; 80 | 81 | let fmt_content = format!("{:.1}", usage); 82 | let raw_content = (usage * 1000.0) as u32; 83 | 84 | self.fmt_contents.insert(proc.pid, fmt_content); 85 | self.raw_contents.insert(proc.pid, raw_content); 86 | } 87 | 88 | column_default!(u32, true); 89 | } 90 | 91 | #[cfg(target_os = "freebsd")] 92 | impl Column for UsageCpu { 93 | fn add(&mut self, proc: &ProcessInfo) { 94 | let curr_time = (proc.curr_proc.info.rusage.utime.to_us() 95 | + proc.curr_proc.info.rusage.stime.to_us()) as u64; 96 | let prev_time = (proc.prev_proc.info.rusage.utime.to_us() 97 | + proc.prev_proc.info.rusage.stime.to_us()) as u64; 98 | let usage_ms = (curr_time - prev_time) / 1_000u64; 99 | let interval_ms = proc.interval.as_secs() * 1000 + u64::from(proc.interval.subsec_millis()); 100 | let usage = usage_ms as f64 * 100.0 / interval_ms as f64; 101 | 102 | let fmt_content = format!("{:.1}", usage); 103 | let raw_content = (usage * 1000.0) as u32; 104 | 105 | self.fmt_contents.insert(proc.pid, fmt_content); 106 | self.raw_contents.insert(proc.pid, raw_content); 107 | } 108 | 109 | column_default!(u32, true); 110 | } 111 | -------------------------------------------------------------------------------- /src/columns/usage_mem.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | #[cfg(any(target_os = "linux", target_os = "android"))] 4 | use procfs::{Current, Meminfo, WithCurrentSystemInfo}; 5 | use std::cmp; 6 | use std::collections::HashMap; 7 | #[cfg(target_os = "windows")] 8 | use std::mem::{size_of, zeroed}; 9 | #[cfg(target_os = "windows")] 10 | use windows_sys::Win32::System::ProcessStatus::{GetPerformanceInfo, PERFORMANCE_INFORMATION}; 11 | 12 | pub struct UsageMem { 13 | header: String, 14 | unit: String, 15 | fmt_contents: HashMap, 16 | raw_contents: HashMap, 17 | width: usize, 18 | mem_total: u64, 19 | } 20 | 21 | impl UsageMem { 22 | pub fn new(header: Option) -> Self { 23 | let header = header.unwrap_or_else(|| String::from("MEM")); 24 | let unit = String::from("[%]"); 25 | 26 | Self { 27 | fmt_contents: HashMap::new(), 28 | raw_contents: HashMap::new(), 29 | width: 0, 30 | header, 31 | unit, 32 | mem_total: get_mem_total(), 33 | } 34 | } 35 | } 36 | 37 | #[cfg(any(target_os = "linux", target_os = "android"))] 38 | fn get_mem_total() -> u64 { 39 | let meminfo = Meminfo::current(); 40 | if let Ok(meminfo) = meminfo { 41 | meminfo.mem_total 42 | } else { 43 | 0 44 | } 45 | } 46 | 47 | #[cfg(target_os = "macos")] 48 | fn get_mem_total() -> u64 { 49 | let mut mem_total: u64 = 0; 50 | let mut mib = [0, 0]; 51 | 52 | unsafe { 53 | crate::util::get_sys_value( 54 | libc::CTL_HW as u32, 55 | libc::HW_MEMSIZE as u32, 56 | std::mem::size_of::(), 57 | &mut mem_total as *mut u64 as *mut libc::c_void, 58 | &mut mib, 59 | ); 60 | } 61 | 62 | mem_total 63 | } 64 | 65 | #[cfg(target_os = "windows")] 66 | fn get_mem_total() -> u64 { 67 | let mut info: PERFORMANCE_INFORMATION = unsafe { zeroed() }; 68 | let ret = unsafe { GetPerformanceInfo(&mut info, size_of::() as u32) }; 69 | 70 | if ret != 0 { 71 | info.PhysicalTotal as u64 * info.PageSize as u64 72 | } else { 73 | 0 74 | } 75 | } 76 | 77 | #[cfg(target_os = "freebsd")] 78 | fn get_mem_total() -> u64 { 79 | let mut mem_total: u64 = 0; 80 | let name = std::ffi::CString::new("hw.availpages").unwrap(); 81 | let mut size = std::mem::size_of::(); 82 | let ptr: *mut u64 = &mut mem_total; 83 | 84 | unsafe { 85 | libc::sysctlbyname( 86 | name.as_ptr(), 87 | ptr as *mut libc::c_void, 88 | &mut size, 89 | std::ptr::null(), 90 | 0, 91 | ); 92 | } 93 | 94 | mem_total 95 | } 96 | 97 | #[cfg(any(target_os = "linux", target_os = "android"))] 98 | impl Column for UsageMem { 99 | fn add(&mut self, proc: &ProcessInfo) { 100 | let usage = proc.curr_proc.stat().rss_bytes().get() as f64 * 100.0 / self.mem_total as f64; 101 | let fmt_content = format!("{usage:.1}"); 102 | let raw_content = (usage * 1000.0) as u32; 103 | 104 | self.fmt_contents.insert(proc.pid, fmt_content); 105 | self.raw_contents.insert(proc.pid, raw_content); 106 | } 107 | 108 | column_default!(u32, true); 109 | } 110 | 111 | #[cfg(target_os = "macos")] 112 | impl Column for UsageMem { 113 | fn add(&mut self, proc: &ProcessInfo) { 114 | let usage = proc.curr_task.ptinfo.pti_resident_size as f64 * 100.0 / self.mem_total as f64; 115 | let fmt_content = format!("{:.1}", usage); 116 | let raw_content = (usage * 1000.0) as u32; 117 | 118 | self.fmt_contents.insert(proc.pid, fmt_content); 119 | self.raw_contents.insert(proc.pid, raw_content); 120 | } 121 | 122 | column_default!(u32, true); 123 | } 124 | 125 | #[cfg(target_os = "windows")] 126 | impl Column for UsageMem { 127 | fn add(&mut self, proc: &ProcessInfo) { 128 | let usage = proc.memory_info.working_set_size as f64 * 100.0 / self.mem_total as f64; 129 | let fmt_content = format!("{:.1}", usage); 130 | let raw_content = (usage * 1000.0) as u32; 131 | 132 | self.fmt_contents.insert(proc.pid, fmt_content); 133 | self.raw_contents.insert(proc.pid, raw_content); 134 | } 135 | 136 | column_default!(u32, true); 137 | } 138 | 139 | #[cfg(target_os = "freebsd")] 140 | impl Column for UsageMem { 141 | fn add(&mut self, proc: &ProcessInfo) { 142 | let usage = proc.curr_proc.info.rssize as f64 * 100.0 / self.mem_total as f64; 143 | let fmt_content = format!("{:.1}", usage); 144 | let raw_content = (usage * 1000.0) as u32; 145 | 146 | self.fmt_contents.insert(proc.pid, fmt_content); 147 | self.raw_contents.insert(proc.pid, raw_content); 148 | } 149 | 150 | column_default!(u32, true); 151 | } 152 | -------------------------------------------------------------------------------- /src/columns/user.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | #[cfg(target_os = "windows")] 3 | use crate::util::format_sid; 4 | #[cfg(not(target_os = "windows"))] 5 | use crate::util::USERS_CACHE; 6 | use crate::{column_default, Column}; 7 | use std::cmp; 8 | use std::collections::HashMap; 9 | #[cfg(not(target_os = "windows"))] 10 | use uzers::Users; 11 | 12 | pub struct User { 13 | header: String, 14 | unit: String, 15 | fmt_contents: HashMap, 16 | raw_contents: HashMap, 17 | width: usize, 18 | #[allow(dead_code)] 19 | abbr_sid: bool, 20 | } 21 | 22 | impl User { 23 | pub fn new(header: Option, abbr_sid: bool) -> Self { 24 | let header = header.unwrap_or_else(|| String::from("User")); 25 | let unit = String::new(); 26 | Self { 27 | fmt_contents: HashMap::new(), 28 | raw_contents: HashMap::new(), 29 | width: 0, 30 | header, 31 | unit, 32 | abbr_sid, 33 | } 34 | } 35 | } 36 | 37 | #[cfg(any(target_os = "linux", target_os = "android"))] 38 | impl Column for User { 39 | fn add(&mut self, proc: &ProcessInfo) { 40 | let user = USERS_CACHE.with(|x| x.borrow_mut().get_user_by_uid(proc.curr_proc.owner())); 41 | let fmt_content = if let Some(user) = user { 42 | format!("{}", user.name().to_string_lossy()) 43 | } else { 44 | format!("{}", proc.curr_proc.owner()) 45 | }; 46 | let raw_content = fmt_content.clone(); 47 | 48 | self.fmt_contents.insert(proc.pid, fmt_content); 49 | self.raw_contents.insert(proc.pid, raw_content); 50 | } 51 | 52 | column_default!(String, false); 53 | } 54 | 55 | #[cfg(target_os = "macos")] 56 | impl Column for User { 57 | fn add(&mut self, proc: &ProcessInfo) { 58 | let uid = proc.curr_task.pbsd.pbi_uid; 59 | let fmt_content = 60 | if let Some(user) = USERS_CACHE.with(|x| x.borrow_mut().get_user_by_uid(uid)) { 61 | format!("{}", user.name().to_string_lossy()) 62 | } else { 63 | format!("{}", uid) 64 | }; 65 | let raw_content = fmt_content.clone(); 66 | 67 | self.fmt_contents.insert(proc.pid, fmt_content); 68 | self.raw_contents.insert(proc.pid, raw_content); 69 | } 70 | 71 | column_default!(String, false); 72 | } 73 | 74 | #[cfg(target_os = "windows")] 75 | impl Column for User { 76 | fn add(&mut self, proc: &ProcessInfo) { 77 | let fmt_content = if let Some(name) = &proc.user.name { 78 | name.clone() 79 | } else { 80 | format_sid(&proc.user.sid, self.abbr_sid) 81 | }; 82 | let raw_content = fmt_content.clone(); 83 | 84 | self.fmt_contents.insert(proc.pid, fmt_content); 85 | self.raw_contents.insert(proc.pid, raw_content); 86 | } 87 | 88 | column_default!(String, false); 89 | } 90 | 91 | #[cfg(target_os = "freebsd")] 92 | impl Column for User { 93 | fn add(&mut self, proc: &ProcessInfo) { 94 | let uid = proc.curr_proc.info.uid; 95 | let fmt_content = 96 | if let Some(user) = USERS_CACHE.with(|x| x.borrow_mut().get_user_by_uid(uid)) { 97 | format!("{}", user.name().to_string_lossy()) 98 | } else { 99 | format!("{}", uid) 100 | }; 101 | let raw_content = fmt_content.clone(); 102 | 103 | self.fmt_contents.insert(proc.pid, fmt_content); 104 | self.raw_contents.insert(proc.pid, raw_content); 105 | } 106 | 107 | column_default!(String, false); 108 | } 109 | -------------------------------------------------------------------------------- /src/columns/user_fs.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::USERS_CACHE; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | use uzers::Users; 7 | 8 | pub struct UserFs { 9 | header: String, 10 | unit: String, 11 | fmt_contents: HashMap, 12 | raw_contents: HashMap, 13 | width: usize, 14 | } 15 | 16 | impl UserFs { 17 | pub fn new(header: Option) -> Self { 18 | let header = header.unwrap_or_else(|| String::from("File System User")); 19 | let unit = String::new(); 20 | Self { 21 | fmt_contents: HashMap::new(), 22 | raw_contents: HashMap::new(), 23 | width: 0, 24 | header, 25 | unit, 26 | } 27 | } 28 | } 29 | 30 | impl Column for UserFs { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let fmt_content = if let Some(ref status) = proc.curr_status { 33 | let uid = status.fuid; 34 | if let Some(user) = USERS_CACHE.with(|x| x.borrow_mut().get_user_by_uid(uid)) { 35 | format!("{}", user.name().to_string_lossy()) 36 | } else { 37 | format!("{uid}") 38 | } 39 | } else { 40 | String::new() 41 | }; 42 | let raw_content = fmt_content.clone(); 43 | 44 | self.fmt_contents.insert(proc.pid, fmt_content); 45 | self.raw_contents.insert(proc.pid, raw_content); 46 | } 47 | 48 | column_default!(String, false); 49 | } 50 | -------------------------------------------------------------------------------- /src/columns/user_login.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::USERS_CACHE; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | use uzers::Users; 7 | 8 | const UID_NOT_SET: u32 = 0xffffffff; 9 | 10 | pub struct UserLogin { 11 | header: String, 12 | unit: String, 13 | fmt_contents: HashMap, 14 | raw_contents: HashMap, 15 | width: usize, 16 | } 17 | 18 | impl UserLogin { 19 | pub fn new(header: Option) -> Self { 20 | let header = header.unwrap_or_else(|| String::from("Login User")); 21 | let unit = String::new(); 22 | Self { 23 | fmt_contents: HashMap::new(), 24 | raw_contents: HashMap::new(), 25 | width: 0, 26 | header, 27 | unit, 28 | } 29 | } 30 | } 31 | 32 | impl Column for UserLogin { 33 | fn add(&mut self, proc: &ProcessInfo) { 34 | let fmt_content = if let Ok(uid) = proc.curr_proc.loginuid() { 35 | if let Some(user) = USERS_CACHE.with(|x| x.borrow_mut().get_user_by_uid(uid)) { 36 | format!("{}", user.name().to_string_lossy()) 37 | } else if uid == UID_NOT_SET { 38 | String::new() 39 | } else { 40 | format!("{uid}") 41 | } 42 | } else { 43 | String::new() 44 | }; 45 | let raw_content = fmt_content.clone(); 46 | 47 | self.fmt_contents.insert(proc.pid, fmt_content); 48 | self.raw_contents.insert(proc.pid, raw_content); 49 | } 50 | 51 | column_default!(String, false); 52 | } 53 | -------------------------------------------------------------------------------- /src/columns/user_real.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::USERS_CACHE; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | use uzers::Users; 7 | 8 | pub struct UserReal { 9 | header: String, 10 | unit: String, 11 | fmt_contents: HashMap, 12 | raw_contents: HashMap, 13 | width: usize, 14 | } 15 | 16 | impl UserReal { 17 | pub fn new(header: Option) -> Self { 18 | let header = header.unwrap_or_else(|| String::from("Real User")); 19 | let unit = String::new(); 20 | Self { 21 | fmt_contents: HashMap::new(), 22 | raw_contents: HashMap::new(), 23 | width: 0, 24 | header, 25 | unit, 26 | } 27 | } 28 | } 29 | 30 | #[cfg(any(target_os = "linux", target_os = "android"))] 31 | impl Column for UserReal { 32 | fn add(&mut self, proc: &ProcessInfo) { 33 | let fmt_content = if let Some(ref status) = proc.curr_status { 34 | let uid = status.ruid; 35 | if let Some(user) = USERS_CACHE.with(|x| x.borrow_mut().get_user_by_uid(uid)) { 36 | format!("{}", user.name().to_string_lossy()) 37 | } else { 38 | format!("{uid}") 39 | } 40 | } else { 41 | String::new() 42 | }; 43 | let raw_content = fmt_content.clone(); 44 | 45 | self.fmt_contents.insert(proc.pid, fmt_content); 46 | self.raw_contents.insert(proc.pid, raw_content); 47 | } 48 | 49 | column_default!(String, false); 50 | } 51 | 52 | #[cfg(target_os = "macos")] 53 | impl Column for UserReal { 54 | fn add(&mut self, proc: &ProcessInfo) { 55 | let uid = proc.curr_task.pbsd.pbi_ruid; 56 | let fmt_content = 57 | if let Some(user) = USERS_CACHE.with(|x| x.borrow_mut().get_user_by_uid(uid)) { 58 | format!("{}", user.name().to_string_lossy()) 59 | } else { 60 | format!("{}", uid) 61 | }; 62 | let raw_content = fmt_content.clone(); 63 | 64 | self.fmt_contents.insert(proc.pid, fmt_content); 65 | self.raw_contents.insert(proc.pid, raw_content); 66 | } 67 | 68 | column_default!(String, false); 69 | } 70 | 71 | #[cfg(target_os = "freebsd")] 72 | impl Column for UserReal { 73 | fn add(&mut self, proc: &ProcessInfo) { 74 | let uid = proc.curr_proc.info.ruid; 75 | let fmt_content = 76 | if let Some(user) = USERS_CACHE.with(|x| x.borrow_mut().get_user_by_uid(uid)) { 77 | format!("{}", user.name().to_string_lossy()) 78 | } else { 79 | format!("{}", uid) 80 | }; 81 | let raw_content = fmt_content.clone(); 82 | 83 | self.fmt_contents.insert(proc.pid, fmt_content); 84 | self.raw_contents.insert(proc.pid, raw_content); 85 | } 86 | 87 | column_default!(String, false); 88 | } 89 | -------------------------------------------------------------------------------- /src/columns/user_saved.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::USERS_CACHE; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | use uzers::Users; 7 | 8 | pub struct UserSaved { 9 | header: String, 10 | unit: String, 11 | fmt_contents: HashMap, 12 | raw_contents: HashMap, 13 | width: usize, 14 | } 15 | 16 | impl UserSaved { 17 | pub fn new(header: Option) -> Self { 18 | let header = header.unwrap_or_else(|| String::from("Saved User")); 19 | let unit = String::new(); 20 | Self { 21 | fmt_contents: HashMap::new(), 22 | raw_contents: HashMap::new(), 23 | width: 0, 24 | header, 25 | unit, 26 | } 27 | } 28 | } 29 | 30 | #[cfg(any(target_os = "linux", target_os = "android"))] 31 | impl Column for UserSaved { 32 | fn add(&mut self, proc: &ProcessInfo) { 33 | let fmt_content = if let Some(ref status) = proc.curr_status { 34 | let uid = status.suid; 35 | if let Some(user) = USERS_CACHE.with(|x| x.borrow_mut().get_user_by_uid(uid)) { 36 | format!("{}", user.name().to_string_lossy()) 37 | } else { 38 | format!("{uid}") 39 | } 40 | } else { 41 | String::new() 42 | }; 43 | let raw_content = fmt_content.clone(); 44 | 45 | self.fmt_contents.insert(proc.pid, fmt_content); 46 | self.raw_contents.insert(proc.pid, raw_content); 47 | } 48 | 49 | column_default!(String, false); 50 | } 51 | 52 | #[cfg(target_os = "macos")] 53 | impl Column for UserSaved { 54 | fn add(&mut self, proc: &ProcessInfo) { 55 | let uid = proc.curr_task.pbsd.pbi_svuid; 56 | let fmt_content = 57 | if let Some(user) = USERS_CACHE.with(|x| x.borrow_mut().get_user_by_uid(uid)) { 58 | format!("{}", user.name().to_string_lossy()) 59 | } else { 60 | format!("{}", uid) 61 | }; 62 | let raw_content = fmt_content.clone(); 63 | 64 | self.fmt_contents.insert(proc.pid, fmt_content); 65 | self.raw_contents.insert(proc.pid, raw_content); 66 | } 67 | 68 | column_default!(String, false); 69 | } 70 | 71 | #[cfg(target_os = "freebsd")] 72 | impl Column for UserSaved { 73 | fn add(&mut self, proc: &ProcessInfo) { 74 | let uid = proc.curr_proc.info.svuid; 75 | let fmt_content = 76 | if let Some(user) = USERS_CACHE.with(|x| x.borrow_mut().get_user_by_uid(uid)) { 77 | format!("{}", user.name().to_string_lossy()) 78 | } else { 79 | format!("{}", uid) 80 | }; 81 | let raw_content = fmt_content.clone(); 82 | 83 | self.fmt_contents.insert(proc.pid, fmt_content); 84 | self.raw_contents.insert(proc.pid, raw_content); 85 | } 86 | 87 | column_default!(String, false); 88 | } 89 | -------------------------------------------------------------------------------- /src/columns/vm_data.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmData { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmData { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmData")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for VmData { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let (raw_content, fmt_content) = if let Some(ref curr_status) = proc.curr_status { 33 | if let Some(val) = curr_status.vmdata { 34 | let val = val.saturating_mul(1024); 35 | (val, bytify(val)) 36 | } else { 37 | (0, String::new()) 38 | } 39 | } else { 40 | (0, String::new()) 41 | }; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u64, true); 48 | } 49 | 50 | #[cfg(target_os = "freebsd")] 51 | impl Column for VmData { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let raw_content = (proc.curr_proc.info.dsize as u64).saturating_mul(4096); 54 | let fmt_content = bytify(raw_content); 55 | 56 | self.fmt_contents.insert(proc.pid, fmt_content); 57 | self.raw_contents.insert(proc.pid, raw_content); 58 | } 59 | 60 | column_default!(u64, true); 61 | } 62 | -------------------------------------------------------------------------------- /src/columns/vm_exe.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmExe { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmExe { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmExe")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for VmExe { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let (raw_content, fmt_content) = if let Some(ref curr_status) = proc.curr_status { 33 | if let Some(val) = curr_status.vmexe { 34 | let val = val.saturating_mul(1024); 35 | (val, bytify(val)) 36 | } else { 37 | (0, String::new()) 38 | } 39 | } else { 40 | (0, String::new()) 41 | }; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u64, true); 48 | } 49 | 50 | #[cfg(target_os = "freebsd")] 51 | impl Column for VmExe { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let raw_content = (proc.curr_proc.info.tsize as u64).saturating_mul(4096); 54 | let fmt_content = bytify(raw_content); 55 | 56 | self.fmt_contents.insert(proc.pid, fmt_content); 57 | self.raw_contents.insert(proc.pid, raw_content); 58 | } 59 | 60 | column_default!(u64, true); 61 | } 62 | -------------------------------------------------------------------------------- /src/columns/vm_hwm.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmHwm { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmHwm { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmHwm")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for VmHwm { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let (raw_content, fmt_content) = if let Some(ref curr_status) = proc.curr_status { 33 | if let Some(val) = curr_status.vmhwm { 34 | let val = val.saturating_mul(1024); 35 | (val, bytify(val)) 36 | } else { 37 | (0, String::new()) 38 | } 39 | } else { 40 | (0, String::new()) 41 | }; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u64, true); 48 | } 49 | 50 | #[cfg(target_os = "windows")] 51 | impl Column for VmHwm { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let raw_content = proc.memory_info.peak_working_set_size; 54 | let fmt_content = bytify(raw_content); 55 | 56 | self.fmt_contents.insert(proc.pid, fmt_content); 57 | self.raw_contents.insert(proc.pid, raw_content); 58 | } 59 | 60 | column_default!(u64, true); 61 | } 62 | 63 | #[cfg(target_os = "freebsd")] 64 | impl Column for VmHwm { 65 | fn add(&mut self, proc: &ProcessInfo) { 66 | let raw_content = (proc.curr_proc.info.rusage.maxrss as u64).saturating_mul(4096); 67 | let fmt_content = bytify(raw_content); 68 | 69 | self.fmt_contents.insert(proc.pid, fmt_content); 70 | self.raw_contents.insert(proc.pid, raw_content); 71 | } 72 | 73 | column_default!(u64, true); 74 | } 75 | -------------------------------------------------------------------------------- /src/columns/vm_lib.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmLib { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmLib { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmLib")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | impl Column for VmLib { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (raw_content, fmt_content) = if let Some(ref curr_status) = proc.curr_status { 32 | if let Some(val) = curr_status.vmlib { 33 | let val = val.saturating_mul(1024); 34 | (val, bytify(val)) 35 | } else { 36 | (0, String::new()) 37 | } 38 | } else { 39 | (0, String::new()) 40 | }; 41 | 42 | self.fmt_contents.insert(proc.pid, fmt_content); 43 | self.raw_contents.insert(proc.pid, raw_content); 44 | } 45 | 46 | column_default!(u64, true); 47 | } 48 | -------------------------------------------------------------------------------- /src/columns/vm_lock.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmLock { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmLock { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmLock")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | impl Column for VmLock { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (raw_content, fmt_content) = if let Some(ref curr_status) = proc.curr_status { 32 | if let Some(val) = curr_status.vmlck { 33 | let val = val.saturating_mul(1024); 34 | (val, bytify(val)) 35 | } else { 36 | (0, String::new()) 37 | } 38 | } else { 39 | (0, String::new()) 40 | }; 41 | 42 | self.fmt_contents.insert(proc.pid, fmt_content); 43 | self.raw_contents.insert(proc.pid, raw_content); 44 | } 45 | 46 | column_default!(u64, true); 47 | } 48 | -------------------------------------------------------------------------------- /src/columns/vm_peak.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmPeak { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmPeak { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmPeak")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for VmPeak { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let (raw_content, fmt_content) = if let Some(ref curr_status) = proc.curr_status { 33 | if let Some(val) = curr_status.vmpeak { 34 | let val = val.saturating_mul(1024); 35 | (val, bytify(val)) 36 | } else { 37 | (0, String::new()) 38 | } 39 | } else { 40 | (0, String::new()) 41 | }; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u64, true); 48 | } 49 | 50 | #[cfg(target_os = "windows")] 51 | impl Column for VmPeak { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let raw_content = proc.memory_info.peak_page_file_usage; 54 | let fmt_content = bytify(raw_content); 55 | 56 | self.fmt_contents.insert(proc.pid, fmt_content); 57 | self.raw_contents.insert(proc.pid, raw_content); 58 | } 59 | 60 | column_default!(u64, true); 61 | } 62 | -------------------------------------------------------------------------------- /src/columns/vm_pin.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmPin { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmPin { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmPin")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for VmPin { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let (raw_content, fmt_content) = if let Some(ref curr_status) = proc.curr_status { 33 | if let Some(val) = curr_status.vmpin { 34 | let val = val.saturating_mul(1024); 35 | (val, bytify(val)) 36 | } else { 37 | (0, String::new()) 38 | } 39 | } else { 40 | (0, String::new()) 41 | }; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u64, true); 48 | } 49 | 50 | #[cfg(target_os = "windows")] 51 | impl Column for VmPin { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let raw_content = proc.memory_info.quota_non_paged_pool_usage; 54 | let fmt_content = bytify(raw_content); 55 | 56 | self.fmt_contents.insert(proc.pid, fmt_content); 57 | self.raw_contents.insert(proc.pid, raw_content); 58 | } 59 | 60 | column_default!(u64, true); 61 | } 62 | -------------------------------------------------------------------------------- /src/columns/vm_pte.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmPte { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmPte { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmPte")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | impl Column for VmPte { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let (raw_content, fmt_content) = if let Some(ref curr_status) = proc.curr_status { 32 | if let Some(val) = curr_status.vmpte { 33 | let val = val.saturating_mul(1024); 34 | (val, bytify(val)) 35 | } else { 36 | (0, String::new()) 37 | } 38 | } else { 39 | (0, String::new()) 40 | }; 41 | 42 | self.fmt_contents.insert(proc.pid, fmt_content); 43 | self.raw_contents.insert(proc.pid, raw_content); 44 | } 45 | 46 | column_default!(u64, true); 47 | } 48 | -------------------------------------------------------------------------------- /src/columns/vm_rss.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmRss { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmRss { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmRSS")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for VmRss { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | use procfs::WithCurrentSystemInfo; 33 | let raw_content = proc.curr_proc.stat().rss_bytes().get(); 34 | let fmt_content = bytify(raw_content); 35 | 36 | self.fmt_contents.insert(proc.pid, fmt_content); 37 | self.raw_contents.insert(proc.pid, raw_content); 38 | } 39 | 40 | column_default!(u64, true); 41 | } 42 | 43 | #[cfg(target_os = "macos")] 44 | impl Column for VmRss { 45 | fn add(&mut self, proc: &ProcessInfo) { 46 | let raw_content = proc.curr_task.ptinfo.pti_resident_size; 47 | let fmt_content = bytify(raw_content); 48 | 49 | self.fmt_contents.insert(proc.pid, fmt_content); 50 | self.raw_contents.insert(proc.pid, raw_content); 51 | } 52 | 53 | column_default!(u64, true); 54 | } 55 | 56 | #[cfg(target_os = "windows")] 57 | impl Column for VmRss { 58 | fn add(&mut self, proc: &ProcessInfo) { 59 | let raw_content = proc.memory_info.working_set_size; 60 | let fmt_content = bytify(raw_content); 61 | 62 | self.fmt_contents.insert(proc.pid, fmt_content); 63 | self.raw_contents.insert(proc.pid, raw_content); 64 | } 65 | 66 | column_default!(u64, true); 67 | } 68 | 69 | #[cfg(target_os = "freebsd")] 70 | impl Column for VmRss { 71 | fn add(&mut self, proc: &ProcessInfo) { 72 | let raw_content = (proc.curr_proc.info.rssize as u64).saturating_mul(4096); 73 | let fmt_content = bytify(raw_content); 74 | 75 | self.fmt_contents.insert(proc.pid, fmt_content); 76 | self.raw_contents.insert(proc.pid, raw_content); 77 | } 78 | 79 | column_default!(u64, true); 80 | } 81 | -------------------------------------------------------------------------------- /src/columns/vm_size.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmSize { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmSize { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmSize")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for VmSize { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let raw_content = proc.curr_proc.stat().vsize; 33 | let fmt_content = bytify(raw_content); 34 | 35 | self.fmt_contents.insert(proc.pid, fmt_content); 36 | self.raw_contents.insert(proc.pid, raw_content); 37 | } 38 | 39 | column_default!(u64, true); 40 | } 41 | 42 | #[cfg(target_os = "macos")] 43 | impl Column for VmSize { 44 | fn add(&mut self, proc: &ProcessInfo) { 45 | let raw_content = proc.curr_task.ptinfo.pti_virtual_size; 46 | let fmt_content = bytify(raw_content); 47 | 48 | self.fmt_contents.insert(proc.pid, fmt_content); 49 | self.raw_contents.insert(proc.pid, raw_content); 50 | } 51 | 52 | column_default!(u64, true); 53 | } 54 | 55 | #[cfg(target_os = "windows")] 56 | impl Column for VmSize { 57 | fn add(&mut self, proc: &ProcessInfo) { 58 | let raw_content = proc.memory_info.private_usage; 59 | let fmt_content = bytify(raw_content); 60 | 61 | self.fmt_contents.insert(proc.pid, fmt_content); 62 | self.raw_contents.insert(proc.pid, raw_content); 63 | } 64 | 65 | column_default!(u64, true); 66 | } 67 | 68 | #[cfg(target_os = "freebsd")] 69 | impl Column for VmSize { 70 | fn add(&mut self, proc: &ProcessInfo) { 71 | let raw_content = proc.curr_proc.info.size as u64; 72 | let fmt_content = bytify(raw_content); 73 | 74 | self.fmt_contents.insert(proc.pid, fmt_content); 75 | self.raw_contents.insert(proc.pid, raw_content); 76 | } 77 | 78 | column_default!(u64, true); 79 | } 80 | -------------------------------------------------------------------------------- /src/columns/vm_stack.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmStack { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmStack { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmStack")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for VmStack { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let (raw_content, fmt_content) = if let Some(ref curr_status) = proc.curr_status { 33 | if let Some(val) = curr_status.vmstk { 34 | let val = val.saturating_mul(1024); 35 | (val, bytify(val)) 36 | } else { 37 | (0, String::new()) 38 | } 39 | } else { 40 | (0, String::new()) 41 | }; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u64, true); 48 | } 49 | 50 | #[cfg(target_os = "freebsd")] 51 | impl Column for VmStack { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let raw_content = (proc.curr_proc.info.ssize as u64).saturating_mul(4096); 54 | let fmt_content = bytify(raw_content); 55 | 56 | self.fmt_contents.insert(proc.pid, fmt_content); 57 | self.raw_contents.insert(proc.pid, raw_content); 58 | } 59 | 60 | column_default!(u64, true); 61 | } 62 | -------------------------------------------------------------------------------- /src/columns/vm_swap.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct VmSwap { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl VmSwap { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("VmSwap")); 18 | let unit = String::from("[bytes]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for VmSwap { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let (raw_content, fmt_content) = if let Some(ref curr_status) = proc.curr_status { 33 | if let Some(val) = curr_status.vmswap { 34 | let val = val.saturating_mul(1024); 35 | (val, bytify(val)) 36 | } else { 37 | (0, String::new()) 38 | } 39 | } else { 40 | (0, String::new()) 41 | }; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u64, true); 48 | } 49 | 50 | #[cfg(target_os = "windows")] 51 | impl Column for VmSwap { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let raw_content = proc.memory_info.quota_paged_pool_usage; 54 | let fmt_content = bytify(raw_content); 55 | 56 | self.fmt_contents.insert(proc.pid, fmt_content); 57 | self.raw_contents.insert(proc.pid, raw_content); 58 | } 59 | 60 | column_default!(u64, true); 61 | } 62 | -------------------------------------------------------------------------------- /src/columns/wchan.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | 6 | pub struct Wchan { 7 | header: String, 8 | unit: String, 9 | fmt_contents: HashMap, 10 | raw_contents: HashMap, 11 | width: usize, 12 | } 13 | 14 | impl Wchan { 15 | pub fn new(header: Option) -> Self { 16 | let header = header.unwrap_or_else(|| String::from("Wchan")); 17 | let unit = String::new(); 18 | Self { 19 | fmt_contents: HashMap::new(), 20 | raw_contents: HashMap::new(), 21 | width: 0, 22 | header, 23 | unit, 24 | } 25 | } 26 | } 27 | 28 | #[cfg(any(target_os = "linux", target_os = "android"))] 29 | impl Column for Wchan { 30 | fn add(&mut self, proc: &ProcessInfo) { 31 | let raw_content = proc.curr_proc.wchan().unwrap_or_default(); 32 | let fmt_content = if raw_content == "0" { 33 | String::from("-") 34 | } else { 35 | raw_content.clone() 36 | }; 37 | 38 | self.fmt_contents.insert(proc.pid, fmt_content); 39 | self.raw_contents.insert(proc.pid, raw_content); 40 | } 41 | 42 | column_default!(String, false); 43 | } 44 | 45 | #[cfg(target_os = "freebsd")] 46 | impl Column for Wchan { 47 | fn add(&mut self, proc: &ProcessInfo) { 48 | let raw_content = if let Ok(wmesg) = crate::util::ptr_to_cstr(&proc.curr_proc.info.wmesg) { 49 | wmesg.to_string_lossy().into_owned() 50 | } else { 51 | String::from("") 52 | }; 53 | let fmt_content = raw_content.clone(); 54 | 55 | self.fmt_contents.insert(proc.pid, fmt_content); 56 | self.raw_contents.insert(proc.pid, raw_content); 57 | } 58 | 59 | column_default!(String, false); 60 | } 61 | -------------------------------------------------------------------------------- /src/columns/work_dir.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::{column_default, Column}; 3 | use std::cmp; 4 | use std::collections::HashMap; 5 | use std::path::PathBuf; 6 | 7 | pub struct WorkDir { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | procfs: Option, 14 | } 15 | 16 | impl WorkDir { 17 | pub fn new(header: Option, procfs: Option) -> Self { 18 | let header = header.unwrap_or_else(|| String::from("WorkDir")); 19 | let unit = String::new(); 20 | Self { 21 | fmt_contents: HashMap::new(), 22 | raw_contents: HashMap::new(), 23 | width: 0, 24 | header, 25 | unit, 26 | procfs, 27 | } 28 | } 29 | } 30 | 31 | #[cfg(any(target_os = "linux", target_os = "android"))] 32 | impl Column for WorkDir { 33 | fn add(&mut self, proc: &ProcessInfo) { 34 | let fmt_content = if let Ok(proc) = crate::util::process_new(proc.pid, &self.procfs) { 35 | if let Ok(path) = proc.cwd() { 36 | path.to_string_lossy().to_string() 37 | } else { 38 | String::from("") 39 | } 40 | } else { 41 | String::from("") 42 | }; 43 | let raw_content = fmt_content.clone(); 44 | 45 | self.fmt_contents.insert(proc.pid, fmt_content); 46 | self.raw_contents.insert(proc.pid, raw_content); 47 | } 48 | 49 | column_default!(String, false); 50 | } 51 | -------------------------------------------------------------------------------- /src/columns/write_bytes.rs: -------------------------------------------------------------------------------- 1 | use crate::process::ProcessInfo; 2 | use crate::util::bytify; 3 | use crate::{column_default, Column}; 4 | use std::cmp; 5 | use std::collections::HashMap; 6 | 7 | pub struct WriteBytes { 8 | header: String, 9 | unit: String, 10 | fmt_contents: HashMap, 11 | raw_contents: HashMap, 12 | width: usize, 13 | } 14 | 15 | impl WriteBytes { 16 | pub fn new(header: Option) -> Self { 17 | let header = header.unwrap_or_else(|| String::from("Write")); 18 | let unit = String::from("[B/s]"); 19 | Self { 20 | fmt_contents: HashMap::new(), 21 | raw_contents: HashMap::new(), 22 | width: 0, 23 | header, 24 | unit, 25 | } 26 | } 27 | } 28 | 29 | #[cfg(any(target_os = "linux", target_os = "android"))] 30 | impl Column for WriteBytes { 31 | fn add(&mut self, proc: &ProcessInfo) { 32 | let (fmt_content, raw_content) = if proc.curr_io.is_some() && proc.prev_io.is_some() { 33 | let interval_ms = proc.interval.as_secs() + u64::from(proc.interval.subsec_millis()); 34 | let io = (proc.curr_io.as_ref().unwrap().write_bytes 35 | - proc.prev_io.as_ref().unwrap().write_bytes) 36 | * 1000 37 | / interval_ms; 38 | (bytify(io), io) 39 | } else { 40 | (String::new(), 0) 41 | }; 42 | 43 | self.fmt_contents.insert(proc.pid, fmt_content); 44 | self.raw_contents.insert(proc.pid, raw_content); 45 | } 46 | 47 | column_default!(u64, true); 48 | } 49 | 50 | #[cfg(target_os = "macos")] 51 | impl Column for WriteBytes { 52 | fn add(&mut self, proc: &ProcessInfo) { 53 | let (fmt_content, raw_content) = if proc.curr_res.is_some() && proc.prev_res.is_some() { 54 | let interval_ms = proc.interval.as_secs() + u64::from(proc.interval.subsec_millis()); 55 | let io = (proc.curr_res.as_ref().unwrap().ri_diskio_byteswritten 56 | - proc.prev_res.as_ref().unwrap().ri_diskio_byteswritten) 57 | * 1000 58 | / interval_ms; 59 | (bytify(io), io) 60 | } else { 61 | (String::from(""), 0) 62 | }; 63 | 64 | self.fmt_contents.insert(proc.pid, fmt_content); 65 | self.raw_contents.insert(proc.pid, raw_content); 66 | } 67 | 68 | column_default!(u64, true); 69 | } 70 | 71 | #[cfg(target_os = "windows")] 72 | impl Column for WriteBytes { 73 | fn add(&mut self, proc: &ProcessInfo) { 74 | let interval_ms = proc.interval.as_secs() + u64::from(proc.interval.subsec_millis()); 75 | let io = (proc.disk_info.curr_write - proc.disk_info.prev_write) * 1000 / interval_ms; 76 | 77 | let raw_content = io; 78 | let fmt_content = bytify(raw_content); 79 | 80 | self.fmt_contents.insert(proc.pid, fmt_content); 81 | self.raw_contents.insert(proc.pid, raw_content); 82 | } 83 | 84 | column_default!(u64, true); 85 | } 86 | 87 | #[cfg(target_os = "freebsd")] 88 | impl Column for WriteBytes { 89 | fn add(&mut self, proc: &ProcessInfo) { 90 | // io block size: 128KB 91 | let block_size = 128 * 1024; 92 | let interval_ms = proc.interval.as_secs() + u64::from(proc.interval.subsec_millis()); 93 | let io = (proc.curr_proc.info.rusage.oublock as u64 94 | - proc.prev_proc.info.rusage.oublock as u64) 95 | * block_size 96 | * 1000 97 | / interval_ms; 98 | 99 | let raw_content = io; 100 | let fmt_content = bytify(raw_content); 101 | 102 | self.fmt_contents.insert(proc.pid, fmt_content); 103 | self.raw_contents.insert(proc.pid, raw_content); 104 | } 105 | 106 | column_default!(u64, true); 107 | } 108 | -------------------------------------------------------------------------------- /src/process.rs: -------------------------------------------------------------------------------- 1 | #[cfg(target_os = "freebsd")] 2 | pub mod freebsd; 3 | #[cfg(any(target_os = "linux", target_os = "android"))] 4 | pub mod linux; 5 | #[cfg(target_os = "macos")] 6 | pub mod macos; 7 | #[cfg(target_os = "windows")] 8 | pub mod windows; 9 | 10 | #[cfg(target_os = "freebsd")] 11 | pub use self::freebsd::*; 12 | #[cfg(any(target_os = "linux", target_os = "android"))] 13 | pub use self::linux::*; 14 | #[cfg(target_os = "macos")] 15 | pub use self::macos::*; 16 | #[cfg(target_os = "windows")] 17 | pub use self::windows::*; 18 | -------------------------------------------------------------------------------- /src/process/freebsd.rs: -------------------------------------------------------------------------------- 1 | use bsd_kvm::{Access, KernProc, Kvm, Process}; 2 | use std::collections::HashMap; 3 | use std::path::PathBuf; 4 | use std::thread; 5 | use std::time::{Duration, Instant}; 6 | 7 | pub struct ProcessInfo { 8 | pub pid: i32, 9 | pub ppid: i32, 10 | pub curr_proc: Process, 11 | pub prev_proc: Process, 12 | pub interval: Duration, 13 | } 14 | 15 | pub fn collect_proc( 16 | interval: Duration, 17 | _with_thread: bool, 18 | _show_kthreads: bool, 19 | _procfs_path: &Option, 20 | ) -> Vec { 21 | let mut base_procs = HashMap::new(); 22 | let mut ret = Vec::new(); 23 | 24 | let kvm = Kvm::open(None, Some("/dev/null"), Access::ReadOnly); 25 | if let Ok(mut kvm) = kvm { 26 | for proc in kvm.get_process(KernProc::Proc, 0) { 27 | let time = Instant::now(); 28 | base_procs.insert(proc.info.pid, (proc.clone(), time)); 29 | } 30 | 31 | thread::sleep(interval); 32 | 33 | for proc in kvm.get_process(KernProc::Proc, 0) { 34 | let pid = proc.info.pid; 35 | if let Some((prev_proc, prev_time)) = base_procs.remove(&pid) { 36 | let curr_time = Instant::now(); 37 | let curr_proc = proc.clone(); 38 | let interval = curr_time - prev_time; 39 | 40 | let proc = ProcessInfo { 41 | pid: proc.info.pid, 42 | ppid: proc.info.ppid, 43 | curr_proc, 44 | prev_proc, 45 | interval, 46 | }; 47 | ret.push(proc); 48 | } 49 | } 50 | } 51 | 52 | ret 53 | } 54 | -------------------------------------------------------------------------------- /src/term_info.rs: -------------------------------------------------------------------------------- 1 | use anyhow::Error; 2 | use console::Term; 3 | use minus::input::{self, HashedEventRegister, InputEvent}; 4 | use minus::Pager; 5 | use std::cell::RefCell; 6 | use std::fmt::Write; 7 | 8 | pub struct TermInfo { 9 | term: Term, 10 | pub pager: RefCell>, 11 | pub height: usize, 12 | pub width: usize, 13 | pub clear_by_line: bool, 14 | pub use_pager: bool, 15 | } 16 | 17 | impl TermInfo { 18 | pub fn new(clear_by_line: bool, use_pager: bool) -> Result { 19 | let term = Term::stdout(); 20 | let pager = RefCell::new(Some(gen_pager()?)); 21 | let (term_h, term_w) = term.size(); 22 | let height = term_h as usize; 23 | let width = term_w as usize; 24 | 25 | Ok(TermInfo { 26 | term, 27 | pager, 28 | height, 29 | width, 30 | clear_by_line, 31 | use_pager, 32 | }) 33 | } 34 | 35 | pub fn write_line(&self, s: &str) -> Result<(), Error> { 36 | if self.clear_by_line { 37 | self.term.clear_line()?; 38 | } 39 | if self.use_pager { 40 | writeln!(self.pager.borrow_mut().as_mut().unwrap(), "{s}")?; 41 | } else { 42 | self.term.write_line(s)?; 43 | } 44 | Ok(()) 45 | } 46 | 47 | pub fn clear_screen(&self) -> Result<(), Error> { 48 | self.term.clear_screen()?; 49 | Ok(()) 50 | } 51 | 52 | pub fn move_cursor_to(&self, x: usize, y: usize) -> Result<(), Error> { 53 | self.term.move_cursor_to(x, y)?; 54 | Ok(()) 55 | } 56 | 57 | pub fn clear_rest_lines(&self) -> Result<(), Error> { 58 | for _ in 0..self.height { 59 | self.term.clear_line()?; 60 | self.term.move_cursor_down(1)?; 61 | } 62 | Ok(()) 63 | } 64 | } 65 | 66 | fn gen_pager() -> Result { 67 | let pager = Pager::new(); 68 | let mut input_register = HashedEventRegister::default(); 69 | input::generate_default_bindings(&mut input_register); 70 | 71 | // less compatible keybindings 72 | input_register.add_key_events(&["y", "c-y", "c-p", "c-k"], |_, ps| { 73 | let position = ps.prefix_num.parse::().unwrap_or(1); 74 | InputEvent::UpdateUpperMark(ps.upper_mark.saturating_sub(position)) 75 | }); 76 | 77 | input_register.add_key_events(&["c-n", "e", "c-e", "c-j"], |_, ps| { 78 | let position = ps.prefix_num.parse::().unwrap_or(1); 79 | InputEvent::UpdateUpperMark(ps.upper_mark.saturating_add(position)) 80 | }); 81 | input_register.add_key_events(&["b", "c-b"], |_, ps| { 82 | InputEvent::UpdateUpperMark(ps.upper_mark.saturating_sub(ps.rows - 1)) 83 | }); 84 | input_register.add_key_events(&["c-v", "f", "c-f"], |_, ps| { 85 | InputEvent::UpdateUpperMark(ps.upper_mark.saturating_add(ps.rows - 1)) 86 | }); 87 | input_register.add_key_events(&["<"], |_, _| InputEvent::UpdateUpperMark(0)); 88 | input_register.add_key_events(&[">"], |_, ps| { 89 | let mut position = ps 90 | .prefix_num 91 | .parse::() 92 | .unwrap_or(usize::MAX) 93 | // Reduce 1 here, because line numbering starts from 1 94 | // while upper_mark starts from 0 95 | .saturating_sub(1); 96 | 97 | if position == 0 { 98 | position = usize::MAX; 99 | } 100 | InputEvent::UpdateUpperMark(position) 101 | }); 102 | 103 | pager.set_input_classifier(Box::new(input_register))?; 104 | Ok(pager) 105 | } 106 | -------------------------------------------------------------------------------- /src/watcher.rs: -------------------------------------------------------------------------------- 1 | use crate::config::*; 2 | use crate::term_info::TermInfo; 3 | use crate::util::get_theme; 4 | use crate::view::View; 5 | use crate::Opt; 6 | use anyhow::Error; 7 | use chrono::offset::Local; 8 | use getch::Getch; 9 | use std::collections::HashMap; 10 | use std::sync::mpsc::{channel, Receiver, Sender}; 11 | use std::thread; 12 | use std::time::Duration; 13 | 14 | enum Command { 15 | Wake, 16 | Sleep, 17 | Next, 18 | Prev, 19 | Ascending, 20 | Descending, 21 | Quit, 22 | } 23 | 24 | pub struct Watcher; 25 | 26 | impl Watcher { 27 | fn spawn_cmd(tx: Sender) { 28 | let _ = thread::spawn(move || { 29 | let getch = Getch::new(); 30 | loop { 31 | match getch.getch() { 32 | Ok(x) if char::from(x) == 'q' => { 33 | let _ = tx.send(Command::Quit); 34 | break; 35 | } 36 | Ok(x) if char::from(x) == 'n' => { 37 | let _ = tx.send(Command::Next); 38 | } 39 | Ok(x) if char::from(x) == 'p' => { 40 | let _ = tx.send(Command::Prev); 41 | } 42 | Ok(x) if char::from(x) == 'a' => { 43 | let _ = tx.send(Command::Ascending); 44 | } 45 | Ok(x) if char::from(x) == 'd' => { 46 | let _ = tx.send(Command::Descending); 47 | } 48 | // On windows, _getch return EXT(0x3) by Ctrl-C 49 | #[cfg(target_os = "windows")] 50 | Ok(x) if x == 3 => { 51 | let _ = tx.send(Command::Quit); 52 | break; 53 | } 54 | _ => (), 55 | } 56 | } 57 | }); 58 | } 59 | 60 | fn spawn_sleep(rx: Receiver, tx: Sender, interval: u64) { 61 | let _ = thread::spawn(move || loop { 62 | if let Ok(Command::Quit) = rx.recv() { 63 | break; 64 | } 65 | thread::sleep(Duration::from_millis(interval)); 66 | let _ = tx.send(Command::Wake); 67 | }); 68 | } 69 | 70 | fn display_header(term_info: &TermInfo, opt: &Opt, interval: u64) -> Result { 71 | let header = if opt.tree { 72 | format!( 73 | " Interval: {}ms, Last Updated: {} ( Quit: q or Ctrl-C )", 74 | interval, 75 | Local::now().format("%Y/%m/%d %H:%M:%S"), 76 | ) 77 | } else { 78 | format!( 79 | " Interval: {}ms, Last Updated: {} ( Next: n, Prev: p, Ascending: a, Descending: d, Quit: q or Ctrl-C )", 80 | interval, 81 | Local::now().format("%Y/%m/%d %H:%M:%S"), 82 | ) 83 | }; 84 | let result = header.len(); 85 | term_info.write_line(&format!( 86 | "{}", 87 | console::style(header).white().bold().underlined() 88 | ))?; 89 | 90 | term_info.write_line("")?; 91 | Ok(result.div_ceil(term_info.width)) 92 | } 93 | 94 | pub fn start(opt: &mut Opt, config: &Config, interval: u64) -> Result<(), Error> { 95 | let theme = get_theme(opt, config); 96 | 97 | let (tx_cmd, rx_cmd) = channel(); 98 | Watcher::spawn_cmd(tx_cmd.clone()); 99 | 100 | let (tx_sleep, rx_sleep) = channel(); 101 | Watcher::spawn_sleep(rx_sleep, tx_cmd, interval); 102 | 103 | let term_info = TermInfo::new(false, false)?; 104 | term_info.clear_screen()?; 105 | 106 | let mut sort_idx = None; 107 | let mut sort_order = None; 108 | let mut min_widths = HashMap::new(); 109 | let mut prev_term_width = 0; 110 | let mut prev_term_height = 0; 111 | 'outer: loop { 112 | let mut view = View::new(opt, config, true)?; 113 | 114 | // Override sort_info by key 115 | if !opt.tree { 116 | view.sort_info.idx = sort_idx.unwrap_or(view.sort_info.idx); 117 | view.sort_info.order = sort_order.clone().unwrap_or(view.sort_info.order); 118 | } 119 | 120 | let resized = prev_term_width != view.term_info.width 121 | || prev_term_height != view.term_info.height; 122 | if resized { 123 | term_info.clear_screen()?; 124 | } 125 | let header_lines = Watcher::display_header(&view.term_info, opt, interval)?; 126 | 127 | view.filter(opt, config, header_lines); 128 | view.adjust(config, &min_widths); 129 | for (i, c) in view.columns.iter().enumerate() { 130 | min_widths.insert(i, c.column.get_width()); 131 | } 132 | 133 | view.display(opt, config, &theme)?; 134 | 135 | view.term_info.clear_rest_lines()?; 136 | view.term_info.move_cursor_to(0, 0)?; 137 | 138 | tx_sleep.send(Command::Sleep)?; 139 | let mut cmds = Vec::new(); 140 | if let Ok(cmd) = rx_cmd.recv() { 141 | cmds.push(cmd); 142 | for c in rx_cmd.try_iter() { 143 | cmds.push(c); 144 | } 145 | } 146 | 147 | for cmd in cmds { 148 | match cmd { 149 | Command::Quit => { 150 | tx_sleep.send(Command::Quit)?; 151 | view.term_info.clear_screen()?; 152 | break 'outer; 153 | } 154 | Command::Next => sort_idx = Some(view.inc_sort_column()), 155 | Command::Prev => sort_idx = Some(view.dec_sort_column()), 156 | Command::Ascending => sort_order = Some(ConfigSortOrder::Ascending), 157 | Command::Descending => sort_order = Some(ConfigSortOrder::Descending), 158 | _ => (), 159 | } 160 | } 161 | 162 | prev_term_width = view.term_info.width; 163 | prev_term_height = view.term_info.height; 164 | } 165 | Ok(()) 166 | } 167 | } 168 | --------------------------------------------------------------------------------