AffinityPanel.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453
  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. Vector_delete(this->cpuids);
  121. #ifdef HAVE_LIBHWLOC
  122. hwloc_bitmap_free(this->workCpuset);
  123. MaskItem_delete((Object*) this->topoRoot);
  124. #endif
  125. Panel_done(&this->super);
  126. free(this);
  127. }
  128. #ifdef HAVE_LIBHWLOC
  129. static void AffinityPanel_updateItem(AffinityPanel* this, MaskItem* item) {
  130. Panel* super = &this->super;
  131. item->value = hwloc_bitmap_isincluded(item->cpuset, this->workCpuset) ? 2 :
  132. hwloc_bitmap_intersects(item->cpuset, this->workCpuset) ? 1 : 0;
  133. Panel_add(super, (Object*) item);
  134. }
  135. static void AffinityPanel_updateTopo(AffinityPanel* this, MaskItem* item) {
  136. AffinityPanel_updateItem(this, item);
  137. if (item->sub_tree == 2)
  138. return;
  139. for (int i = 0; i < Vector_size(item->children); i++)
  140. AffinityPanel_updateTopo(this, (MaskItem*) Vector_get(item->children, i));
  141. }
  142. #endif
  143. static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) {
  144. Panel* super = &this->super;
  145. FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : "");
  146. int oldSelected = Panel_getSelectedIndex(super);
  147. Panel_prune(super);
  148. #ifdef HAVE_LIBHWLOC
  149. if (this->topoView) {
  150. AffinityPanel_updateTopo(this, this->topoRoot);
  151. } else {
  152. for (int i = 0; i < Vector_size(this->cpuids); i++) {
  153. AffinityPanel_updateItem(this, (MaskItem*) Vector_get(this->cpuids, i));
  154. }
  155. }
  156. #else
  157. Panel_splice(super, this->cpuids);
  158. #endif
  159. if (keepSelected)
  160. Panel_setSelected(super, oldSelected);
  161. super->needsRedraw = true;
  162. }
  163. static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) {
  164. AffinityPanel* this = (AffinityPanel*) super;
  165. HandlerResult result = IGNORED;
  166. MaskItem* selected = (MaskItem*) Panel_getSelected(super);
  167. bool keepSelected = true;
  168. switch (ch) {
  169. case KEY_MOUSE:
  170. case KEY_RECLICK:
  171. case ' ':
  172. if (!selected) {
  173. return result;
  174. }
  175. #ifdef HAVE_LIBHWLOC
  176. if (selected->value == 2) {
  177. /* Item was selected, so remove this mask from the top cpuset. */
  178. hwloc_bitmap_andnot(this->workCpuset, this->workCpuset, selected->cpuset);
  179. selected->value = 0;
  180. } else {
  181. /* Item was not or only partial selected, so set all bits from this object
  182. in the top cpuset. */
  183. hwloc_bitmap_or(this->workCpuset, this->workCpuset, selected->cpuset);
  184. selected->value = 2;
  185. }
  186. #else
  187. selected->value = selected->value ? 0 : 2; /* toggle between 0 and 2 */
  188. #endif
  189. result = HANDLED;
  190. break;
  191. #ifdef HAVE_LIBHWLOC
  192. case KEY_F(1):
  193. hwloc_bitmap_copy(this->workCpuset, this->allCpuset);
  194. result = HANDLED;
  195. break;
  196. case KEY_F(2):
  197. this->topoView = !this->topoView;
  198. keepSelected = false;
  199. result = HANDLED;
  200. break;
  201. case KEY_F(3):
  202. case '-':
  203. case '+':
  204. if (!selected) {
  205. break;
  206. }
  207. if (selected->sub_tree) {
  208. selected->sub_tree = 1 + !(selected->sub_tree - 1); /* toggle between 1 and 2 */
  209. }
  210. result = HANDLED;
  211. break;
  212. #endif
  213. case 0x0a:
  214. case 0x0d:
  215. case KEY_ENTER:
  216. result = BREAK_LOOP;
  217. break;
  218. }
  219. if (HANDLED == result)
  220. AffinityPanel_update(this, keepSelected);
  221. return result;
  222. }
  223. #ifdef HAVE_LIBHWLOC
  224. static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
  225. const char* type_name = hwloc_obj_type_string(obj->type);
  226. const char* index_prefix = "#";
  227. unsigned depth = obj->depth;
  228. unsigned index = obj->logical_index;
  229. size_t off = 0, left = 10 * depth;
  230. char buf[64], indent_buf[left + 1];
  231. if (obj->type == HWLOC_OBJ_PU) {
  232. index = Settings_cpuId(this->host->settings, obj->os_index);
  233. type_name = "CPU";
  234. index_prefix = "";
  235. }
  236. indent_buf[0] = '\0';
  237. if (depth > 0) {
  238. for (unsigned i = 1; i < depth; i++) {
  239. xSnprintf(&indent_buf[off], left, "%s ", (indent & (1U << i)) ? CRT_treeStr[TREE_STR_VERT] : " ");
  240. size_t len = strlen(&indent_buf[off]);
  241. off += len;
  242. left -= len;
  243. }
  244. xSnprintf(&indent_buf[off], left, "%s",
  245. obj->next_sibling ? CRT_treeStr[TREE_STR_RTEE] : CRT_treeStr[TREE_STR_BEND]);
  246. // Uncomment when further appending to indent_buf
  247. //size_t len = strlen(&indent_buf[off]);
  248. //off += len;
  249. //left -= len;
  250. }
  251. xSnprintf(buf, sizeof(buf), "%s %s%u", type_name, index_prefix, index);
  252. MaskItem* item = MaskItem_newMask(buf, indent_buf, obj->complete_cpuset, false);
  253. if (parent)
  254. Vector_add(parent->children, item);
  255. if (item->sub_tree && parent && parent->sub_tree == 1) {
  256. /* if obj is fully included or fully excluded, collapse the item */
  257. hwloc_bitmap_t result = hwloc_bitmap_alloc();
  258. hwloc_bitmap_and(result, obj->complete_cpuset, this->workCpuset);
  259. int weight = hwloc_bitmap_weight(result);
  260. hwloc_bitmap_free(result);
  261. if (weight == 0 || weight == (hwloc_bitmap_weight(this->workCpuset) + hwloc_bitmap_weight(obj->complete_cpuset))) {
  262. item->sub_tree = 2;
  263. }
  264. }
  265. /* "[x] " + "|- " * depth + ("- ")?(if root node) + name */
  266. unsigned width = 4 + 3 * depth + (2 * !depth) + strlen(buf);
  267. if (width > this->width) {
  268. this->width = width;
  269. }
  270. return item;
  271. }
  272. static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) {
  273. MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent);
  274. if (obj->next_sibling) {
  275. indent |= (1U << obj->depth);
  276. } else {
  277. indent &= ~(1U << obj->depth);
  278. }
  279. for (unsigned i = 0; i < obj->arity; i++) {
  280. AffinityPanel_buildTopology(this, obj->children[i], indent, item);
  281. }
  282. return parent == NULL ? item : NULL;
  283. }
  284. #endif
  285. const PanelClass AffinityPanel_class = {
  286. .super = {
  287. .extends = Class(Panel),
  288. .delete = AffinityPanel_delete
  289. },
  290. .eventHandler = AffinityPanel_eventHandler
  291. };
  292. static const char* const AffinityPanelFunctions[] = {
  293. "Set ",
  294. "Cancel ",
  295. #ifdef HAVE_LIBHWLOC
  296. "All",
  297. "Topology",
  298. " ",
  299. #endif
  300. NULL
  301. };
  302. static const char* const AffinityPanelKeys[] = {"Enter", "Esc", "F1", "F2", "F3"};
  303. static const int AffinityPanelEvents[] = {13, 27, KEY_F(1), KEY_F(2), KEY_F(3)};
  304. Panel* AffinityPanel_new(Machine* host, const Affinity* affinity, int* width) {
  305. AffinityPanel* this = AllocThis(AffinityPanel);
  306. Panel* super = &this->super;
  307. Panel_init(super, 1, 1, 1, 1, Class(MaskItem), false, FunctionBar_new(AffinityPanelFunctions, AffinityPanelKeys, AffinityPanelEvents));
  308. this->host = host;
  309. /* defaults to 15, this also includes the gap between the panels,
  310. * but this will be added by the caller */
  311. this->width = 14;
  312. this->cpuids = Vector_new(Class(MaskItem), true, DEFAULT_SIZE);
  313. #ifdef HAVE_LIBHWLOC
  314. this->topoView = host->settings->topologyAffinity;
  315. #else
  316. this->topoView = false;
  317. #endif
  318. #ifdef HAVE_LIBHWLOC
  319. this->allCpuset = hwloc_topology_get_complete_cpuset(host->topology);
  320. this->workCpuset = hwloc_bitmap_alloc();
  321. #endif
  322. Panel_setHeader(super, "Use CPUs:");
  323. unsigned int curCpu = 0;
  324. for (unsigned int i = 0; i < host->existingCPUs; i++) {
  325. if (!Machine_isCPUonline(host, i))
  326. continue;
  327. char number[16];
  328. xSnprintf(number, 9, "CPU %d", Settings_cpuId(host->settings, i));
  329. unsigned cpu_width = 4 + strlen(number);
  330. if (cpu_width > this->width) {
  331. this->width = cpu_width;
  332. }
  333. bool isSet = false;
  334. if (curCpu < affinity->used && affinity->cpus[curCpu] == i) {
  335. #ifdef HAVE_LIBHWLOC
  336. hwloc_bitmap_set(this->workCpuset, i);
  337. #endif
  338. isSet = true;
  339. curCpu++;
  340. }
  341. MaskItem* cpuItem = MaskItem_newSingleton(number, i, isSet);
  342. Vector_add(this->cpuids, (Object*) cpuItem);
  343. }
  344. #ifdef HAVE_LIBHWLOC
  345. this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(host->topology), 0, NULL);
  346. #endif
  347. if (width) {
  348. *width = this->width;
  349. }
  350. AffinityPanel_update(this, false);
  351. return super;
  352. }
  353. Affinity* AffinityPanel_getAffinity(Panel* super, Machine* host) {
  354. const AffinityPanel* this = (AffinityPanel*) super;
  355. Affinity* affinity = Affinity_new(host);
  356. #ifdef HAVE_LIBHWLOC
  357. int i;
  358. hwloc_bitmap_foreach_begin(i, this->workCpuset)
  359. Affinity_add(affinity, (unsigned)i);
  360. hwloc_bitmap_foreach_end();
  361. #else
  362. for (int i = 0; i < Vector_size(this->cpuids); i++) {
  363. const MaskItem* item = (const MaskItem*)Vector_get(this->cpuids, i);
  364. if (item->value) {
  365. Affinity_add(affinity, item->cpu);
  366. }
  367. }
  368. #endif
  369. return affinity;
  370. }