AffinityPanel.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442
  1. /*
  2. htop - AffinityPanel.c
  3. (C) 2004-2011 Hisham H. Muhammad
  4. Released under the GNU GPLv2+, see the COPYING file
  5. in the source distribution for its full text.
  6. */
  7. #include "config.h" // IWYU pragma: keep
  8. #include "AffinityPanel.h"
  9. #include <assert.h>
  10. #include <stdbool.h>
  11. #include <stdlib.h>
  12. #include <string.h>
  13. #include "CRT.h"
  14. #include "FunctionBar.h"
  15. #include "Object.h"
  16. #include "ProvideCurses.h"
  17. #include "RichString.h"
  18. #include "Settings.h"
  19. #include "Vector.h"
  20. #include "XUtils.h"
  21. #ifdef HAVE_LIBHWLOC
  22. #include <hwloc.h>
  23. #include <hwloc/bitmap.h>
  24. #endif
  25. typedef struct MaskItem_ {
  26. Object super;
  27. char* text;
  28. char* indent; /* used also as an condition whether this is a tree node */
  29. int value; /* tri-state: 0 - off, 1 - some set, 2 - all set */
  30. int sub_tree; /* tri-state: 0 - no sub-tree, 1 - open sub-tree, 2 - closed sub-tree */
  31. Vector* children;
  32. #ifdef HAVE_LIBHWLOC
  33. bool ownCpuset;
  34. hwloc_bitmap_t cpuset;
  35. #else
  36. int cpu;
  37. #endif
  38. } MaskItem;
  39. static void MaskItem_delete(Object* cast) {
  40. MaskItem* this = (MaskItem*) cast;
  41. free(this->text);
  42. free(this->indent);
  43. Vector_delete(this->children);
  44. #ifdef HAVE_LIBHWLOC
  45. if (this->ownCpuset)
  46. hwloc_bitmap_free(this->cpuset);
  47. #endif
  48. free(this);
  49. }
  50. static void MaskItem_display(const Object* cast, RichString* out) {
  51. const MaskItem* this = (const MaskItem*)cast;
  52. assert (this != NULL);
  53. RichString_appendAscii(out, CRT_colors[CHECK_BOX], "[");
  54. if (this->value == 2) {
  55. RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x");
  56. } else if (this->value == 1) {
  57. RichString_appendAscii(out, CRT_colors[CHECK_MARK], "o");
  58. } else {
  59. RichString_appendAscii(out, CRT_colors[CHECK_MARK], " ");
  60. }
  61. RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]");
  62. RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
  63. if (this->indent) {
  64. RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->indent);
  65. RichString_appendWide(out, CRT_colors[PROCESS_TREE],
  66. this->sub_tree == 2
  67. ? CRT_treeStr[TREE_STR_OPEN]
  68. : CRT_treeStr[TREE_STR_SHUT]);
  69. RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " ");
  70. }
  71. RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->text);
  72. }
  73. static const ObjectClass MaskItem_class = {
  74. .display = MaskItem_display,
  75. .delete = MaskItem_delete
  76. };
  77. #ifdef HAVE_LIBHWLOC
  78. static MaskItem* MaskItem_newMask(const char* text, const char* indent, hwloc_bitmap_t cpuset, bool owner) {
  79. MaskItem* this = AllocThis(MaskItem);
  80. this->text = xStrdup(text);
  81. this->indent = xStrdup(indent); /* nonnull for tree node */
  82. this->value = 0;
  83. this->ownCpuset = owner;
  84. this->cpuset = cpuset;
  85. this->sub_tree = hwloc_bitmap_weight(cpuset) > 1 ? 1 : 0;
  86. this->children = Vector_new(Class(MaskItem), true, DEFAULT_SIZE);
  87. return this;
  88. }
  89. #endif
  90. static MaskItem* MaskItem_newSingleton(const char* text, int cpu, bool isSet) {
  91. MaskItem* this = AllocThis(MaskItem);
  92. this->text = xStrdup(text);
  93. this->indent = NULL; /* not a tree node */
  94. this->sub_tree = 0;
  95. this->children = Vector_new(Class(MaskItem), true, DEFAULT_SIZE);
  96. #ifdef HAVE_LIBHWLOC
  97. this->ownCpuset = true;
  98. this->cpuset = hwloc_bitmap_alloc();
  99. hwloc_bitmap_set(this->cpuset, cpu);
  100. #else
  101. this->cpu = cpu;
  102. #endif
  103. this->value = isSet ? 2 : 0;
  104. return this;
  105. }
  106. typedef struct AffinityPanel_ {
  107. Panel super;
  108. Machine* host;
  109. bool topoView;
  110. Vector* cpuids;
  111. unsigned width;
  112. #ifdef HAVE_LIBHWLOC
  113. MaskItem* topoRoot;
  114. hwloc_const_cpuset_t allCpuset;
  115. hwloc_bitmap_t workCpuset;
  116. #endif
  117. } AffinityPanel;
  118. static void AffinityPanel_delete(Object* cast) {
  119. AffinityPanel* this = (AffinityPanel*) cast;
  120. Panel* super = (Panel*) this;
  121. Panel_done(super);
  122. Vector_delete(this->cpuids);
  123. #ifdef HAVE_LIBHWLOC
  124. hwloc_bitmap_free(this->workCpuset);
  125. MaskItem_delete((Object*) this->topoRoot);
  126. #endif
  127. free(this);
  128. }
  129. #ifdef HAVE_LIBHWLOC
  130. static void AffinityPanel_updateItem(AffinityPanel* this, MaskItem* item) {
  131. Panel* super = (Panel*) this;
  132. item->value = hwloc_bitmap_isincluded(item->cpuset, this->workCpuset) ? 2 :
  133. hwloc_bitmap_intersects(item->cpuset, this->workCpuset) ? 1 : 0;
  134. Panel_add(super, (Object*) item);
  135. }
  136. static void AffinityPanel_updateTopo(AffinityPanel* this, MaskItem* item) {
  137. AffinityPanel_updateItem(this, item);
  138. if (item->sub_tree == 2)
  139. return;
  140. for (int i = 0; i < Vector_size(item->children); i++)
  141. AffinityPanel_updateTopo(this, (MaskItem*) Vector_get(item->children, i));
  142. }
  143. #endif
  144. static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) {
  145. Panel* super = (Panel*) this;
  146. FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : "");
  147. int oldSelected = Panel_getSelectedIndex(super);
  148. Panel_prune(super);
  149. #ifdef HAVE_LIBHWLOC
  150. if (this->topoView) {
  151. AffinityPanel_updateTopo(this, this->topoRoot);
  152. } else {
  153. for (int i = 0; i < Vector_size(this->cpuids); i++) {
  154. AffinityPanel_updateItem(this, (MaskItem*) Vector_get(this->cpuids, i));
  155. }
  156. }
  157. #else
  158. Panel_splice(super, this->cpuids);
  159. #endif
  160. if (keepSelected)
  161. Panel_setSelected(super, oldSelected);
  162. super->needsRedraw = true;
  163. }
  164. static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) {
  165. AffinityPanel* this = (AffinityPanel*) super;
  166. HandlerResult result = IGNORED;
  167. MaskItem* selected = (MaskItem*) Panel_getSelected(super);
  168. bool keepSelected = true;
  169. switch (ch) {
  170. case KEY_MOUSE:
  171. case KEY_RECLICK:
  172. case ' ':
  173. #ifdef HAVE_LIBHWLOC
  174. if (selected->value == 2) {
  175. /* Item was selected, so remove this mask from the top cpuset. */
  176. hwloc_bitmap_andnot(this->workCpuset, this->workCpuset, selected->cpuset);
  177. selected->value = 0;
  178. } else {
  179. /* Item was not or only partial selected, so set all bits from this object
  180. in the top cpuset. */
  181. hwloc_bitmap_or(this->workCpuset, this->workCpuset, selected->cpuset);
  182. selected->value = 2;
  183. }
  184. #else
  185. selected->value = selected->value ? 0 : 2; /* toggle between 0 and 2 */
  186. #endif
  187. result = HANDLED;
  188. break;
  189. #ifdef HAVE_LIBHWLOC
  190. case KEY_F(1):
  191. hwloc_bitmap_copy(this->workCpuset, this->allCpuset);
  192. result = HANDLED;
  193. break;
  194. case KEY_F(2):
  195. this->topoView = !this->topoView;
  196. keepSelected = false;
  197. result = HANDLED;
  198. break;
  199. case KEY_F(3):
  200. case '-':
  201. case '+':
  202. if (selected->sub_tree)
  203. selected->sub_tree = 1 + !(selected->sub_tree - 1); /* toggle between 1 and 2 */
  204. result = HANDLED;
  205. break;
  206. #endif
  207. case 0x0a:
  208. case 0x0d:
  209. case KEY_ENTER:
  210. result = BREAK_LOOP;
  211. break;
  212. }
  213. if (HANDLED == result)
  214. AffinityPanel_update(this, keepSelected);
  215. return result;
  216. }
  217. #ifdef HAVE_LIBHWLOC
  218. static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
  219. const char* type_name = hwloc_obj_type_string(obj->type);
  220. const char* index_prefix = "#";
  221. unsigned depth = obj->depth;
  222. unsigned index = obj->logical_index;
  223. size_t off = 0, left = 10 * depth;
  224. char buf[64], indent_buf[left + 1];
  225. if (obj->type == HWLOC_OBJ_PU) {
  226. index = Settings_cpuId(this->host->settings, obj->os_index);
  227. type_name = "CPU";
  228. index_prefix = "";
  229. }
  230. indent_buf[0] = '\0';
  231. if (depth > 0) {
  232. for (unsigned i = 1; i < depth; i++) {
  233. xSnprintf(&indent_buf[off], left, "%s ", (indent & (1U << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
  234. size_t len = strlen(&indent_buf[off]);
  235. off += len;
  236. left -= len;
  237. }
  238. xSnprintf(&indent_buf[off], left, "%s",
  239. obj->next_sibling ? CRT_treeStr[TREE_STR_RTEE] : CRT_treeStr[TREE_STR_BEND]);
  240. // Uncomment when further appending to indent_buf
  241. //size_t len = strlen(&indent_buf[off]);
  242. //off += len;
  243. //left -= len;
  244. }
  245. xSnprintf(buf, sizeof(buf), "%s %s%u", type_name, index_prefix, index);
  246. MaskItem* item = MaskItem_newMask(buf, indent_buf, obj->complete_cpuset, false);
  247. if (parent)
  248. Vector_add(parent->children, item);
  249. if (item->sub_tree && parent && parent->sub_tree == 1) {
  250. /* if obj is fully included or fully excluded, collapse the item */
  251. hwloc_bitmap_t result = hwloc_bitmap_alloc();
  252. hwloc_bitmap_and(result, obj->complete_cpuset, this->workCpuset);
  253. int weight = hwloc_bitmap_weight(result);
  254. hwloc_bitmap_free(result);
  255. if (weight == 0 || weight == (hwloc_bitmap_weight(this->workCpuset) + hwloc_bitmap_weight(obj->complete_cpuset))) {
  256. item->sub_tree = 2;
  257. }
  258. }
  259. /* "[x] " + "|- " * depth + ("- ")?(if root node) + name */
  260. unsigned width = 4 + 3 * depth + (2 * !depth) + strlen(buf);
  261. if (width > this->width) {
  262. this->width = width;
  263. }
  264. return item;
  265. }
  266. static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
  267. MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent);
  268. if (obj->next_sibling) {
  269. indent |= (1U << obj->depth);
  270. } else {
  271. indent &= ~(1U << obj->depth);
  272. }
  273. for (unsigned i = 0; i < obj->arity; i++) {
  274. AffinityPanel_buildTopology(this, obj->children[i], indent, item);
  275. }
  276. return parent == NULL ? item : NULL;
  277. }
  278. #endif
  279. const PanelClass AffinityPanel_class = {
  280. .super = {
  281. .extends = Class(Panel),
  282. .delete = AffinityPanel_delete
  283. },
  284. .eventHandler = AffinityPanel_eventHandler
  285. };
  286. static const char* const AffinityPanelFunctions[] = {
  287. "Set ",
  288. "Cancel ",
  289. #ifdef HAVE_LIBHWLOC
  290. "All",
  291. "Topology",
  292. " ",
  293. #endif
  294. NULL
  295. };
  296. static const char* const AffinityPanelKeys[] = {"Enter", "Esc", "F1", "F2", "F3"};
  297. static const int AffinityPanelEvents[] = {13, 27, KEY_F(1), KEY_F(2), KEY_F(3)};
  298. Panel* AffinityPanel_new(Machine* host, const Affinity* affinity, int* width) {
  299. AffinityPanel* this = AllocThis(AffinityPanel);
  300. Panel* super = (Panel*) this;
  301. Panel_init(super, 1, 1, 1, 1, Class(MaskItem), false, FunctionBar_new(AffinityPanelFunctions, AffinityPanelKeys, AffinityPanelEvents));
  302. this->host = host;
  303. /* defaults to 15, this also includes the gap between the panels,
  304. * but this will be added by the caller */
  305. this->width = 14;
  306. this->cpuids = Vector_new(Class(MaskItem), true, DEFAULT_SIZE);
  307. #ifdef HAVE_LIBHWLOC
  308. this->topoView = host->settings->topologyAffinity;
  309. #else
  310. this->topoView = false;
  311. #endif
  312. #ifdef HAVE_LIBHWLOC
  313. this->allCpuset = hwloc_topology_get_complete_cpuset(host->topology);
  314. this->workCpuset = hwloc_bitmap_alloc();
  315. #endif
  316. Panel_setHeader(super, "Use CPUs:");
  317. unsigned int curCpu = 0;
  318. for (unsigned int i = 0; i < host->existingCPUs; i++) {
  319. if (!Machine_isCPUonline(host, i))
  320. continue;
  321. char number[16];
  322. xSnprintf(number, 9, "CPU %d", Settings_cpuId(host->settings, i));
  323. unsigned cpu_width = 4 + strlen(number);
  324. if (cpu_width > this->width) {
  325. this->width = cpu_width;
  326. }
  327. bool isSet = false;
  328. if (curCpu < affinity->used && affinity->cpus[curCpu] == i) {
  329. #ifdef HAVE_LIBHWLOC
  330. hwloc_bitmap_set(this->workCpuset, i);
  331. #endif
  332. isSet = true;
  333. curCpu++;
  334. }
  335. MaskItem* cpuItem = MaskItem_newSingleton(number, i, isSet);
  336. Vector_add(this->cpuids, (Object*) cpuItem);
  337. }
  338. #ifdef HAVE_LIBHWLOC
  339. this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(host->topology), 0, NULL);
  340. #endif
  341. if (width) {
  342. *width = this->width;
  343. }
  344. AffinityPanel_update(this, false);
  345. return super;
  346. }
  347. Affinity* AffinityPanel_getAffinity(Panel* super, Machine* host) {
  348. const AffinityPanel* this = (AffinityPanel*) super;
  349. Affinity* affinity = Affinity_new(host);
  350. #ifdef HAVE_LIBHWLOC
  351. int i;
  352. hwloc_bitmap_foreach_begin(i, this->workCpuset)
  353. Affinity_add(affinity, (unsigned)i);
  354. hwloc_bitmap_foreach_end();
  355. #else
  356. for (int i = 0; i < Vector_size(this->cpuids); i++) {
  357. const MaskItem* item = (const MaskItem*)Vector_get(this->cpuids, i);
  358. if (item->value) {
  359. Affinity_add(affinity, item->cpu);
  360. }
  361. }
  362. #endif
  363. return affinity;
  364. }