ProcessModel.cpp 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. #include "ProcessModel.h"
  2. #include <LibCore/CFile.h>
  3. #include <fcntl.h>
  4. #include <stdio.h>
  5. #include <pwd.h>
  6. ProcessModel::ProcessModel()
  7. {
  8. setpwent();
  9. while (auto* passwd = getpwent())
  10. m_usernames.set(passwd->pw_uid, passwd->pw_name);
  11. endpwent();
  12. m_generic_process_icon = GraphicsBitmap::load_from_file("/res/icons/gear16.png");
  13. m_high_priority_icon = GraphicsBitmap::load_from_file("/res/icons/highpriority16.png");
  14. m_low_priority_icon = GraphicsBitmap::load_from_file("/res/icons/lowpriority16.png");
  15. m_normal_priority_icon = GraphicsBitmap::load_from_file("/res/icons/normalpriority16.png");
  16. }
  17. ProcessModel::~ProcessModel()
  18. {
  19. }
  20. int ProcessModel::row_count(const GModelIndex&) const
  21. {
  22. return m_processes.size();
  23. }
  24. int ProcessModel::column_count(const GModelIndex&) const
  25. {
  26. return Column::__Count;
  27. }
  28. String ProcessModel::column_name(int column) const
  29. {
  30. switch (column) {
  31. case Column::Icon: return "";
  32. case Column::PID: return "PID";
  33. case Column::State: return "State";
  34. case Column::User: return "User";
  35. case Column::Priority: return "Pr";
  36. case Column::Linear: return "Linear";
  37. case Column::Physical: return "Physical";
  38. case Column::CPU: return "CPU";
  39. case Column::Name: return "Name";
  40. case Column::Syscalls: return "Syscalls";
  41. default: ASSERT_NOT_REACHED();
  42. }
  43. }
  44. GModel::ColumnMetadata ProcessModel::column_metadata(int column) const
  45. {
  46. switch (column) {
  47. case Column::Icon: return { 16, TextAlignment::CenterLeft };
  48. case Column::PID: return { 25, TextAlignment::CenterRight };
  49. case Column::State: return { 75, TextAlignment::CenterLeft };
  50. case Column::Priority: return { 16, TextAlignment::CenterLeft };
  51. case Column::User: return { 50, TextAlignment::CenterLeft };
  52. case Column::Linear: return { 65, TextAlignment::CenterRight };
  53. case Column::Physical: return { 65, TextAlignment::CenterRight };
  54. case Column::CPU: return { 25, TextAlignment::CenterRight };
  55. case Column::Name: return { 140, TextAlignment::CenterLeft };
  56. case Column::Syscalls: return { 60, TextAlignment::CenterRight };
  57. default: ASSERT_NOT_REACHED();
  58. }
  59. }
  60. static String pretty_byte_size(size_t size)
  61. {
  62. return String::format("%uK", size / 1024);
  63. }
  64. GVariant ProcessModel::data(const GModelIndex& index, Role role) const
  65. {
  66. ASSERT(is_valid(index));
  67. auto it = m_processes.find(m_pids[index.row()]);
  68. auto& process = *(*it).value;
  69. if (role == Role::Sort) {
  70. switch (index.column()) {
  71. case Column::Icon: return 0;
  72. case Column::PID: return process.current_state.pid;
  73. case Column::State: return process.current_state.state;
  74. case Column::User: return process.current_state.user;
  75. case Column::Priority:
  76. if (process.current_state.priority == "Idle")
  77. return 0;
  78. if (process.current_state.priority == "Low")
  79. return 1;
  80. if (process.current_state.priority == "Normal")
  81. return 2;
  82. if (process.current_state.priority == "High")
  83. return 3;
  84. ASSERT_NOT_REACHED();
  85. return 3;
  86. case Column::Linear: return (int)process.current_state.linear;
  87. case Column::Physical: return (int)process.current_state.physical;
  88. case Column::CPU: return process.current_state.cpu_percent;
  89. case Column::Name: return process.current_state.name;
  90. // FIXME: GVariant with unsigned?
  91. case Column::Syscalls: return (int)process.current_state.syscalls;
  92. }
  93. ASSERT_NOT_REACHED();
  94. return { };
  95. }
  96. if (role == Role::Display) {
  97. switch (index.column()) {
  98. case Column::Icon: return *m_generic_process_icon;
  99. case Column::PID: return process.current_state.pid;
  100. case Column::State: return process.current_state.state;
  101. case Column::User: return process.current_state.user;
  102. case Column::Priority:
  103. if (process.current_state.priority == "Idle")
  104. return String::empty();
  105. if (process.current_state.priority == "High")
  106. return *m_high_priority_icon;
  107. if (process.current_state.priority == "Low")
  108. return *m_low_priority_icon;
  109. if (process.current_state.priority == "Normal")
  110. return *m_normal_priority_icon;
  111. return process.current_state.priority;
  112. case Column::Linear: return pretty_byte_size(process.current_state.linear);
  113. case Column::Physical: return pretty_byte_size(process.current_state.physical);
  114. case Column::CPU: return process.current_state.cpu_percent;
  115. case Column::Name: return process.current_state.name;
  116. // FIXME: It's weird that GVariant doesn't support unsigned ints. Should it?
  117. case Column::Syscalls: return (int)process.current_state.syscalls;
  118. }
  119. }
  120. return { };
  121. }
  122. void ProcessModel::update()
  123. {
  124. CFile file("/proc/all");
  125. if (!file.open(CIODevice::ReadOnly)) {
  126. fprintf(stderr, "ProcessManager: Failed to open /proc/all: %s\n", file.error_string());
  127. exit(1);
  128. return;
  129. }
  130. unsigned last_sum_nsched = 0;
  131. for (auto& it : m_processes)
  132. last_sum_nsched += it.value->current_state.nsched;
  133. HashTable<pid_t> live_pids;
  134. unsigned sum_nsched = 0;
  135. for (;;) {
  136. auto line = file.read_line(1024);
  137. if (line.is_empty())
  138. break;
  139. auto chomped = String((const char*)line.pointer(), line.size() - 1, Chomp);
  140. auto parts = chomped.split_view(',');
  141. if (parts.size() < 18)
  142. break;
  143. bool ok;
  144. pid_t pid = parts[0].to_uint(ok);
  145. ASSERT(ok);
  146. unsigned nsched = parts[1].to_uint(ok);
  147. ASSERT(ok);
  148. ProcessState state;
  149. state.pid = pid;
  150. state.nsched = nsched;
  151. unsigned uid = parts[5].to_uint(ok);
  152. ASSERT(ok);
  153. {
  154. auto it = m_usernames.find((uid_t)uid);
  155. if (it != m_usernames.end())
  156. state.user = String::format("%s", (*it).value.characters());
  157. else
  158. state.user = String::format("%u", uid);
  159. }
  160. state.priority = parts[16];
  161. state.syscalls = parts[17].to_uint(ok);
  162. ASSERT(ok);
  163. state.state = parts[7];
  164. state.name = parts[11];
  165. state.linear = parts[12].to_uint(ok);
  166. ASSERT(ok);
  167. state.physical = parts[13].to_uint(ok);
  168. ASSERT(ok);
  169. sum_nsched += nsched;
  170. {
  171. auto it = m_processes.find(pid);
  172. if (it == m_processes.end())
  173. m_processes.set(pid, make<Process>());
  174. }
  175. auto it = m_processes.find(pid);
  176. ASSERT(it != m_processes.end());
  177. (*it).value->previous_state = (*it).value->current_state;
  178. (*it).value->current_state = state;
  179. live_pids.set(pid);
  180. }
  181. m_pids.clear();
  182. Vector<pid_t, 16> pids_to_remove;
  183. for (auto& it : m_processes) {
  184. if (!live_pids.contains(it.key)) {
  185. pids_to_remove.append(it.key);
  186. continue;
  187. }
  188. auto& process = *it.value;
  189. dword nsched_diff = process.current_state.nsched - process.previous_state.nsched;
  190. process.current_state.cpu_percent = ((float)nsched_diff * 100) / (float)(sum_nsched - last_sum_nsched);
  191. m_pids.append(it.key);
  192. }
  193. for (auto pid : pids_to_remove)
  194. m_processes.remove(pid);
  195. did_update();
  196. }