Serenity Operating System
at portability 416 lines 16 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 "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}