ProcessModel.cpp 7.6 KB

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