Serenity Operating System
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}