ProcessModel.cpp 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264
  1. #include "ProcessModel.h"
  2. #include "GraphWidget.h"
  3. #include <LibCore/CFile.h>
  4. #include <fcntl.h>
  5. #include <pwd.h>
  6. #include <stdio.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:
  39. return "";
  40. case Column::PID:
  41. return "PID";
  42. case Column::State:
  43. return "State";
  44. case Column::User:
  45. return "User";
  46. case Column::Priority:
  47. return "Pr";
  48. case Column::Virtual:
  49. return "Virtual";
  50. case Column::Physical:
  51. return "Physical";
  52. case Column::CPU:
  53. return "CPU";
  54. case Column::Name:
  55. return "Name";
  56. case Column::Syscalls:
  57. return "Syscalls";
  58. default:
  59. ASSERT_NOT_REACHED();
  60. }
  61. }
  62. GModel::ColumnMetadata ProcessModel::column_metadata(int column) const
  63. {
  64. switch (column) {
  65. case Column::Icon:
  66. return { 16, TextAlignment::CenterLeft };
  67. case Column::PID:
  68. return { 32, TextAlignment::CenterRight };
  69. case Column::State:
  70. return { 75, TextAlignment::CenterLeft };
  71. case Column::Priority:
  72. return { 16, TextAlignment::CenterLeft };
  73. case Column::User:
  74. return { 50, TextAlignment::CenterLeft };
  75. case Column::Virtual:
  76. return { 65, TextAlignment::CenterRight };
  77. case Column::Physical:
  78. return { 65, TextAlignment::CenterRight };
  79. case Column::CPU:
  80. return { 32, TextAlignment::CenterRight };
  81. case Column::Name:
  82. return { 140, TextAlignment::CenterLeft };
  83. case Column::Syscalls:
  84. return { 60, TextAlignment::CenterRight };
  85. default:
  86. ASSERT_NOT_REACHED();
  87. }
  88. }
  89. static String pretty_byte_size(size_t size)
  90. {
  91. return String::format("%uK", size / 1024);
  92. }
  93. GVariant ProcessModel::data(const GModelIndex& index, Role role) const
  94. {
  95. ASSERT(is_valid(index));
  96. auto it = m_processes.find(m_pids[index.row()]);
  97. auto& process = *(*it).value;
  98. if (role == Role::Sort) {
  99. switch (index.column()) {
  100. case Column::Icon:
  101. return 0;
  102. case Column::PID:
  103. return process.current_state.pid;
  104. case Column::State:
  105. return process.current_state.state;
  106. case Column::User:
  107. return process.current_state.user;
  108. case Column::Priority:
  109. if (process.current_state.priority == "Idle")
  110. return 0;
  111. if (process.current_state.priority == "Low")
  112. return 1;
  113. if (process.current_state.priority == "Normal")
  114. return 2;
  115. if (process.current_state.priority == "High")
  116. return 3;
  117. ASSERT_NOT_REACHED();
  118. return 3;
  119. case Column::Virtual:
  120. return (int)process.current_state.virtual_size;
  121. case Column::Physical:
  122. return (int)process.current_state.physical_size;
  123. case Column::CPU:
  124. return process.current_state.cpu_percent;
  125. case Column::Name:
  126. return process.current_state.name;
  127. // FIXME: GVariant with unsigned?
  128. case Column::Syscalls:
  129. return (int)process.current_state.syscalls;
  130. }
  131. ASSERT_NOT_REACHED();
  132. return {};
  133. }
  134. if (role == Role::Display) {
  135. switch (index.column()) {
  136. case Column::Icon:
  137. return *m_generic_process_icon;
  138. case Column::PID:
  139. return process.current_state.pid;
  140. case Column::State:
  141. return process.current_state.state;
  142. case Column::User:
  143. return process.current_state.user;
  144. case Column::Priority:
  145. if (process.current_state.priority == "Idle")
  146. return String::empty();
  147. if (process.current_state.priority == "High")
  148. return *m_high_priority_icon;
  149. if (process.current_state.priority == "Low")
  150. return *m_low_priority_icon;
  151. if (process.current_state.priority == "Normal")
  152. return *m_normal_priority_icon;
  153. return process.current_state.priority;
  154. case Column::Virtual:
  155. return pretty_byte_size(process.current_state.virtual_size);
  156. case Column::Physical:
  157. return pretty_byte_size(process.current_state.physical_size);
  158. case Column::CPU:
  159. return process.current_state.cpu_percent;
  160. case Column::Name:
  161. return process.current_state.name;
  162. // FIXME: It's weird that GVariant doesn't support unsigned ints. Should it?
  163. case Column::Syscalls:
  164. return (int)process.current_state.syscalls;
  165. }
  166. }
  167. return {};
  168. }
  169. void ProcessModel::update()
  170. {
  171. m_proc_all.seek(0);
  172. unsigned last_sum_nsched = 0;
  173. for (auto& it : m_processes)
  174. last_sum_nsched += it.value->current_state.nsched;
  175. HashTable<pid_t> live_pids;
  176. unsigned sum_nsched = 0;
  177. for (;;) {
  178. auto line = m_proc_all.read_line(1024);
  179. if (line.is_empty())
  180. break;
  181. auto chomped = String((const char*)line.pointer(), line.size() - 1, Chomp);
  182. auto parts = chomped.split_view(',');
  183. if (parts.size() < 18)
  184. break;
  185. bool ok;
  186. pid_t pid = parts[0].to_uint(ok);
  187. ASSERT(ok);
  188. unsigned nsched = parts[1].to_uint(ok);
  189. ASSERT(ok);
  190. ProcessState state;
  191. state.pid = pid;
  192. state.nsched = nsched;
  193. unsigned uid = parts[5].to_uint(ok);
  194. ASSERT(ok);
  195. {
  196. auto it = m_usernames.find((uid_t)uid);
  197. if (it != m_usernames.end())
  198. state.user = String::format("%s", (*it).value.characters());
  199. else
  200. state.user = String::format("%u", uid);
  201. }
  202. state.priority = parts[16];
  203. state.syscalls = parts[17].to_uint(ok);
  204. ASSERT(ok);
  205. state.state = parts[7];
  206. state.name = parts[11];
  207. state.virtual_size = parts[12].to_uint(ok);
  208. ASSERT(ok);
  209. state.physical_size = parts[13].to_uint(ok);
  210. ASSERT(ok);
  211. sum_nsched += nsched;
  212. {
  213. auto it = m_processes.find(pid);
  214. if (it == m_processes.end())
  215. m_processes.set(pid, make<Process>());
  216. }
  217. auto it = m_processes.find(pid);
  218. ASSERT(it != m_processes.end());
  219. (*it).value->previous_state = (*it).value->current_state;
  220. (*it).value->current_state = state;
  221. live_pids.set(pid);
  222. }
  223. m_pids.clear();
  224. float total_cpu_percent = 0;
  225. Vector<pid_t, 16> pids_to_remove;
  226. for (auto& it : m_processes) {
  227. if (!live_pids.contains(it.key)) {
  228. pids_to_remove.append(it.key);
  229. continue;
  230. }
  231. auto& process = *it.value;
  232. dword nsched_diff = process.current_state.nsched - process.previous_state.nsched;
  233. process.current_state.cpu_percent = ((float)nsched_diff * 100) / (float)(sum_nsched - last_sum_nsched);
  234. if (it.key != 0) {
  235. total_cpu_percent += process.current_state.cpu_percent;
  236. m_pids.append(it.key);
  237. }
  238. }
  239. for (auto pid : pids_to_remove)
  240. m_processes.remove(pid);
  241. m_graph.add_value(total_cpu_percent);
  242. did_update();
  243. }