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 "ProcessModel.h"
28#include "GraphWidget.h"
29#include <AK/JsonArray.h>
30#include <AK/JsonObject.h>
31#include <AK/JsonValue.h>
32#include <AK/SharedBuffer.h>
33#include <LibCore/ProcessStatisticsReader.h>
34#include <fcntl.h>
35#include <stdio.h>
36
37static ProcessModel* s_the;
38
39ProcessModel& ProcessModel::the()
40{
41 ASSERT(s_the);
42 return *s_the;
43}
44
45ProcessModel::ProcessModel()
46{
47 ASSERT(!s_the);
48 s_the = this;
49 m_generic_process_icon = Gfx::Bitmap::load_from_file("/res/icons/gear16.png");
50 m_high_priority_icon = Gfx::Bitmap::load_from_file("/res/icons/highpriority16.png");
51 m_low_priority_icon = Gfx::Bitmap::load_from_file("/res/icons/lowpriority16.png");
52 m_normal_priority_icon = Gfx::Bitmap::load_from_file("/res/icons/normalpriority16.png");
53}
54
55ProcessModel::~ProcessModel()
56{
57}
58
59int ProcessModel::row_count(const GUI::ModelIndex&) const
60{
61 return m_pids.size();
62}
63
64int ProcessModel::column_count(const GUI::ModelIndex&) const
65{
66 return Column::__Count;
67}
68
69String ProcessModel::column_name(int column) const
70{
71 switch (column) {
72 case Column::Icon:
73 return "";
74 case Column::PID:
75 return "PID";
76 case Column::TID:
77 return "TID";
78 case Column::State:
79 return "State";
80 case Column::User:
81 return "User";
82 case Column::Priority:
83 return "Pr";
84 case Column::EffectivePriority:
85 return "EPr";
86 case Column::Virtual:
87 return "Virtual";
88 case Column::Physical:
89 return "Physical";
90 case Column::DirtyPrivate:
91 return "DirtyP";
92 case Column::CleanInode:
93 return "CleanI";
94 case Column::PurgeableVolatile:
95 return "Purg:V";
96 case Column::PurgeableNonvolatile:
97 return "Purg:N";
98 case Column::CPU:
99 return "CPU";
100 case Column::Name:
101 return "Name";
102 case Column::Syscalls:
103 return "Syscalls";
104 case Column::InodeFaults:
105 return "F:Inode";
106 case Column::ZeroFaults:
107 return "F:Zero";
108 case Column::CowFaults:
109 return "F:CoW";
110 case Column::IPv4SocketReadBytes:
111 return "IPv4 In";
112 case Column::IPv4SocketWriteBytes:
113 return "IPv4 Out";
114 case Column::UnixSocketReadBytes:
115 return "Unix In";
116 case Column::UnixSocketWriteBytes:
117 return "Unix Out";
118 case Column::FileReadBytes:
119 return "File In";
120 case Column::FileWriteBytes:
121 return "File Out";
122 case Column::Pledge:
123 return "Pledge";
124 case Column::Veil:
125 return "Veil";
126 default:
127 ASSERT_NOT_REACHED();
128 }
129}
130
131GUI::Model::ColumnMetadata ProcessModel::column_metadata(int column) const
132{
133 switch (column) {
134 case Column::Icon:
135 return { 16, Gfx::TextAlignment::CenterLeft };
136 case Column::PID:
137 return { 32, Gfx::TextAlignment::CenterRight };
138 case Column::TID:
139 return { 32, Gfx::TextAlignment::CenterRight };
140 case Column::State:
141 return { 75, Gfx::TextAlignment::CenterLeft };
142 case Column::Priority:
143 return { 16, Gfx::TextAlignment::CenterRight };
144 case Column::EffectivePriority:
145 return { 16, Gfx::TextAlignment::CenterRight };
146 case Column::User:
147 return { 50, Gfx::TextAlignment::CenterLeft };
148 case Column::Virtual:
149 return { 65, Gfx::TextAlignment::CenterRight };
150 case Column::Physical:
151 return { 65, Gfx::TextAlignment::CenterRight };
152 case Column::DirtyPrivate:
153 return { 65, Gfx::TextAlignment::CenterRight };
154 case Column::CleanInode:
155 return { 65, Gfx::TextAlignment::CenterRight };
156 case Column::PurgeableVolatile:
157 return { 65, Gfx::TextAlignment::CenterRight };
158 case Column::PurgeableNonvolatile:
159 return { 65, Gfx::TextAlignment::CenterRight };
160 case Column::CPU:
161 return { 32, Gfx::TextAlignment::CenterRight };
162 case Column::Name:
163 return { 140, Gfx::TextAlignment::CenterLeft };
164 case Column::Syscalls:
165 return { 60, Gfx::TextAlignment::CenterRight };
166 case Column::InodeFaults:
167 return { 60, Gfx::TextAlignment::CenterRight };
168 case Column::ZeroFaults:
169 return { 60, Gfx::TextAlignment::CenterRight };
170 case Column::CowFaults:
171 return { 60, Gfx::TextAlignment::CenterRight };
172 case Column::FileReadBytes:
173 return { 60, Gfx::TextAlignment::CenterRight };
174 case Column::FileWriteBytes:
175 return { 60, Gfx::TextAlignment::CenterRight };
176 case Column::UnixSocketReadBytes:
177 return { 60, Gfx::TextAlignment::CenterRight };
178 case Column::UnixSocketWriteBytes:
179 return { 60, Gfx::TextAlignment::CenterRight };
180 case Column::IPv4SocketReadBytes:
181 return { 60, Gfx::TextAlignment::CenterRight };
182 case Column::IPv4SocketWriteBytes:
183 return { 60, Gfx::TextAlignment::CenterRight };
184 case Column::Pledge:
185 return { 60, Gfx::TextAlignment::CenterLeft };
186 case Column::Veil:
187 return { 60, Gfx::TextAlignment::CenterLeft };
188 default:
189 ASSERT_NOT_REACHED();
190 }
191}
192
193static String pretty_byte_size(size_t size)
194{
195 return String::format("%uK", size / 1024);
196}
197
198GUI::Variant ProcessModel::data(const GUI::ModelIndex& index, Role role) const
199{
200 ASSERT(is_valid(index));
201
202 auto it = m_threads.find(m_pids[index.row()]);
203 auto& thread = *(*it).value;
204
205 if (role == Role::Sort) {
206 switch (index.column()) {
207 case Column::Icon:
208 return 0;
209 case Column::PID:
210 return thread.current_state.pid;
211 case Column::TID:
212 return thread.current_state.tid;
213 case Column::State:
214 return thread.current_state.state;
215 case Column::User:
216 return thread.current_state.user;
217 case Column::Priority:
218 return thread.current_state.priority;
219 case Column::EffectivePriority:
220 return thread.current_state.effective_priority;
221 case Column::Virtual:
222 return (int)thread.current_state.amount_virtual;
223 case Column::Physical:
224 return (int)thread.current_state.amount_resident;
225 case Column::DirtyPrivate:
226 return (int)thread.current_state.amount_dirty_private;
227 case Column::CleanInode:
228 return (int)thread.current_state.amount_clean_inode;
229 case Column::PurgeableVolatile:
230 return (int)thread.current_state.amount_purgeable_volatile;
231 case Column::PurgeableNonvolatile:
232 return (int)thread.current_state.amount_purgeable_nonvolatile;
233 case Column::CPU:
234 return thread.current_state.cpu_percent;
235 case Column::Name:
236 return thread.current_state.name;
237 case Column::Syscalls:
238 return thread.current_state.syscall_count;
239 case Column::InodeFaults:
240 return thread.current_state.inode_faults;
241 case Column::ZeroFaults:
242 return thread.current_state.zero_faults;
243 case Column::CowFaults:
244 return thread.current_state.cow_faults;
245 case Column::IPv4SocketReadBytes:
246 return thread.current_state.ipv4_socket_read_bytes;
247 case Column::IPv4SocketWriteBytes:
248 return thread.current_state.ipv4_socket_write_bytes;
249 case Column::UnixSocketReadBytes:
250 return thread.current_state.unix_socket_read_bytes;
251 case Column::UnixSocketWriteBytes:
252 return thread.current_state.unix_socket_write_bytes;
253 case Column::FileReadBytes:
254 return thread.current_state.file_read_bytes;
255 case Column::FileWriteBytes:
256 return thread.current_state.file_write_bytes;
257 case Column::Pledge:
258 return thread.current_state.pledge;
259 case Column::Veil:
260 return thread.current_state.veil;
261 }
262 ASSERT_NOT_REACHED();
263 return {};
264 }
265
266 if (role == Role::Display) {
267 switch (index.column()) {
268 case Column::Icon:
269 if (thread.current_state.icon_id != -1) {
270 auto icon_buffer = SharedBuffer::create_from_shared_buffer_id(thread.current_state.icon_id);
271 if (icon_buffer) {
272 auto icon_bitmap = Gfx::Bitmap::create_with_shared_buffer(Gfx::BitmapFormat::RGBA32, *icon_buffer, { 16, 16 });
273 if (icon_bitmap)
274 return *icon_bitmap;
275 }
276 }
277 return *m_generic_process_icon;
278 case Column::PID:
279 return thread.current_state.pid;
280 case Column::TID:
281 return thread.current_state.tid;
282 case Column::State:
283 return thread.current_state.state;
284 case Column::User:
285 return thread.current_state.user;
286 case Column::Priority:
287 return thread.current_state.priority;
288 case Column::EffectivePriority:
289 return thread.current_state.effective_priority;
290 case Column::Virtual:
291 return pretty_byte_size(thread.current_state.amount_virtual);
292 case Column::Physical:
293 return pretty_byte_size(thread.current_state.amount_resident);
294 case Column::DirtyPrivate:
295 return pretty_byte_size(thread.current_state.amount_dirty_private);
296 case Column::CleanInode:
297 return pretty_byte_size(thread.current_state.amount_clean_inode);
298 case Column::PurgeableVolatile:
299 return pretty_byte_size(thread.current_state.amount_purgeable_volatile);
300 case Column::PurgeableNonvolatile:
301 return pretty_byte_size(thread.current_state.amount_purgeable_nonvolatile);
302 case Column::CPU:
303 return thread.current_state.cpu_percent;
304 case Column::Name:
305 return thread.current_state.name;
306 case Column::Syscalls:
307 return thread.current_state.syscall_count;
308 case Column::InodeFaults:
309 return thread.current_state.inode_faults;
310 case Column::ZeroFaults:
311 return thread.current_state.zero_faults;
312 case Column::CowFaults:
313 return thread.current_state.cow_faults;
314 case Column::IPv4SocketReadBytes:
315 return thread.current_state.ipv4_socket_read_bytes;
316 case Column::IPv4SocketWriteBytes:
317 return thread.current_state.ipv4_socket_write_bytes;
318 case Column::UnixSocketReadBytes:
319 return thread.current_state.unix_socket_read_bytes;
320 case Column::UnixSocketWriteBytes:
321 return thread.current_state.unix_socket_write_bytes;
322 case Column::FileReadBytes:
323 return thread.current_state.file_read_bytes;
324 case Column::FileWriteBytes:
325 return thread.current_state.file_write_bytes;
326 case Column::Pledge:
327 return thread.current_state.pledge;
328 case Column::Veil:
329 return thread.current_state.veil;
330 }
331 }
332
333 return {};
334}
335
336void ProcessModel::update()
337{
338 auto all_processes = Core::ProcessStatisticsReader::get_all();
339
340 unsigned last_sum_times_scheduled = 0;
341 for (auto& it : m_threads)
342 last_sum_times_scheduled += it.value->current_state.times_scheduled;
343
344 HashTable<PidAndTid> live_pids;
345 unsigned sum_times_scheduled = 0;
346 for (auto& it : all_processes) {
347 for (auto& thread : it.value.threads) {
348 ThreadState state;
349 state.pid = it.value.pid;
350 state.user = it.value.username;
351 state.pledge = it.value.pledge;
352 state.veil = it.value.veil;
353 state.syscall_count = thread.syscall_count;
354 state.inode_faults = thread.inode_faults;
355 state.zero_faults = thread.zero_faults;
356 state.cow_faults = thread.cow_faults;
357 state.unix_socket_read_bytes = thread.unix_socket_read_bytes;
358 state.unix_socket_write_bytes = thread.unix_socket_write_bytes;
359 state.ipv4_socket_read_bytes = thread.ipv4_socket_read_bytes;
360 state.ipv4_socket_write_bytes = thread.ipv4_socket_write_bytes;
361 state.file_read_bytes = thread.file_read_bytes;
362 state.file_write_bytes = thread.file_write_bytes;
363 state.amount_virtual = it.value.amount_virtual;
364 state.amount_resident = it.value.amount_resident;
365 state.amount_dirty_private = it.value.amount_dirty_private;
366 state.amount_clean_inode = it.value.amount_clean_inode;
367 state.amount_purgeable_volatile = it.value.amount_purgeable_volatile;
368 state.amount_purgeable_nonvolatile = it.value.amount_purgeable_nonvolatile;
369 state.icon_id = it.value.icon_id;
370
371 state.name = thread.name;
372
373 state.tid = thread.tid;
374 state.times_scheduled = thread.times_scheduled;
375 state.priority = thread.priority;
376 state.effective_priority = thread.effective_priority;
377 state.state = thread.state;
378 sum_times_scheduled += thread.times_scheduled;
379 {
380 auto pit = m_threads.find({ it.value.pid, thread.tid });
381 if (pit == m_threads.end())
382 m_threads.set({ it.value.pid, thread.tid }, make<Thread>());
383 }
384 auto pit = m_threads.find({ it.value.pid, thread.tid });
385 ASSERT(pit != m_threads.end());
386 (*pit).value->previous_state = (*pit).value->current_state;
387 (*pit).value->current_state = state;
388
389 live_pids.set({ it.value.pid, thread.tid });
390 }
391 }
392
393 m_pids.clear();
394 float total_cpu_percent = 0;
395 Vector<PidAndTid, 16> pids_to_remove;
396 for (auto& it : m_threads) {
397 if (!live_pids.contains(it.key)) {
398 pids_to_remove.append(it.key);
399 continue;
400 }
401 auto& process = *it.value;
402 u32 times_scheduled_diff = process.current_state.times_scheduled - process.previous_state.times_scheduled;
403 process.current_state.cpu_percent = ((float)times_scheduled_diff * 100) / (float)(sum_times_scheduled - last_sum_times_scheduled);
404 if (it.key.pid != 0) {
405 total_cpu_percent += process.current_state.cpu_percent;
406 m_pids.append(it.key);
407 }
408 }
409 for (auto pid : pids_to_remove)
410 m_threads.remove(pid);
411
412 if (on_new_cpu_data_point)
413 on_new_cpu_data_point(total_cpu_percent);
414
415 did_update();
416}