diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:00:29 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-28 09:00:29 +0000 |
commit | 52e25213825024b8bb446eb26b03bedc9d5c2103 (patch) | |
tree | da70bf44b2423f6f8e9a070c063fed79d190b489 /AffinityPanel.c | |
parent | Initial commit. (diff) | |
download | htop-upstream.tar.xz htop-upstream.zip |
Adding upstream version 3.2.2.upstream/3.2.2upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r-- | AffinityPanel.c | 442 |
1 files changed, 442 insertions, 0 deletions
diff --git a/AffinityPanel.c b/AffinityPanel.c new file mode 100644 index 0000000..b724397 --- /dev/null +++ b/AffinityPanel.c @@ -0,0 +1,442 @@ +/* +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 <assert.h> +#include <stdbool.h> +#include <stdlib.h> +#include <string.h> + +#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 <hwloc.h> +#include <hwloc/bitmap.h> +#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++) { + if (!ProcessList_isCPUonline(this->pl, i)) + continue; + + 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 + int i; + hwloc_bitmap_foreach_begin(i, this->workCpuset) + Affinity_add(affinity, (unsigned)i); + hwloc_bitmap_foreach_end(); + #else + for (int i = 0; i < Vector_size(this->cpuids); i++) { + const MaskItem* item = (const MaskItem*)Vector_get(this->cpuids, i); + if (item->value) { + Affinity_add(affinity, item->cpu); + } + } + #endif + + return affinity; +} |