ProcessModel.cpp 8.1 KB

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