├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md └── src └── main.rs /Cargo.lock: -------------------------------------------------------------------------------- 1 | # This file is automatically @generated by Cargo. 2 | # It is not intended for manual editing. 3 | version = 3 4 | 5 | [[package]] 6 | name = "autocfg" 7 | version = "1.1.0" 8 | source = "registry+https://github.com/rust-lang/crates.io-index" 9 | checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" 10 | 11 | [[package]] 12 | name = "cfg-if" 13 | version = "1.0.0" 14 | source = "registry+https://github.com/rust-lang/crates.io-index" 15 | checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" 16 | 17 | [[package]] 18 | name = "crossbeam-channel" 19 | version = "0.5.5" 20 | source = "registry+https://github.com/rust-lang/crates.io-index" 21 | checksum = "4c02a4d71819009c192cf4872265391563fd6a84c81ff2c0f2a7026ca4c1d85c" 22 | dependencies = [ 23 | "cfg-if", 24 | "crossbeam-utils", 25 | ] 26 | 27 | [[package]] 28 | name = "crossbeam-deque" 29 | version = "0.8.1" 30 | source = "registry+https://github.com/rust-lang/crates.io-index" 31 | checksum = "6455c0ca19f0d2fbf751b908d5c55c1f5cbc65e03c4225427254b46890bdde1e" 32 | dependencies = [ 33 | "cfg-if", 34 | "crossbeam-epoch", 35 | "crossbeam-utils", 36 | ] 37 | 38 | [[package]] 39 | name = "crossbeam-epoch" 40 | version = "0.9.9" 41 | source = "registry+https://github.com/rust-lang/crates.io-index" 42 | checksum = "07db9d94cbd326813772c968ccd25999e5f8ae22f4f8d1b11effa37ef6ce281d" 43 | dependencies = [ 44 | "autocfg", 45 | "cfg-if", 46 | "crossbeam-utils", 47 | "memoffset", 48 | "once_cell", 49 | "scopeguard", 50 | ] 51 | 52 | [[package]] 53 | name = "crossbeam-utils" 54 | version = "0.8.10" 55 | source = "registry+https://github.com/rust-lang/crates.io-index" 56 | checksum = "7d82ee10ce34d7bc12c2122495e7593a9c41347ecdd64185af4ecf72cb1a7f83" 57 | dependencies = [ 58 | "cfg-if", 59 | "once_cell", 60 | ] 61 | 62 | [[package]] 63 | name = "either" 64 | version = "1.7.0" 65 | source = "registry+https://github.com/rust-lang/crates.io-index" 66 | checksum = "3f107b87b6afc2a64fd13cac55fe06d6c8859f12d4b14cbcdd2c67d0976781be" 67 | 68 | [[package]] 69 | name = "hermit-abi" 70 | version = "0.1.19" 71 | source = "registry+https://github.com/rust-lang/crates.io-index" 72 | checksum = "62b467343b94ba476dcb2500d242dadbb39557df889310ac77c5d99100aaac33" 73 | dependencies = [ 74 | "libc", 75 | ] 76 | 77 | [[package]] 78 | name = "libc" 79 | version = "0.2.126" 80 | source = "registry+https://github.com/rust-lang/crates.io-index" 81 | checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" 82 | 83 | [[package]] 84 | name = "memoffset" 85 | version = "0.6.5" 86 | source = "registry+https://github.com/rust-lang/crates.io-index" 87 | checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce" 88 | dependencies = [ 89 | "autocfg", 90 | ] 91 | 92 | [[package]] 93 | name = "num_cpus" 94 | version = "1.13.1" 95 | source = "registry+https://github.com/rust-lang/crates.io-index" 96 | checksum = "19e64526ebdee182341572e50e9ad03965aa510cd94427a4549448f285e957a1" 97 | dependencies = [ 98 | "hermit-abi", 99 | "libc", 100 | ] 101 | 102 | [[package]] 103 | name = "once_cell" 104 | version = "1.13.0" 105 | source = "registry+https://github.com/rust-lang/crates.io-index" 106 | checksum = "18a6dbe30758c9f83eb00cbea4ac95966305f5a7772f3f42ebfc7fc7eddbd8e1" 107 | 108 | [[package]] 109 | name = "rayon" 110 | version = "1.5.3" 111 | source = "registry+https://github.com/rust-lang/crates.io-index" 112 | checksum = "bd99e5772ead8baa5215278c9b15bf92087709e9c1b2d1f97cdb5a183c933a7d" 113 | dependencies = [ 114 | "autocfg", 115 | "crossbeam-deque", 116 | "either", 117 | "rayon-core", 118 | ] 119 | 120 | [[package]] 121 | name = "rayon-core" 122 | version = "1.9.3" 123 | source = "registry+https://github.com/rust-lang/crates.io-index" 124 | checksum = "258bcdb5ac6dad48491bb2992db6b7cf74878b0384908af124823d118c99683f" 125 | dependencies = [ 126 | "crossbeam-channel", 127 | "crossbeam-deque", 128 | "crossbeam-utils", 129 | "num_cpus", 130 | ] 131 | 132 | [[package]] 133 | name = "scopeguard" 134 | version = "1.1.0" 135 | source = "registry+https://github.com/rust-lang/crates.io-index" 136 | checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd" 137 | 138 | [[package]] 139 | name = "swapview" 140 | version = "0.1.0" 141 | dependencies = [ 142 | "rayon", 143 | ] 144 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "swapview" 3 | version = "0.1.0" 4 | authors = ["lilydjwg "] 5 | edition = "2021" 6 | license = "BSD-3-Clause" 7 | keywords = ["swap"] 8 | description = " A simple program to view processes' swap usage on Linux. " 9 | homepage = "https://github.com/lilydjwg/swapview" 10 | repository = "https://github.com/lilydjwg/swapview" 11 | readme = "README.md" 12 | 13 | [dependencies] 14 | rayon = "1.5.1" 15 | 16 | [profile.release] 17 | lto = true 18 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, 依云 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | swapview is a simple program to view processes' swap usage on Linux. 2 | 3 | This is the version for daily use. For implementations in different programming languages, see [swapview-rosetta](https://github.com/lilydjwg/swapview-rosetta). 4 | 5 | Install: 6 | 7 | ```sh 8 | cargo install swapview 9 | ``` 10 | 11 | Tips: you can continuously monitor swap usage in a terminal with 12 | 13 | ```sh 14 | watch -n 1 "swapview | tail -\$((\$LINES - 2)) | cut -b -\$COLUMNS" 15 | ``` 16 | -------------------------------------------------------------------------------- /src/main.rs: -------------------------------------------------------------------------------- 1 | extern crate rayon; 2 | 3 | use std::fs::{File,read_dir}; 4 | use std::io::Read; 5 | use std::sync::mpsc::channel; 6 | use rayon::prelude::*; 7 | 8 | const UNITS: [char; 4] = ['K', 'M', 'G', 'T']; 9 | 10 | fn filesize(size: isize) -> String { 11 | let mut left = size.abs() as f64; 12 | let mut unit = -1; 13 | 14 | while left > 1100. && unit < 3 { 15 | left /= 1024.; 16 | unit += 1; 17 | } 18 | if unit == -1 { 19 | format!("{}B", size) 20 | } else { 21 | if size < 0 { 22 | left = -left; 23 | } 24 | format!("{:.1}{}iB", left, UNITS[unit as usize]) 25 | } 26 | } 27 | 28 | fn chop_null(mut s: String) -> String { 29 | let last = s.len() - 1; 30 | if !s.is_empty() && s.as_bytes()[last] == 0 { 31 | s.truncate(last); 32 | } 33 | s.replace("\0", " ") 34 | } 35 | 36 | fn get_comm_for(pid: usize) -> String { 37 | let cmdline_path = format!("/proc/{}/cmdline", pid); 38 | let mut buf = String::new(); 39 | let mut file = match File::open(&cmdline_path) { 40 | Ok(f) => f, 41 | Err(_) => return String::new(), 42 | }; 43 | match file.read_to_string(&mut buf) { 44 | Ok(_) => (), 45 | Err(_) => return String::new(), 46 | }; 47 | chop_null(buf) 48 | } 49 | 50 | fn get_swap_for(pid: usize) -> isize { 51 | let smaps_path = format!("/proc/{}/smaps_rollup", pid); 52 | 53 | let mut file = match File::open(&smaps_path) { 54 | Ok(f) => f, 55 | Err(_) => return 0, 56 | }; 57 | 58 | let mut vec = vec![]; 59 | if file.read_to_end(&mut vec).is_err() { 60 | return 0 61 | } 62 | for line in vec.split(|&c| c == b'\n') { 63 | if line.starts_with(b"Swap:") { 64 | let string = line[5..] 65 | .iter() 66 | .skip_while(|&&c| c == b' ') 67 | .take_while(|&&c| c != b' ') 68 | .map(|&c| c as char) 69 | .collect::(); 70 | return string.parse::().unwrap() * 1024; 71 | } 72 | } 73 | 0 74 | } 75 | 76 | fn get_swap() -> Vec<(usize, isize, String)> { 77 | rayon::in_place_scope(|pool| { 78 | let (tx, rx) = channel(); 79 | for d in read_dir("/proc").unwrap() { 80 | let tx = tx.clone(); 81 | pool.spawn(move |_| { 82 | let path = d.unwrap().path(); 83 | if let Ok(pid) = path.file_name().unwrap().to_str().unwrap().parse() { 84 | tx.send(match get_swap_for(pid) { 85 | 0 => None, 86 | swap => Some((pid, swap, get_comm_for(pid))), 87 | }).unwrap(); 88 | } else { 89 | tx.send(None).unwrap(); 90 | } 91 | }); 92 | } 93 | drop(tx); 94 | rx.iter().filter_map(|x| x).collect() 95 | }) 96 | } 97 | 98 | fn main() { 99 | // let format = "{:>5} {:>9} {}"; 100 | // let totalFmt = "Total: {:8}"; 101 | let mut swapinfo = get_swap(); 102 | swapinfo.par_sort_unstable_by_key(|&(_, size, _)| size); 103 | 104 | println!("{:>7} {:>9} {}", "PID", "SWAP", "COMMAND"); 105 | let mut total = 0; 106 | for &(pid, swap, ref comm) in &swapinfo { 107 | total += swap; 108 | println!("{:>7} {:>9} {}", pid, filesize(swap), comm); 109 | } 110 | println!("Total: {:>10}", filesize(total)); 111 | } 112 | 113 | // vim: se sw=2: 114 | --------------------------------------------------------------------------------