Serenity Operating System
at portability 209 lines 6.8 kB view raw
1/* 2 * Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org> 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, this 9 * list of conditions and the following disclaimer. 10 * 11 * 2. Redistributions in binary form must reproduce the above copyright notice, 12 * this list of conditions and the following disclaimer in the documentation 13 * and/or other materials provided with the distribution. 14 * 15 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 16 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 17 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 18 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 19 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 20 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 21 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 22 * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 25 */ 26 27#include <AK/HashMap.h> 28#include <AK/JsonArray.h> 29#include <AK/JsonObject.h> 30#include <AK/JsonValue.h> 31#include <AK/QuickSort.h> 32#include <AK/String.h> 33#include <AK/Vector.h> 34#include <LibCore/ProcessStatisticsReader.h> 35#include <fcntl.h> 36#include <stdio.h> 37#include <stdlib.h> 38#include <unistd.h> 39 40struct ThreadData { 41 int tid; 42 pid_t pid; 43 unsigned pgid; 44 unsigned pgp; 45 unsigned sid; 46 uid_t uid; 47 gid_t gid; 48 pid_t ppid; 49 unsigned nfds; 50 String name; 51 String tty; 52 size_t amount_virtual; 53 size_t amount_resident; 54 size_t amount_shared; 55 unsigned syscall_count; 56 unsigned inode_faults; 57 unsigned zero_faults; 58 unsigned cow_faults; 59 int icon_id; 60 unsigned times_scheduled; 61 62 unsigned times_scheduled_since_prev { 0 }; 63 unsigned cpu_percent { 0 }; 64 unsigned cpu_percent_decimal { 0 }; 65 66 u32 priority; 67 String username; 68 String state; 69}; 70 71struct PidAndTid { 72 bool operator==(const PidAndTid& other) const 73 { 74 return pid == other.pid && tid == other.tid; 75 } 76 pid_t pid; 77 int tid; 78}; 79 80namespace AK { 81template<> 82struct Traits<PidAndTid> : public GenericTraits<PidAndTid> { 83 static unsigned hash(const PidAndTid& value) { return pair_int_hash(value.pid, value.tid); } 84}; 85} 86 87struct Snapshot { 88 HashMap<PidAndTid, ThreadData> map; 89 u32 sum_times_scheduled { 0 }; 90}; 91 92static Snapshot get_snapshot() 93{ 94 Snapshot snapshot; 95 96 auto all_processes = Core::ProcessStatisticsReader::get_all(); 97 98 for (auto& it : all_processes) { 99 auto& stats = it.value; 100 for (auto& thread : stats.threads) { 101 snapshot.sum_times_scheduled += thread.times_scheduled; 102 ThreadData thread_data; 103 thread_data.tid = thread.tid; 104 thread_data.pid = stats.pid; 105 thread_data.pgid = stats.pgid; 106 thread_data.pgp = stats.pgp; 107 thread_data.sid = stats.sid; 108 thread_data.uid = stats.uid; 109 thread_data.gid = stats.gid; 110 thread_data.ppid = stats.ppid; 111 thread_data.nfds = stats.nfds; 112 thread_data.name = stats.name; 113 thread_data.tty = stats.tty; 114 thread_data.amount_virtual = stats.amount_virtual; 115 thread_data.amount_resident = stats.amount_resident; 116 thread_data.amount_shared = stats.amount_shared; 117 thread_data.syscall_count = thread.syscall_count; 118 thread_data.inode_faults = thread.inode_faults; 119 thread_data.zero_faults = thread.zero_faults; 120 thread_data.cow_faults = thread.cow_faults; 121 thread_data.icon_id = stats.icon_id; 122 thread_data.times_scheduled = thread.times_scheduled; 123 thread_data.priority = thread.priority; 124 thread_data.state = thread.state; 125 thread_data.username = stats.username; 126 127 snapshot.map.set({ stats.pid, thread.tid }, move(thread_data)); 128 } 129 } 130 131 return snapshot; 132} 133 134int main(int, char**) 135{ 136 if (pledge("stdio rpath", nullptr) < 0) { 137 perror("pledge"); 138 return 1; 139 } 140 141 if (unveil("/proc/all", "r") < 0) { 142 perror("unveil"); 143 return 1; 144 } 145 146 if (unveil("/etc/passwd", "r") < 0) { 147 perror("unveil"); 148 return 1; 149 } 150 151 unveil(nullptr, nullptr); 152 153 Vector<ThreadData*> threads; 154 auto prev = get_snapshot(); 155 usleep(10000); 156 for (;;) { 157 auto current = get_snapshot(); 158 auto sum_diff = current.sum_times_scheduled - prev.sum_times_scheduled; 159 160 printf("\033[3J\033[H\033[2J"); 161 printf("\033[47;30m%6s %3s %3s %-8s %-10s %6s %6s %4s %s\033[K\033[0m\n", 162 "PID", 163 "TID", 164 "PRI", 165 "USER", 166 "STATE", 167 "VIRT", 168 "PHYS", 169 "%CPU", 170 "NAME"); 171 for (auto& it : current.map) { 172 auto pid_and_tid = it.key; 173 if (pid_and_tid.pid == 0) 174 continue; 175 u32 times_scheduled_now = it.value.times_scheduled; 176 auto jt = prev.map.find(pid_and_tid); 177 if (jt == prev.map.end()) 178 continue; 179 u32 times_scheduled_before = (*jt).value.times_scheduled; 180 u32 times_scheduled_diff = times_scheduled_now - times_scheduled_before; 181 it.value.times_scheduled_since_prev = times_scheduled_diff; 182 it.value.cpu_percent = ((times_scheduled_diff * 100) / sum_diff); 183 it.value.cpu_percent_decimal = (((times_scheduled_diff * 1000) / sum_diff) % 10); 184 threads.append(&it.value); 185 } 186 187 quick_sort(threads.begin(), threads.end(), [](auto* p1, auto* p2) { 188 return p2->times_scheduled_since_prev < p1->times_scheduled_since_prev; 189 }); 190 191 for (auto* thread : threads) { 192 printf("%6d %3d %2u %-8s %-10s %6zu %6zu %2u.%1u %s\n", 193 thread->pid, 194 thread->tid, 195 thread->priority, 196 thread->username.characters(), 197 thread->state.characters(), 198 thread->amount_virtual / 1024, 199 thread->amount_resident / 1024, 200 thread->cpu_percent, 201 thread->cpu_percent_decimal, 202 thread->name.characters()); 203 } 204 threads.clear_with_capacity(); 205 prev = move(current); 206 sleep(1); 207 } 208 return 0; 209}