mirror of
https://github.com/xzeldon/htop.git
synced 2024-12-25 15:25:45 +00:00
443 lines
12 KiB
C
443 lines
12 KiB
C
/*
|
|
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;
|
|
}
|