/* htop - AffinityPanel.c (C) 2004-2011 Hisham H. Muhammad Released under the GNU GPLv2, see the COPYING file in the source distribution for its full text. */ #include "config.h" // IWYU pragma: keep #include "AffinityPanel.h" #include #include #include #include #include "CRT.h" #include "FunctionBar.h" #include "Object.h" #include "ProvideCurses.h" #include "RichString.h" #include "Settings.h" #include "Vector.h" #include "XUtils.h" #ifdef HAVE_LIBHWLOC #include #include #endif typedef struct MaskItem_ { Object super; char* text; char* indent; /* used also as an condition whether this is a tree node */ int value; /* tri-state: 0 - off, 1 - some set, 2 - all set */ int sub_tree; /* tri-state: 0 - no sub-tree, 1 - open sub-tree, 2 - closed sub-tree */ Vector* children; #ifdef HAVE_LIBHWLOC bool ownCpuset; hwloc_bitmap_t cpuset; #else int cpu; #endif } MaskItem; static void MaskItem_delete(Object* cast) { MaskItem* this = (MaskItem*) cast; free(this->text); free(this->indent); Vector_delete(this->children); #ifdef HAVE_LIBHWLOC if (this->ownCpuset) hwloc_bitmap_free(this->cpuset); #endif free(this); } static void MaskItem_display(const Object* cast, RichString* out) { const MaskItem* this = (const MaskItem*)cast; assert (this != NULL); RichString_appendAscii(out, CRT_colors[CHECK_BOX], "["); if (this->value == 2) { RichString_appendAscii(out, CRT_colors[CHECK_MARK], "x"); } else if (this->value == 1) { RichString_appendAscii(out, CRT_colors[CHECK_MARK], "o"); } else { RichString_appendAscii(out, CRT_colors[CHECK_MARK], " "); } RichString_appendAscii(out, CRT_colors[CHECK_BOX], "]"); RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " "); if (this->indent) { RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->indent); RichString_appendWide(out, CRT_colors[PROCESS_TREE], this->sub_tree == 2 ? CRT_treeStr[TREE_STR_OPEN] : CRT_treeStr[TREE_STR_SHUT]); RichString_appendAscii(out, CRT_colors[CHECK_TEXT], " "); } RichString_appendWide(out, CRT_colors[CHECK_TEXT], this->text); } static const ObjectClass MaskItem_class = { .display = MaskItem_display, .delete = MaskItem_delete }; #ifdef HAVE_LIBHWLOC static MaskItem* MaskItem_newMask(const char* text, const char* indent, hwloc_bitmap_t cpuset, bool owner) { MaskItem* this = AllocThis(MaskItem); this->text = xStrdup(text); this->indent = xStrdup(indent); /* nonnull for tree node */ this->value = 0; this->ownCpuset = owner; this->cpuset = cpuset; this->sub_tree = hwloc_bitmap_weight(cpuset) > 1 ? 1 : 0; this->children = Vector_new(Class(MaskItem), true, DEFAULT_SIZE); return this; } #endif static MaskItem* MaskItem_newSingleton(const char* text, int cpu, bool isSet) { MaskItem* this = AllocThis(MaskItem); this->text = xStrdup(text); this->indent = NULL; /* not a tree node */ this->sub_tree = 0; this->children = Vector_new(Class(MaskItem), true, DEFAULT_SIZE); #ifdef HAVE_LIBHWLOC this->ownCpuset = true; this->cpuset = hwloc_bitmap_alloc(); hwloc_bitmap_set(this->cpuset, cpu); #else this->cpu = cpu; #endif this->value = isSet ? 2 : 0; return this; } typedef struct AffinityPanel_ { Panel super; ProcessList* pl; bool topoView; Vector* cpuids; unsigned width; #ifdef HAVE_LIBHWLOC MaskItem* topoRoot; hwloc_const_cpuset_t allCpuset; hwloc_bitmap_t workCpuset; #endif } AffinityPanel; static void AffinityPanel_delete(Object* cast) { AffinityPanel* this = (AffinityPanel*) cast; Panel* super = (Panel*) this; Panel_done(super); Vector_delete(this->cpuids); #ifdef HAVE_LIBHWLOC hwloc_bitmap_free(this->workCpuset); MaskItem_delete((Object*) this->topoRoot); #endif free(this); } #ifdef HAVE_LIBHWLOC static void AffinityPanel_updateItem(AffinityPanel* this, MaskItem* item) { Panel* super = (Panel*) this; item->value = hwloc_bitmap_isincluded(item->cpuset, this->workCpuset) ? 2 : hwloc_bitmap_intersects(item->cpuset, this->workCpuset) ? 1 : 0; Panel_add(super, (Object*) item); } static void AffinityPanel_updateTopo(AffinityPanel* this, MaskItem* item) { AffinityPanel_updateItem(this, item); if (item->sub_tree == 2) return; for (int i = 0; i < Vector_size(item->children); i++) AffinityPanel_updateTopo(this, (MaskItem*) Vector_get(item->children, i)); } #endif static void AffinityPanel_update(AffinityPanel* this, bool keepSelected) { Panel* super = (Panel*) this; FunctionBar_setLabel(super->currentBar, KEY_F(3), this->topoView ? "Collapse/Expand" : ""); int oldSelected = Panel_getSelectedIndex(super); Panel_prune(super); #ifdef HAVE_LIBHWLOC if (this->topoView) { AffinityPanel_updateTopo(this, this->topoRoot); } else { for (int i = 0; i < Vector_size(this->cpuids); i++) { AffinityPanel_updateItem(this, (MaskItem*) Vector_get(this->cpuids, i)); } } #else Panel_splice(super, this->cpuids); #endif if (keepSelected) Panel_setSelected(super, oldSelected); super->needsRedraw = true; } static HandlerResult AffinityPanel_eventHandler(Panel* super, int ch) { AffinityPanel* this = (AffinityPanel*) super; HandlerResult result = IGNORED; MaskItem* selected = (MaskItem*) Panel_getSelected(super); bool keepSelected = true; switch(ch) { case KEY_MOUSE: case KEY_RECLICK: case ' ': #ifdef HAVE_LIBHWLOC if (selected->value == 2) { /* Item was selected, so remove this mask from the top cpuset. */ hwloc_bitmap_andnot(this->workCpuset, this->workCpuset, selected->cpuset); selected->value = 0; } else { /* Item was not or only partial selected, so set all bits from this object in the top cpuset. */ hwloc_bitmap_or(this->workCpuset, this->workCpuset, selected->cpuset); selected->value = 2; } #else selected->value = selected->value ? 0 : 2; /* toggle between 0 and 2 */ #endif result = HANDLED; break; #ifdef HAVE_LIBHWLOC case KEY_F(1): hwloc_bitmap_copy(this->workCpuset, this->allCpuset); result = HANDLED; break; case KEY_F(2): this->topoView = !this->topoView; keepSelected = false; result = HANDLED; break; case KEY_F(3): case '-': case '+': if (selected->sub_tree) selected->sub_tree = 1 + !(selected->sub_tree - 1); /* toggle between 1 and 2 */ result = HANDLED; break; #endif case 0x0a: case 0x0d: case KEY_ENTER: result = BREAK_LOOP; break; } if (HANDLED == result) AffinityPanel_update(this, keepSelected); return result; } #ifdef HAVE_LIBHWLOC static MaskItem* AffinityPanel_addObject(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) { const char* type_name = hwloc_obj_type_string(obj->type); const char* index_prefix = "#"; unsigned depth = obj->depth; unsigned index = obj->logical_index; size_t off = 0, left = 10 * depth; char buf[64], indent_buf[left + 1]; if (obj->type == HWLOC_OBJ_PU) { index = Settings_cpuId(this->pl->settings, obj->os_index); type_name = "CPU"; index_prefix = ""; } indent_buf[0] = '\0'; if (depth > 0) { for (unsigned i = 1; i < depth; i++) { xSnprintf(&indent_buf[off], left, "%s ", (indent & (1U << i)) ? CRT_treeStr[TREE_STR_VERT] : " "); size_t len = strlen(&indent_buf[off]); off += len; left -= len; } xSnprintf(&indent_buf[off], left, "%s", obj->next_sibling ? CRT_treeStr[TREE_STR_RTEE] : CRT_treeStr[TREE_STR_BEND]); // Uncomment when further appending to indent_buf //size_t len = strlen(&indent_buf[off]); //off += len; //left -= len; } xSnprintf(buf, sizeof(buf), "%s %s%u", type_name, index_prefix, index); MaskItem* item = MaskItem_newMask(buf, indent_buf, obj->complete_cpuset, false); if (parent) Vector_add(parent->children, item); if (item->sub_tree && parent && parent->sub_tree == 1) { /* if obj is fully included or fully excluded, collapse the item */ hwloc_bitmap_t result = hwloc_bitmap_alloc(); hwloc_bitmap_and(result, obj->complete_cpuset, this->workCpuset); int weight = hwloc_bitmap_weight(result); hwloc_bitmap_free(result); if (weight == 0 || weight == (hwloc_bitmap_weight(this->workCpuset) + hwloc_bitmap_weight(obj->complete_cpuset))) { item->sub_tree = 2; } } /* "[x] " + "|- " * depth + ("- ")?(if root node) + name */ unsigned width = 4 + 3 * depth + (2 * !depth) + strlen(buf); if (width > this->width) { this->width = width; } return item; } static MaskItem* AffinityPanel_buildTopology(AffinityPanel* this, hwloc_obj_t obj, unsigned indent, MaskItem* parent) { MaskItem* item = AffinityPanel_addObject(this, obj, indent, parent); if (obj->next_sibling) { indent |= (1U << obj->depth); } else { indent &= ~(1U << obj->depth); } for (unsigned i = 0; i < obj->arity; i++) { AffinityPanel_buildTopology(this, obj->children[i], indent, item); } return parent == NULL ? item : NULL; } #endif const PanelClass AffinityPanel_class = { .super = { .extends = Class(Panel), .delete = AffinityPanel_delete }, .eventHandler = AffinityPanel_eventHandler }; static const char* const AffinityPanelFunctions[] = { "Set ", "Cancel ", #ifdef HAVE_LIBHWLOC "All", "Topology", " ", #endif NULL }; static const char* const AffinityPanelKeys[] = {"Enter", "Esc", "F1", "F2", "F3"}; static const int AffinityPanelEvents[] = {13, 27, KEY_F(1), KEY_F(2), KEY_F(3)}; Panel* AffinityPanel_new(ProcessList* pl, const Affinity* affinity, int* width) { AffinityPanel* this = AllocThis(AffinityPanel); Panel* super = (Panel*) this; Panel_init(super, 1, 1, 1, 1, Class(MaskItem), false, FunctionBar_new(AffinityPanelFunctions, AffinityPanelKeys, AffinityPanelEvents)); this->pl = pl; /* defaults to 15, this also includes the gap between the panels, * but this will be added by the caller */ this->width = 14; this->cpuids = Vector_new(Class(MaskItem), true, DEFAULT_SIZE); #ifdef HAVE_LIBHWLOC this->topoView = pl->settings->topologyAffinity; #else this->topoView = false; #endif #ifdef HAVE_LIBHWLOC this->allCpuset = hwloc_topology_get_complete_cpuset(pl->topology); this->workCpuset = hwloc_bitmap_alloc(); #endif Panel_setHeader(super, "Use CPUs:"); unsigned int curCpu = 0; for (unsigned int i = 0; i < pl->existingCPUs; i++) { /* TODO: skip offline CPUs */ char number[16]; xSnprintf(number, 9, "CPU %d", Settings_cpuId(pl->settings, i)); unsigned cpu_width = 4 + strlen(number); if (cpu_width > this->width) { this->width = cpu_width; } bool isSet = false; if (curCpu < affinity->used && affinity->cpus[curCpu] == i) { #ifdef HAVE_LIBHWLOC hwloc_bitmap_set(this->workCpuset, i); #endif isSet = true; curCpu++; } MaskItem* cpuItem = MaskItem_newSingleton(number, i, isSet); Vector_add(this->cpuids, (Object*) cpuItem); } #ifdef HAVE_LIBHWLOC this->topoRoot = AffinityPanel_buildTopology(this, hwloc_get_root_obj(pl->topology), 0, NULL); #endif if (width) { *width = this->width; } AffinityPanel_update(this, false); return super; } Affinity* AffinityPanel_getAffinity(Panel* super, ProcessList* pl) { const AffinityPanel* this = (AffinityPanel*) super; Affinity* affinity = Affinity_new(pl); #ifdef HAVE_LIBHWLOC unsigned int i; hwloc_bitmap_foreach_begin(i, this->workCpuset) Affinity_add(affinity, i); hwloc_bitmap_foreach_end(); #else for (unsigned int i = 0; i < this->pl->existingCPUs; i++) { /* TODO: skip offline CPUs */ const MaskItem* item = (const MaskItem*)Vector_get(this->cpuids, i); if (item->value) { Affinity_add(affinity, item->cpu); } } #endif return affinity; }