summaryrefslogtreecommitdiffstats
path: root/AffinityPanel.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:00:29 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:00:29 +0000
commit52e25213825024b8bb446eb26b03bedc9d5c2103 (patch)
treeda70bf44b2423f6f8e9a070c063fed79d190b489 /AffinityPanel.c
parentInitial commit. (diff)
downloadhtop-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.c442
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;
+}