59 Commits

Author SHA1 Message Date
5c6d7cca3e Bump version to 3.0.0beta4 2018-04-06 12:43:24 -03:00
6fe06fb7e5 Portability: make list of default screens per-platform 2018-04-06 12:41:36 -03:00
657836a2ae Minor style fixes. 2018-04-06 12:40:51 -03:00
63e1417b8c Update some field accesses to new structures 2018-04-06 12:34:57 -03:00
9b4bdfcb3e Add -t command-line flag for tree view 2018-04-06 11:14:09 -03:00
7a71f9cf32 macOS: fix the switched version test (#772) 2018-04-06 11:14:09 -03:00
c91b0938d5 Solaris: update proc state letters to reflect Solaris usage 2018-04-06 11:14:09 -03:00
4f934c977d Solaris: bump copyright in Platform.{c,h} 2018-04-06 11:14:09 -03:00
d1696b5d5d Solaris: fix a memory leak caused by calling ProcessList_getProcess twice for each LWP 2018-04-06 11:14:09 -03:00
beb47cbb12 Solaris: Implement process environment listing 2018-04-06 11:14:09 -03:00
677cac9fab Solaris: add placeholder message about environment listing 2018-04-06 11:14:09 -03:00
6790e004cd Solaris: showing a dash for the top-level process is no longer necessary 2018-04-06 11:14:09 -03:00
8677162836 Solaris: add warning about proc_walk_f callback function 2018-04-06 11:14:09 -03:00
475798e8ab Solaris: condense separate process vs lwp handling down to a single workflow 2018-04-06 11:14:09 -03:00
7689c5c3b7 Solaris: get completely out of the file handling business using libproc 2018-04-06 11:14:09 -03:00
c2ab3ac422 Solaris: Condense conditional blocks for new vs old LWPs and procs 2018-04-06 11:14:09 -03:00
61bb649e0a Solaris: remove unneeded accumulators for process and thread counting 2018-04-06 11:14:09 -03:00
b8ec1c0337 Solaris: Assorted post-LWP code cleanup 2018-04-06 11:14:09 -03:00
499af738e7 Solaris: Implement kernel thread counting 2018-04-06 11:14:09 -03:00
416ef48a62 Solaris: If a process has a running LWP, then the process is by definition running 2018-04-06 11:14:09 -03:00
b9e0da9200 Collapse current subtree pressing Backspace 2018-04-06 11:14:09 -03:00
509303323f Solaris: Implement LWP enumeration (#768)
Squashed the following commits:

* Solaris: Get LWP enumeration working
* Solaris: Make showing and hiding of kernel threads behave
* Solaris: remove usage of lwpstatus that is no longer needed
* Solaris: no discrete access to parent proc structure needed
* Solaris: Restore runtime MaxPid detection after LWP changes
* Solaris: Workaround virtual PID signal issue by shadowing kill() with a macro
* Solaris: Fix unintention double-shifting of virtual PID for LWP enumeration
* Solaris: Add LWPID to default display since LWP enumeration is also default
* Solaris: use PAGE_SIZE_KB from Process.h instead of custom definition
* Solaris: stop LWP enumeration at 1023 LWPs per proc since that is all we can handle in the virtual PID
2018-04-06 11:14:09 -03:00
421a31d6f1 OpenBSD: read Battery data
Signed-off-by: Hisham Muhammad <hisham@gobolinux.org>
2018-04-06 11:14:09 -03:00
d4c4c02b97 macOS: keep scanning thread for versions before High Sierra (#728)
Keep scanning threads for versions before High Sierra 13.0.0 and after 13.3.0.
2018-04-06 11:14:09 -03:00
1ef20d1f14 Solaris: Fix virt and resident memory sizes. Was using KiB, needed pages. 2018-04-06 11:14:09 -03:00
1ab5afa1fe New makefile targets to rebuild and clean htop headers.
`make htop-headers` will regenerate all '.h' headers in htop source for
all platforms.
`make clean-htop-headers` will delete all generated htop headers.

Because of the introduction of these two targets, I slightly changed
the style of platform-specific portions of makefile rules.
Please comment if you accept such a style, or need me to revert to old
style.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-04-06 11:14:04 -03:00
00d1fb019a Linux: change how kernel threads are detected
Use the same method that ps and top use to determine if a
process is a kernel thread on Linux: check if cmdline is empty.

Thanks to @wangqr's investigation reported here:
https://github.com/hishamhm/htop/issues/761#issuecomment-375306069

Fixes #761.
2018-04-06 11:13:30 -03:00
dd175b6881 Fix overflow for signals >= 100.
Thanks to @gzip4 for tracking this down.

Closes #764.
2018-04-06 11:13:30 -03:00
7ad9701a12 strace: increase string length 2018-04-06 11:13:30 -03:00
881fe9631a Solaris: code indentation fix 2018-04-06 11:13:30 -03:00
0de77c7075 Solaris: enough changes made to justify a copyright bump to 2018 2018-04-06 11:13:30 -03:00
0f71db9d82 Solaris: fix memory allocation for usernames (some empty usernames in 32-bit builds) 2018-04-06 11:13:30 -03:00
da4010783e Solaris: fix malloc() / free() issues with zone name handling 2018-04-06 11:13:30 -03:00
3f7622a302 Solaris: Link against libmalloc to fix various crashes 2018-04-06 11:13:30 -03:00
df5d81a881 Solaris: Import backtrace-on-abort from Linux, with minor modification for Solaris 2018-04-06 11:13:30 -03:00
6c2b3b546d Use fork/exec instead of popen to run lsof (#757)
Fixes #675
2018-04-06 11:13:30 -03:00
f48f5a10ca Use AM_CFLAGS and AM_LDFLAGS in Makefile.am (#760)
This reduces generated Makefile.in size by 74%.
(217319 bytes -> 56326 bytes)

Automake considers that <prog>_CFLAGS and <prog>_LDFLAGS are
program-specific build rules, and when such are specified, Automake
will generate additional code just to avoid the "generic" and
package-wide AM_CFLAGS or AM_LDFLAGS. (Especially for <prog>_CFLAGS,
Automake will rename generated object files to become "prog-foo.o" and
such, and it's _a lot_ of code to achieve this in Makefile.)

There's no reason for htop to rename intermediate object files. It's
better to make things simpler.

Signed-off-by: Kang-Che Sung <explorer09@gmail.com>
2018-04-06 11:13:30 -03:00
1aa23925c9 Import Solaris support (#741)
This commit adds support for Solaris, squashed from PR #741:

Summary of additions:

* Initial setup of Solaris platform directory
* Add Solaris platform into autoconf template
* Uptime and load averages
* Add dependency on libkstat
* Basic process listing
* Zone name display
* CPU detection
* Per-process memory and CPU usage parsed correctly
* Uses sysconf to discover number of CPUs, instead of more complex libkstat code
* Simple memory display working
* Reduce repetitive calls to the PAGE_SIZE macro when reading memory info
* Add Project, Contract, Task, and Pool into process properties
* Use system major()/minor() implementations and remove extraneous definition of mkdev()
* Get the STARTTIME column working properly, using the Linux implementation as a guide
2018-04-06 11:13:30 -03:00
2e1f56d934 Changed type of some integer variables to avoid overflows 2018-03-16 12:01:24 -03:00
6ee99566cd Bump version to 3.0.0beta3 2018-02-26 20:13:09 -03:00
dc6bb069f0 Update generated header 2018-02-26 20:13:09 -03:00
0169577019 Fix inttypes.h header 2018-02-26 20:13:09 -03:00
0e38be9ee7 Darwin: expose LAST_PROCESSFIELD like the other platforms 2018-02-26 20:13:09 -03:00
8e6c1e1bac Add more default screens 2018-02-26 20:13:09 -03:00
709619800f Only compute counters is process is shown 2018-02-26 20:13:09 -03:00
a72439c9b7 Implemented various performance counters 2018-02-26 20:13:09 -03:00
61e94c1b5b Add IPC performance counter for Linux 2018-02-26 20:13:09 -03:00
b9f5892593 Add perf counter object 2018-02-26 20:13:09 -03:00
267d03b6d8 configure.ac: add --enable-perfcounters 2018-02-26 20:13:09 -03:00
3b819daf82 Set default sort keys in default screens 2018-02-26 20:13:09 -03:00
d9f8cdf0a6 Add make symbols target 2018-02-26 20:13:09 -03:00
59982a188c Store .sort_key as a string 2018-02-26 20:13:09 -03:00
0800424fe6 Match iotop's screen configuration 2018-02-26 20:13:09 -03:00
b4a8f048d1 Use screen's flags when reading process data 2018-02-26 20:13:09 -03:00
2df1f61d77 Screens: Fix "New Screen" option 2018-02-26 20:13:09 -03:00
e6c98b6e8e htoprc: store screen 0's setup for improved compatibility 2018-02-26 20:13:09 -03:00
b815e4c7a3 Add support for multiple screens, switchable using Tab 2018-02-26 20:13:09 -03:00
4791050cea Begin add supporting for multiple screens 2018-02-26 20:13:09 -03:00
1edcfad874 Move responsibility for cursor placement to Panels 2018-02-26 20:13:09 -03:00
67 changed files with 3051 additions and 406 deletions

View File

@ -155,10 +155,26 @@ static bool expandCollapse(Panel* panel) {
return true;
}
static bool collapseIntoParent(Panel* panel) {
Process* p = (Process*) Panel_getSelected(panel);
if (!p) return false;
pid_t ppid = Process_getParentPid(p);
for (int i = 0; i < Panel_size(panel); i++) {
Process* q = (Process*) Panel_get(panel, i);
if (q->pid == ppid) {
q->showChildren = false;
Panel_setSelected(panel, i);
return true;
}
}
return false;
}
Htop_Reaction Action_setSortKey(Settings* settings, ProcessField sortKey) {
settings->sortKey = sortKey;
settings->direction = 1;
settings->treeView = false;
ScreenSettings* ss = settings->ss;
ss->sortKey = sortKey;
ss->direction = 1;
ss->treeView = false;
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_UPDATE_PANELHDR | HTOP_KEEP_FOLLOWING;
}
@ -166,11 +182,12 @@ static Htop_Reaction sortBy(State* st) {
Htop_Reaction reaction = HTOP_OK;
Panel* sortPanel = Panel_new(0, 0, 0, 0, true, Class(ListItem), FunctionBar_newEnterEsc("Sort ", "Cancel "));
Panel_setHeader(sortPanel, "Sort by");
ProcessField* fields = st->settings->fields;
ScreenSettings* ss = st->settings->ss;
ProcessField* fields = ss->fields;
for (int i = 0; fields[i]; i++) {
char* name = String_trim(Process_fields[fields[i]].name);
Panel_add(sortPanel, (Object*) ListItem_new(name, fields[i]));
if (fields[i] == st->settings->sortKey)
if (fields[i] == ss->sortKey)
Panel_setSelected(sortPanel, i);
free(name);
}
@ -219,8 +236,9 @@ static Htop_Reaction actionToggleProgramPath(State* st) {
}
static Htop_Reaction actionToggleTreeView(State* st) {
st->settings->treeView = !st->settings->treeView;
if (st->settings->treeView) st->settings->direction = 1;
ScreenSettings* ss = st->settings->ss;
ss->treeView = !ss->treeView;
if (ss->treeView) ss->direction = 1;
ProcessList_expandTree(st->pl);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS | HTOP_KEEP_FOLLOWING | HTOP_REDRAW_BAR | HTOP_UPDATE_PANELHDR;
}
@ -248,7 +266,7 @@ static Htop_Reaction actionLowerPriority(State* st) {
}
static Htop_Reaction actionInvertSortOrder(State* st) {
Settings_invertSortOrder(st->settings);
ScreenSettings_invertSortOrder(st->settings->ss);
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
}
@ -261,14 +279,32 @@ static Htop_Reaction actionExpandOrCollapse(State* st) {
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionCollapseIntoParent(State* st) {
if (!st->settings->ss->treeView) {
return HTOP_OK;
}
bool changed = collapseIntoParent(st->panel);
return changed ? HTOP_RECALCULATE : HTOP_OK;
}
static Htop_Reaction actionExpandCollapseOrSortColumn(State* st) {
return st->settings->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
return st->settings->ss->treeView ? actionExpandOrCollapse(st) : actionSetSortColumn(st);
}
static Htop_Reaction actionQuit() {
return HTOP_QUIT;
}
static Htop_Reaction actionNextScreen(State* st) {
Settings* settings = st->settings;
settings->ssIndex++;
if (settings->ssIndex == settings->nScreens) {
settings->ssIndex = 0;
}
settings->ss = settings->screens[settings->ssIndex];
return HTOP_REFRESH;
}
static Htop_Reaction actionSetAffinity(State* st) {
if (st->pl->cpuCount == 1)
return HTOP_OK;
@ -557,6 +593,7 @@ void Action_setBindings(Htop_Action* keys) {
keys['+'] = actionExpandOrCollapse;
keys['='] = actionExpandOrCollapse;
keys['-'] = actionExpandOrCollapse;
keys['\177'] = actionCollapseIntoParent;
keys['u'] = actionFilterByUser;
keys['F'] = Action_follow;
keys['S'] = actionSetup;
@ -572,5 +609,6 @@ void Action_setBindings(Htop_Action* keys) {
keys['U'] = actionUntagAll;
keys['c'] = actionTagAllChildren;
keys['e'] = actionShowEnvScreen;
keys['\t'] = actionNextScreen;
}

9
CRT.c
View File

@ -128,6 +128,7 @@ typedef enum ColorElements_ {
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
PANEL_EDIT,
LAST_COLORELEMENT
} ColorElements;
@ -232,6 +233,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Magenta,Black),
[CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_MONOCHROME] = {
[RESET_COLOR] = A_NORMAL,
@ -291,6 +293,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = A_BOLD,
[CPU_STEAL] = A_REVERSE,
[CPU_GUEST] = A_REVERSE,
[PANEL_EDIT] = A_BOLD,
},
[COLORSCHEME_BLACKONWHITE] = {
[RESET_COLOR] = ColorPair(Black,White),
@ -350,6 +353,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,White),
[CPU_STEAL] = ColorPair(Cyan,White),
[CPU_GUEST] = ColorPair(Cyan,White),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_LIGHTTERMINAL] = {
[RESET_COLOR] = ColorPair(Black,Black),
@ -409,6 +413,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Black,Black),
[CPU_GUEST] = ColorPair(Black,Black),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_MIDNIGHT] = {
[RESET_COLOR] = ColorPair(White,Blue),
@ -468,6 +473,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Black,Blue),
[CPU_STEAL] = ColorPair(White,Blue),
[CPU_GUEST] = ColorPair(White,Blue),
[PANEL_EDIT] = ColorPair(White,Blue),
},
[COLORSCHEME_BLACKNIGHT] = {
[RESET_COLOR] = ColorPair(Cyan,Black),
@ -527,12 +533,11 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
[CPU_SOFTIRQ] = ColorPair(Blue,Black),
[CPU_STEAL] = ColorPair(Cyan,Black),
[CPU_GUEST] = ColorPair(Cyan,Black),
[PANEL_EDIT] = ColorPair(White,Cyan),
},
[COLORSCHEME_BROKENGRAY] = { 0 } // dynamically generated.
};
int CRT_cursorX = 0;
int CRT_scrollHAmount = 5;
int CRT_scrollWheelVAmount = 10;

3
CRT.h
View File

@ -116,6 +116,7 @@ typedef enum ColorElements_ {
CPU_SOFTIRQ,
CPU_STEAL,
CPU_GUEST,
PANEL_EDIT,
LAST_COLORELEMENT
} ColorElements;
@ -144,8 +145,6 @@ int* CRT_colors;
extern int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT];
extern int CRT_cursorX;
extern int CRT_scrollHAmount;
extern int CRT_scrollWheelVAmount;

View File

@ -10,7 +10,7 @@ in the source distribution for its full text.
#include "AvailableMetersPanel.h"
#include "MetersPanel.h"
#include "DisplayOptionsPanel.h"
#include "ColumnsPanel.h"
#include "ScreensPanel.h"
#include "ColorsPanel.h"
#include "AvailableColumnsPanel.h"
@ -64,9 +64,11 @@ static void CategoriesPanel_makeColorsPage(CategoriesPanel* this) {
ScreenManager_add(this->scr, colors, -1);
}
static void CategoriesPanel_makeColumnsPage(CategoriesPanel* this) {
Panel* columns = (Panel*) ColumnsPanel_new(this->settings);
static void CategoriesPanel_makeScreensPage(CategoriesPanel* this) {
Panel* screens = (Panel*) ScreensPanel_new(this->settings);
Panel* columns = (Panel*) ((ScreensPanel*)screens)->columns;
Panel* availableColumns = (Panel*) AvailableColumnsPanel_new(columns);
ScreenManager_add(this->scr, screens, 20);
ScreenManager_add(this->scr, columns, 20);
ScreenManager_add(this->scr, availableColumns, -1);
}
@ -118,7 +120,7 @@ static HandlerResult CategoriesPanel_eventHandler(Panel* super, int ch) {
CategoriesPanel_makeColorsPage(this);
break;
case 3:
CategoriesPanel_makeColumnsPage(this);
CategoriesPanel_makeScreensPage(this);
break;
}
}
@ -147,6 +149,6 @@ CategoriesPanel* CategoriesPanel_new(ScreenManager* scr, Settings* settings, Hea
Panel_add(super, (Object*) ListItem_new("Meters", 0));
Panel_add(super, (Object*) ListItem_new("Display options", 0));
Panel_add(super, (Object*) ListItem_new("Colors", 0));
Panel_add(super, (Object*) ListItem_new("Columns", 0));
Panel_add(super, (Object*) ListItem_new("Screens", 0));
return this;
}

View File

@ -22,8 +22,9 @@ in the source distribution for its full text.
typedef struct ColumnsPanel_ {
Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving;
} ColumnsPanel;
@ -123,22 +124,31 @@ PanelClass ColumnsPanel_class = {
.eventHandler = ColumnsPanel_eventHandler
};
ColumnsPanel* ColumnsPanel_new(Settings* settings) {
ColumnsPanel* this = AllocThis(ColumnsPanel);
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss) {
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->moving = false;
Panel_setHeader(super, "Active Columns");
ProcessField* fields = this->settings->fields;
Panel_prune(super);
ProcessField* fields = ss->fields;
for (; *fields; fields++) {
if (Process_fields[*fields].name) {
Panel_add(super, (Object*) ListItem_new(Process_fields[*fields].name, *fields));
}
}
this->ss = ss;
}
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed) {
ColumnsPanel* this = AllocThis(ColumnsPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ColumnsFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->ss = ss;
this->changed = changed;
this->moving = false;
Panel_setHeader(super, "Active Columns");
ColumnsPanel_fill(this, ss);
return this;
}
@ -154,14 +164,14 @@ int ColumnsPanel_fieldNameToIndex(const char* name) {
void ColumnsPanel_update(Panel* super) {
ColumnsPanel* this = (ColumnsPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
this->settings->fields = xRealloc(this->settings->fields, sizeof(ProcessField) * (size+1));
this->settings->flags = 0;
*(this->changed) = true;
this->ss->fields = xRealloc(this->ss->fields, sizeof(ProcessField) * (size+1));
this->ss->flags = 0;
for (int i = 0; i < size; i++) {
int key = ((ListItem*) Panel_get(super, i))->key;
this->settings->fields[i] = key;
this->settings->flags |= Process_fields[key].flags;
this->ss->fields[i] = key;
this->ss->flags |= key < 1000 ? Process_fields[key].flags : 0;
}
this->settings->fields[size] = 0;
this->ss->fields[size] = 0;
}

View File

@ -14,15 +14,18 @@ in the source distribution for its full text.
typedef struct ColumnsPanel_ {
Panel super;
ScreenSettings* ss;
bool* changed;
Settings* settings;
bool moving;
} ColumnsPanel;
extern PanelClass ColumnsPanel_class;
ColumnsPanel* ColumnsPanel_new(Settings* settings);
void ColumnsPanel_fill(ColumnsPanel* this, ScreenSettings* ss);
ColumnsPanel* ColumnsPanel_new(ScreenSettings* ss, bool* changed);
int ColumnsPanel_fieldNameToIndex(const char* name);

View File

@ -83,7 +83,6 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
this->scr = scr;
Panel_setHeader(super, "Display options");
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Tree view"), &(settings->treeView)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Shadow other users' processes"), &(settings->shadowOtherUsers)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide kernel threads"), &(settings->hideKernelThreads)));
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Hide userland process threads"), &(settings->hideUserlandThreads)));

View File

@ -96,11 +96,12 @@ void FunctionBar_setLabel(FunctionBar* this, int event, const char* text) {
}
}
void FunctionBar_draw(const FunctionBar* this, char* buffer) {
FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
int FunctionBar_draw(const FunctionBar* this, char* buffer) {
return FunctionBar_drawAttr(this, buffer, CRT_colors[FUNCTION_BAR]);
}
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
int cursorX = 0;
attrset(CRT_colors[FUNCTION_BAR]);
mvhline(LINES-1, 0, ' ', COLS);
int x = 0;
@ -115,12 +116,10 @@ void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr) {
if (buffer) {
attrset(attr);
mvaddstr(LINES-1, x, buffer);
CRT_cursorX = x + strlen(buffer);
curs_set(1);
} else {
curs_set(0);
cursorX = x + strlen(buffer);
}
attrset(CRT_colors[RESET_COLOR]);
return cursorX;
}
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos) {

View File

@ -30,9 +30,9 @@ void FunctionBar_delete(FunctionBar* this);
void FunctionBar_setLabel(FunctionBar* this, int event, const char* text);
void FunctionBar_draw(const FunctionBar* this, char* buffer);
int FunctionBar_draw(const FunctionBar* this, char* buffer);
void FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
int FunctionBar_drawAttr(const FunctionBar* this, char* buffer, int attr);
int FunctionBar_synthesizeEvent(const FunctionBar* this, int pos);

View File

@ -62,7 +62,7 @@ void Header_delete(Header* this) {
void Header_populateFromSettings(Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col];
MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
for (int i = 0; i < colSettings->len; i++) {
Header_addMeterByName(this, colSettings->names[i], col);
if (colSettings->modes[i] != 0) {
@ -75,7 +75,7 @@ void Header_populateFromSettings(Header* this) {
void Header_writeBackToSettings(const Header* this) {
Header_forEachColumn(this, col) {
MeterColumnSettings* colSettings = &this->settings->columns[col];
MeterColumnSettings* colSettings = &this->settings->meterColumns[col];
String_freeArray(colSettings->names);
free(colSettings->modes);

View File

@ -38,6 +38,7 @@ typedef struct IncMode_ {
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
Panel* panel;
FunctionBar* defaultBar;
bool filtering;
bool found;
@ -115,23 +116,35 @@ static void updateWeakPanel(IncSet* this, Panel* panel, Vector* lines) {
}
}
static bool search(IncMode* mode, Panel* panel, IncMode_GetPanelValue getPanelValue) {
static bool search(IncSet* this, Panel* panel, IncMode_GetPanelValue getPanelValue) {
int size = Panel_size(panel);
bool found = false;
for (int i = 0; i < size; i++) {
if (String_contains_i(getPanelValue(panel, i), mode->buffer)) {
if (String_contains_i(getPanelValue(panel, i), this->active->buffer)) {
Panel_setSelected(panel, i);
found = true;
break;
}
}
if (found)
FunctionBar_draw(mode->bar, mode->buffer);
else
FunctionBar_drawAttr(mode->bar, mode->buffer, CRT_colors[FAILED_SEARCH]);
IncSet_drawBar(this, found ? CRT_colors[FUNCTION_BAR] : CRT_colors[FAILED_SEARCH]);
return found;
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
panel->currentBar = this->active->bar;
panel->cursorOn = true;
this->panel = panel;
IncSet_drawBar(this, CRT_colors[FUNCTION_BAR]);
}
static void IncSet_deactivate(IncSet* this, Panel* panel) {
this->active = NULL;
Panel_setDefaultBar(panel);
panel->cursorOn = false;
FunctionBar_draw(this->defaultBar, NULL);
}
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines) {
if (ch == ERR)
return true;
@ -189,13 +202,11 @@ bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue
} else {
IncMode_reset(mode);
}
this->active = NULL;
Panel_setDefaultBar(panel);
FunctionBar_draw(this->defaultBar, NULL);
IncSet_deactivate(this, panel);
doSearch = false;
}
if (doSearch) {
this->found = search(mode, panel, getPanelValue);
this->found = search(this, panel, getPanelValue);
}
if (filterChanged && lines) {
updateWeakPanel(this, panel, lines);
@ -210,15 +221,11 @@ const char* IncSet_getListItemValue(Panel* panel, int i) {
return "";
}
void IncSet_activate(IncSet* this, IncType type, Panel* panel) {
this->active = &(this->modes[type]);
FunctionBar_draw(this->active->bar, this->active->buffer);
panel->currentBar = this->active->bar;
}
void IncSet_drawBar(IncSet* this) {
void IncSet_drawBar(IncSet* this, int attr) {
if (this->active) {
FunctionBar_draw(this->active->bar, this->active->buffer);
int cursorX = FunctionBar_drawAttr(this->active->bar, this->active->buffer, attr);
this->panel->cursorY = LINES - 1;
this->panel->cursorX = cursorX;
} else {
FunctionBar_draw(this->defaultBar, NULL);
}

View File

@ -33,6 +33,7 @@ typedef struct IncMode_ {
typedef struct IncSet_ {
IncMode modes[2];
IncMode* active;
Panel* panel;
FunctionBar* defaultBar;
bool filtering;
bool found;
@ -45,13 +46,13 @@ IncSet* IncSet_new(FunctionBar* bar);
void IncSet_delete(IncSet* this);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
bool IncSet_handleKey(IncSet* this, int ch, Panel* panel, IncMode_GetPanelValue getPanelValue, Vector* lines);
const char* IncSet_getListItemValue(Panel* panel, int i);
void IncSet_activate(IncSet* this, IncType type, Panel* panel);
void IncSet_drawBar(IncSet* this);
void IncSet_drawBar(IncSet* this, int attr);
int IncSet_synthesizeEvent(IncSet* this, int x);

View File

@ -85,7 +85,7 @@ void InfoScreen_drawTitled(InfoScreen* this, char* fmt, ...) {
attrset(CRT_colors[DEFAULT_COLOR]);
this->display->needsRedraw = true;
Panel_draw(this->display, true);
IncSet_drawBar(this->inc);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
va_end(ap);
}
@ -115,11 +115,7 @@ void InfoScreen_run(InfoScreen* this) {
Panel_draw(panel, true);
if (this->inc->active) {
(void) move(LINES-1, CRT_cursorX);
}
set_escdelay(25);
int ch = getch();
int ch = Panel_getCh(panel);
if (ch == ERR) {
if (As_InfoScreen(this)->onErr) {

View File

@ -27,13 +27,13 @@ typedef struct ListItem_ {
}*/
static void ListItem_delete(Object* cast) {
void ListItem_delete(Object* cast) {
ListItem* this = (ListItem*)cast;
free(this->value);
free(this);
}
static void ListItem_display(Object* cast, RichString* out) {
void ListItem_display(Object* cast, RichString* out) {
ListItem* const this = (ListItem*)cast;
assert (this != NULL);
/*
@ -59,11 +59,15 @@ ObjectClass ListItem_class = {
.compare = ListItem_compare
};
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
void ListItem_init(ListItem* this, const char* value, int key) {
this->value = xStrdup(value);
this->key = key;
this->moving = false;
}
ListItem* ListItem_new(const char* value, int key) {
ListItem* this = AllocThis(ListItem);
ListItem_init(this, value, key);
return this;
}

View File

@ -19,8 +19,14 @@ typedef struct ListItem_ {
} ListItem;
void ListItem_delete(Object* cast);
void ListItem_display(Object* cast, RichString* out);
extern ObjectClass ListItem_class;
void ListItem_init(ListItem* this, const char* value, int key);
ListItem* ListItem_new(const char* value, int key);
void ListItem_append(ListItem* this, const char* text);

View File

@ -72,15 +72,17 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
Htop_Reaction reaction = HTOP_OK;
Settings* settings = this->state->settings;
ScreenSettings* ss = settings->ss;
if (EVENT_IS_HEADER_CLICK(ch)) {
int x = EVENT_HEADER_CLICK_GET_X(ch);
ProcessList* pl = this->state->pl;
Settings* settings = this->state->settings;
int hx = super->scrollH + x + 1;
ProcessField field = ProcessList_keyAt(pl, hx);
if (field == settings->sortKey) {
Settings_invertSortOrder(settings);
settings->treeView = false;
if (field == ss->sortKey) {
ScreenSettings_invertSortOrder(ss);
ss->treeView = false;
} else {
reaction |= Action_setSortKey(settings, field);
}
@ -113,8 +115,8 @@ static HandlerResult MainPanel_eventHandler(Panel* super, int ch) {
}
if (reaction & HTOP_REDRAW_BAR) {
MainPanel_updateTreeFunctions(this, this->state->settings->treeView);
IncSet_drawBar(this->inc);
MainPanel_updateTreeFunctions(this, settings->ss->treeView);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
}
if (reaction & HTOP_UPDATE_PANELHDR) {
ProcessList_printHeader(this->state->pl, Panel_getHeader(super));

View File

@ -12,8 +12,8 @@ applications_DATA = htop.desktop
pixmapdir = $(datadir)/pixmaps
pixmap_DATA = htop.png
htop_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
htop_LDFLAGS =
AM_CFLAGS = -pedantic -Wall $(wextra_flag) -std=c99 -D_XOPEN_SOURCE_EXTENDED -DSYSCONFDIR=\"$(sysconfdir)\" -I"$(top_srcdir)/$(my_htop_platform)"
AM_LDFLAGS =
AM_CPPFLAGS = -DNDEBUG
myhtopsources = AvailableMetersPanel.c CategoriesPanel.c CheckItem.c \
@ -21,7 +21,7 @@ ClockMeter.c ColorsPanel.c ColumnsPanel.c CPUMeter.c CRT.c MainPanel.c \
DisplayOptionsPanel.c FunctionBar.c Hashtable.c Header.c htop.c ListItem.c \
LoadAverageMeter.c MemoryMeter.c Meter.c MetersPanel.c Object.c Panel.c \
BatteryMeter.c Process.c ProcessList.c RichString.c ScreenManager.c Settings.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c \
SignalsPanel.c StringUtils.c SwapMeter.c TasksMeter.c UptimeMeter.c ScreensPanel.c \
TraceScreen.c UsersTable.c Vector.c AvailableColumnsPanel.c AffinityPanel.c \
HostnameMeter.c OpenFilesScreen.c Affinity.c IncSet.c Action.c EnvScreen.c \
InfoScreen.c XAlloc.c
@ -31,69 +31,169 @@ CategoriesPanel.h CheckItem.h ClockMeter.h ColorsPanel.h ColumnsPanel.h \
CPUMeter.h CRT.h MainPanel.h DisplayOptionsPanel.h FunctionBar.h \
Hashtable.h Header.h htop.h ListItem.h LoadAverageMeter.h MemoryMeter.h \
BatteryMeter.h Meter.h MetersPanel.h Object.h Panel.h ProcessList.h RichString.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h \
ScreenManager.h Settings.h SignalsPanel.h StringUtils.h SwapMeter.h ScreensPanel.h \
TasksMeter.h UptimeMeter.h TraceScreen.h UsersTable.h Vector.h Process.h \
AffinityPanel.h HostnameMeter.h OpenFilesScreen.h Affinity.h IncSet.h Action.h \
EnvScreen.h InfoScreen.h XAlloc.h
if HTOP_LINUX
htop_CFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c
all_platform_headers =
myhtopplatheaders = linux/Platform.h linux/IOPriorityPanel.h linux/IOPriority.h \
linux/LinuxProcess.h linux/LinuxProcessList.h linux/LinuxCRT.h linux/Battery.h
# Linux
# -----
linux_platform_headers = \
linux/Platform.h \
linux/IOPriorityPanel.h \
linux/IOPriority.h \
linux/LinuxProcess.h \
linux/LinuxProcessList.h \
linux/LinuxCRT.h \
linux/Battery.h \
linux/PerfCounter.h
all_platform_headers += $(linux_platform_headers)
if HTOP_LINUX
AM_CFLAGS += -rdynamic
myhtopplatsources = linux/Platform.c linux/IOPriorityPanel.c linux/IOPriority.c \
linux/LinuxProcess.c linux/LinuxProcessList.c linux/LinuxCRT.c linux/Battery.c \
linux/PerfCounter.c
myhtopplatheaders = $(linux_platform_headers)
endif
# FreeBSD
# -------
freebsd_platform_headers = \
freebsd/Platform.h \
freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h \
freebsd/FreeBSDCRT.h \
freebsd/Battery.h
all_platform_headers += $(freebsd_platform_headers)
if HTOP_FREEBSD
myhtopplatsources = freebsd/Platform.c freebsd/FreeBSDProcessList.c \
freebsd/FreeBSDProcess.c freebsd/FreeBSDCRT.c freebsd/Battery.c
myhtopplatheaders = freebsd/Platform.h freebsd/FreeBSDProcessList.h \
freebsd/FreeBSDProcess.h freebsd/FreeBSDCRT.h freebsd/Battery.h
myhtopplatheaders = $(freebsd_platform_headers)
endif
# DragonFlyBSD
# ------------
dragonflybsd_platform_headers = \
dragonflybsd/Platform.h \
dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h \
dragonflybsd/DragonFlyBSDCRT.h \
dragonflybsd/Battery.h
all_platform_headers += $(dragonflybsd_platform_headers)
if HTOP_DRAGONFLYBSD
htop_LDFLAGS += -lkvm -lkinfo -lexecinfo
AM_LDFLAGS += -lkvm -lkinfo -lexecinfo
myhtopplatsources = dragonflybsd/Platform.c dragonflybsd/DragonFlyBSDProcessList.c \
dragonflybsd/DragonFlyBSDProcess.c dragonflybsd/DragonFlyBSDCRT.c dragonflybsd/Battery.c
myhtopplatheaders = dragonflybsd/Platform.h dragonflybsd/DragonFlyBSDProcessList.h \
dragonflybsd/DragonFlyBSDProcess.h dragonflybsd/DragonFlyBSDCRT.h dragonflybsd/Battery.h
myhtopplatheaders = $(dragonflybsd_platform_headers)
endif
# OpenBSD
# -------
openbsd_platform_headers = \
openbsd/Platform.h \
openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h \
openbsd/OpenBSDCRT.h \
openbsd/Battery.h
all_platform_headers += $(openbsd_platform_headers)
if HTOP_OPENBSD
myhtopplatsources = openbsd/Platform.c openbsd/OpenBSDProcessList.c \
openbsd/OpenBSDProcess.c openbsd/OpenBSDCRT.c openbsd/Battery.c
myhtopplatheaders = openbsd/Platform.h openbsd/OpenBSDProcessList.h \
openbsd/OpenBSDProcess.h openbsd/OpenBSDCRT.h openbsd/Battery.h
myhtopplatheaders = $(openbsd_platform_headers)
endif
# Darwin
# ------
darwin_platform_headers = \
darwin/Platform.h \
darwin/DarwinProcess.h \
darwin/DarwinProcessList.h \
darwin/DarwinCRT.h \
darwin/Battery.h
all_platform_headers += $(darwin_platform_headers)
if HTOP_DARWIN
htop_LDFLAGS += -framework IOKit -framework CoreFoundation
AM_LDFLAGS += -framework IOKit -framework CoreFoundation
myhtopplatsources = darwin/Platform.c darwin/DarwinProcess.c \
darwin/DarwinProcessList.c darwin/DarwinCRT.c darwin/Battery.c
myhtopplatheaders = darwin/Platform.h darwin/DarwinProcess.h \
darwin/DarwinProcessList.h darwin/DarwinCRT.h darwin/Battery.h
myhtopplatheaders = $(darwin_platform_headers)
endif
# Solaris
# -------
solaris_platform_headers = \
solaris/Platform.h \
solaris/SolarisProcess.h \
solaris/SolarisProcessList.h \
solaris/SolarisCRT.h \
solaris/Battery.h
all_platform_headers += $(solaris_platform_headers)
if HTOP_SOLARIS
myhtopplatsources = solaris/Platform.c \
solaris/SolarisProcess.c solaris/SolarisProcessList.c \
solaris/SolarisCRT.c solaris/Battery.c
myhtopplatheaders = $(solaris_platform_headers)
endif
# Unsupported
# -----------
unsupported_platform_headers = \
unsupported/Platform.h \
unsupported/UnsupportedProcess.h \
unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h \
unsupported/Battery.h
all_platform_headers += $(unsupported_platform_headers)
if HTOP_UNSUPPORTED
myhtopplatsources = unsupported/Platform.c \
unsupported/UnsupportedProcess.c unsupported/UnsupportedProcessList.c \
unsupported/UnsupportedCRT.c unsupported/Battery.c
myhtopplatheaders = unsupported/Platform.h \
unsupported/UnsupportedProcess.h unsupported/UnsupportedProcessList.h \
unsupported/UnsupportedCRT.h unsupported/Battery.h
myhtopplatheaders = $(unsupported_platform_headers)
endif
# ----
SUFFIXES = .h
BUILT_SOURCES = $(myhtopheaders) $(myhtopplatheaders)
htop_SOURCES = $(myhtopheaders) $(myhtopplatheaders) $(myhtopsources) $(myhtopplatsources) config.h
.PHONY: htop-headers clean-htop-headers
htop-headers: $(myhtopheaders) $(all_platform_headers)
clean-htop-headers:
-rm -f $(myhtopheaders) $(all_platform_headers)
target:
echo $(htop_SOURCES)
@ -103,6 +203,9 @@ profile:
debug:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DDEBUG"
symbols:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-ggdb -DNDEBUG"
coverage:
$(MAKE) all CFLAGS="" AM_CPPFLAGS="-fprofile-arcs -ftest-coverage -DDEBUG" LDFLAGS="-lgcov"

View File

@ -76,16 +76,35 @@ void OpenFilesScreen_draw(InfoScreen* this) {
}
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
char command[1025];
xSnprintf(command, 1024, "lsof -P -p %d -F 2> /dev/null", pid);
FILE* fd = popen(command, "r");
char buffer[1025];
xSnprintf(buffer, 1024, "%d", pid);
OpenFiles_ProcessData* pdata = xCalloc(1, sizeof(OpenFiles_ProcessData));
OpenFiles_FileData* fdata = NULL;
OpenFiles_Data* item = &(pdata->data);
if (!fd) {
pdata->error = 127;
int fdpair[2];
if (pipe(fdpair) == -1) {
pdata->error = 1;
return pdata;
}
pid_t child = fork();
if (child == -1) {
pdata->error = 1;
return pdata;
}
if (child == 0) {
close(fdpair[0]);
dup2(fdpair[1], STDOUT_FILENO);
close(fdpair[1]);
int fdnull = open("/dev/null", O_WRONLY);
if (fdnull < 0)
exit(1);
dup2(fdnull, STDERR_FILENO);
close(fdnull);
execlp("lsof", "lsof", "-P", "-p", buffer, "-F", NULL);
exit(127);
}
close(fdpair[1]);
FILE* fd = fdopen(fdpair[0], "r");
for (;;) {
char* line = String_readLine(fd);
if (!line) {
@ -105,7 +124,15 @@ static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
item->data[cmd] = xStrdup(line + 1);
free(line);
}
pdata->error = pclose(fd);
int wstatus;
if (waitpid(child, &wstatus, 0) == -1) {
pdata->error = 1;
return pdata;
}
if (!WIFEXITED(wstatus))
pdata->error = 1;
else
pdata->error = WEXITSTATUS(wstatus);
return pdata;
}

22
Panel.c
View File

@ -57,6 +57,7 @@ typedef struct PanelClass_ {
struct Panel_ {
Object super;
int x, y, w, h;
int cursorX, cursorY;
WINDOW* window;
Vector* items;
int selected;
@ -66,6 +67,7 @@ struct Panel_ {
int scrollV;
short scrollH;
bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header;
@ -85,6 +87,11 @@ struct Panel_ {
#define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this) {
this->cursorY = this->y + this->selected - this->scrollV + 1;
this->cursorX = this->x + this->selectedLen - this->scrollH;
}
PanelClass Panel_class = {
.super = {
.extends = Class(Object),
@ -112,6 +119,8 @@ void Panel_init(Panel* this, int x, int y, int w, int h, ObjectClass* type, bool
this->y = y;
this->w = w;
this->h = h;
this->cursorX = 0;
this->cursorY = 0;
this->eventHandlerState = NULL;
this->items = Vector_new(type, owner, DEFAULT_SIZE);
this->scrollV = 0;
@ -367,7 +376,6 @@ void Panel_draw(Panel* this, bool focus) {
RichString_end(old);
}
this->oldSelected = this->selected;
move(0, 0);
}
bool Panel_onKey(Panel* this, int key) {
@ -499,3 +507,15 @@ HandlerResult Panel_selectByTyping(Panel* this, int ch) {
}
return IGNORED;
}
int Panel_getCh(Panel* this) {
if (this->cursorOn) {
move(this->cursorY, this->cursorX);
curs_set(1);
} else {
curs_set(0);
}
set_escdelay(25);
return getch();
}

View File

@ -46,6 +46,7 @@ typedef struct PanelClass_ {
struct Panel_ {
Object super;
int x, y, w, h;
int cursorX, cursorY;
WINDOW* window;
Vector* items;
int selected;
@ -55,6 +56,7 @@ struct Panel_ {
int scrollV;
short scrollH;
bool needsRedraw;
bool cursorOn;
FunctionBar* currentBar;
FunctionBar* defaultBar;
RichString header;
@ -73,6 +75,8 @@ struct Panel_ {
#define KEY_CTRL(l) ((l)-'A'+1)
void Panel_setCursorToSelection(Panel* this);
extern PanelClass Panel_class;
Panel* Panel_new(int x, int y, int w, int h, bool owner, ObjectClass* type, FunctionBar* fuBar);
@ -123,4 +127,7 @@ bool Panel_onKey(Panel* this, int key);
HandlerResult Panel_selectByTyping(Panel* this, int ch);
int Panel_getCh(Panel* this);
#endif

View File

@ -51,6 +51,7 @@ in the source distribution for its full text.
#include "Object.h"
#include <sys/types.h>
#include <inttypes.h>
#define PROCESS_FLAG_IO 0x0001
@ -156,7 +157,7 @@ typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
uint64_t flags;
} ProcessFieldData;
// Implemented in platform-specific code:
@ -178,6 +179,8 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
@ -368,6 +371,21 @@ void Process_outputRate(RichString* str, char* buffer, int n, double rate, int c
}
}
void Process_printPercentage(float val, char* buffer, int n, int* attr) {
if (val >= 0) {
if (val < 100) {
xSnprintf(buffer, n, "%4.1f ", val);
} else if (val < 1000) {
xSnprintf(buffer, n, "%3d. ", (unsigned int)val);
} else {
xSnprintf(buffer, n, "%4d ", (unsigned int)val);
}
} else {
*attr = CRT_colors[PROCESS_SHADOW];
xSnprintf(buffer, n, " N/A ");
}
}
void Process_writeField(Process* this, RichString* str, ProcessField field) {
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
@ -376,30 +394,15 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
bool coloring = this->settings->highlightMegabytes;
switch (field) {
case PERCENT_CPU: {
if (this->percent_cpu > 999.9) {
xSnprintf(buffer, n, "%4d ", (unsigned int)this->percent_cpu);
} else if (this->percent_cpu > 99.9) {
xSnprintf(buffer, n, "%3d. ", (unsigned int)this->percent_cpu);
} else {
xSnprintf(buffer, n, "%4.1f ", this->percent_cpu);
}
break;
}
case PERCENT_MEM: {
if (this->percent_mem > 99.9) {
xSnprintf(buffer, n, "100. ");
} else {
xSnprintf(buffer, n, "%4.1f ", this->percent_mem);
}
break;
}
case PERCENT_CPU: Process_printPercentage(this->percent_cpu, buffer, n, &attr); break;
case PERCENT_MEM: Process_printPercentage(this->percent_mem, buffer, n, &attr); break;
case COMM: {
if (this->settings->highlightThreads && Process_isThread(this)) {
attr = CRT_colors[PROCESS_THREAD];
baseattr = CRT_colors[PROCESS_THREAD_BASENAME];
}
if (!this->settings->treeView || this->indent == 0) {
ScreenSettings* ss = this->settings->ss;
if (!ss->treeView || this->indent == 0) {
Process_writeCommand(this, attr, baseattr, str);
return;
} else {
@ -420,7 +423,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
buf += written;
n -= written;
}
const char* draw = CRT_treeStr[lastItem ? (this->settings->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
const char* draw = CRT_treeStr[lastItem ? (ss->direction == 1 ? TREE_STR_BEND : TREE_STR_TEND) : TREE_STR_RTEE];
xSnprintf(buf, n, "%s%s ", draw, this->showChildren ? CRT_treeStr[TREE_STR_SHUT] : CRT_treeStr[TREE_STR_OPEN] );
RichString_append(str, CRT_colors[PROCESS_TREE], buffer);
Process_writeCommand(this, attr, baseattr, str);
@ -491,7 +494,7 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
void Process_display(Object* cast, RichString* out) {
Process* this = (Process*) cast;
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
RichString_prune(out);
for (int i = 0; fields[i]; i++)
As_Process(this)->writeField(this, out, fields[i]);
@ -561,14 +564,15 @@ long Process_pidCompare(const void* v1, const void* v2) {
long Process_compare(const void* v1, const void* v2) {
Process *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
ScreenSettings* ss = settings->ss;
if (ss->direction == 1) {
p1 = (Process*)v1;
p2 = (Process*)v2;
} else {
p2 = (Process*)v1;
p1 = (Process*)v2;
}
switch (settings->sortKey) {
switch (ss->sortKey) {
case PERCENT_CPU:
return (p2->percent_cpu > p1->percent_cpu ? 1 : -1);
case PERCENT_MEM:

View File

@ -29,6 +29,7 @@ in the source distribution for its full text.
#include "Object.h"
#include <sys/types.h>
#include <inttypes.h>
#define PROCESS_FLAG_IO 0x0001
@ -134,7 +135,7 @@ typedef struct ProcessFieldData_ {
const char* name;
const char* title;
const char* description;
int flags;
uint64_t flags;
} ProcessFieldData;
// Implemented in platform-specific code:
@ -156,6 +157,8 @@ typedef struct ProcessClass_ {
#define As_Process(this_) ((ProcessClass*)((this_)->super.klass))
#define Process_getParentPid(process_) (process_->tgid == process_->pid ? process_->ppid : process_->tgid)
#define Process_isChildOf(process_, pid_) (process_->tgid == pid_ || (process_->tgid == process_->pid && process_->ppid == pid_))
#define Process_sortState(state) ((state) == 'I' ? 0x100 : (state))
@ -181,6 +184,8 @@ void Process_printTime(RichString* str, unsigned long long totalHundredths);
void Process_outputRate(RichString* str, char* buffer, int n, double rate, int coloring);
void Process_printPercentage(float val, char* buffer, int n, int* attr);
void Process_writeField(Process* this, RichString* str, ProcessField field);
void Process_display(Object* cast, RichString* out);

View File

@ -124,14 +124,15 @@ void ProcessList_setPanel(ProcessList* this, Panel* panel) {
void ProcessList_printHeader(ProcessList* this, RichString* header) {
RichString_prune(header);
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
for (int i = 0; fields[i]; i++) {
const char* field = Process_fields[fields[i]].title;
unsigned int key = fields[i];
const char* field = Process_fields[key].title;
if (!field) field = "- ";
if (!this->settings->treeView && this->settings->sortKey == fields[i])
RichString_append(header, CRT_colors[PANEL_SELECTION_FOCUS], field);
else
RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
int color = (!this->settings->ss->treeView && this->settings->ss->sortKey == key)
? CRT_colors[PANEL_SELECTION_FOCUS]
: CRT_colors[PANEL_HEADER_FOCUS];
RichString_append(header, color, field);
}
}
@ -200,19 +201,19 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i
}
void ProcessList_sort(ProcessList* this) {
if (!this->settings->treeView) {
if (!this->settings->ss->treeView) {
Vector_insertionSort(this->processes);
} else {
// Save settings
int direction = this->settings->direction;
int sortKey = this->settings->sortKey;
int direction = this->settings->ss->direction;
int sortKey = this->settings->ss->sortKey;
// Sort by PID
this->settings->sortKey = PID;
this->settings->direction = 1;
this->settings->ss->sortKey = PID;
this->settings->ss->direction = 1;
Vector_quickSort(this->processes);
// Restore settings
this->settings->sortKey = sortKey;
this->settings->direction = direction;
this->settings->ss->sortKey = sortKey;
this->settings->ss->direction = direction;
int vsize = Vector_size(this->processes);
// Find all processes whose parent is not visible
int size;
@ -228,7 +229,7 @@ void ProcessList_sort(ProcessList* this) {
ProcessList_buildTree(this, process->pid, 0, 0, direction, false);
break;
}
pid_t ppid = process->tgid == process->pid ? process->ppid : process->tgid;
pid_t ppid = Process_getParentPid(process);
// Bisect the process vector to find parent
int l = 0, r = size;
// If PID corresponds with PPID (e.g. "kernel_task" (PID:0, PPID:0)
@ -271,7 +272,7 @@ void ProcessList_sort(ProcessList* this) {
ProcessField ProcessList_keyAt(ProcessList* this, int at) {
int x = 0;
ProcessField* fields = this->settings->fields;
ProcessField* fields = this->settings->ss->fields;
ProcessField field;
for (int i = 0; (field = fields[i]); i++) {
const char* title = Process_fields[field].title;

View File

@ -71,30 +71,46 @@ inline int ScreenManager_size(ScreenManager* this) {
}
void ScreenManager_add(ScreenManager* this, Panel* item, int size) {
ScreenManager_insert(this, item, size, Vector_size(this->panels));
}
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx) {
if (this->orientation == HORIZONTAL) {
int lastX = 0;
if (this->panelCount > 0) {
Panel* last = (Panel*) Vector_get(this->panels, this->panelCount - 1);
if (idx > 0) {
Panel* last = (Panel*) Vector_get(this->panels, idx - 1);
lastX = last->x + last->w + 1;
}
int height = LINES - this->y1 + this->y2;
if (size > 0) {
Panel_resize(item, size, height);
} else {
Panel_resize(item, COLS-this->x1+this->x2-lastX, height);
if (size <= 0) {
size = COLS-this->x1+this->x2-lastX;
}
Panel_resize(item, size, height);
Panel_move(item, lastX, this->y1);
if (idx < this->panelCount) {
for (int i = idx + 1; i <= this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x + size, p->y);
}
}
}
// TODO: VERTICAL
Vector_add(this->panels, item);
Vector_insert(this->panels, idx, item);
item->needsRedraw = true;
this->panelCount++;
}
Panel* ScreenManager_remove(ScreenManager* this, int idx) {
assert(this->panelCount > idx);
int w = ((Panel*) Vector_get(this->panels, idx))->w;
Panel* panel = (Panel*) Vector_remove(this->panels, idx);
this->panelCount--;
if (idx < this->panelCount) {
for (int i = idx; i < this->panelCount; i++) {
Panel* p = (Panel*) Vector_get(this->panels, i);
Panel_move(p, p->x - w, p->y);
}
}
return panel;
}
@ -131,7 +147,7 @@ static void checkRecalculation(ScreenManager* this, double* oldTime, int* sortTi
if (*rescan) {
*oldTime = newTime;
ProcessList_scan(pl);
if (*sortTimeout == 0 || this->settings->treeView) {
if (*sortTimeout == 0 || this->settings->ss->treeView) {
ProcessList_sort(pl);
*sortTimeout = 1;
}
@ -157,7 +173,8 @@ static void ScreenManager_drawPanels(ScreenManager* this, int focus) {
}
}
static Panel* setCurrentPanel(Panel* panel) {
static Panel* setCurrentPanel(ScreenManager* this, int focus) {
Panel* panel = (Panel*) Vector_get(this->panels, focus);
FunctionBar_draw(panel->currentBar, NULL);
return panel;
}
@ -166,7 +183,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
bool quit = false;
int focus = 0;
Panel* panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
Panel* panelFocus = setCurrentPanel(this, focus);
double oldTime = 0.0;
@ -189,8 +206,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
}
int prevCh = ch;
set_escdelay(25);
ch = getch();
ch = Panel_getCh(panelFocus);
HandlerResult result = IGNORED;
if (ch == KEY_MOUSE) {
@ -212,7 +228,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
ch = KEY_MOUSE;
if (panel == panelFocus || this->allowFocusChange) {
focus = i;
panelFocus = setCurrentPanel(panel);
panelFocus = setCurrentPanel(this, i);
Object* oldSelection = Panel_getSelected(panel);
Panel_setSelected(panel, mevent.y - panel->y + panel->scrollV - 1);
if (Panel_getSelected(panel) == oldSelection) {
@ -288,7 +304,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
tryLeft:
if (focus > 0)
focus--;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
panelFocus = setCurrentPanel(this, focus);
if (Panel_size(panelFocus) == 0 && focus > 0)
goto tryLeft;
break;
@ -303,7 +319,7 @@ void ScreenManager_run(ScreenManager* this, Panel** lastFocus, int* lastKey) {
tryRight:
if (focus < this->panelCount - 1)
focus++;
panelFocus = setCurrentPanel((Panel*) Vector_get(this->panels, focus));
panelFocus = setCurrentPanel(this, focus);
if (Panel_size(panelFocus) == 0 && focus < this->panelCount - 1)
goto tryRight;
break;

View File

@ -43,6 +43,8 @@ extern int ScreenManager_size(ScreenManager* this);
void ScreenManager_add(ScreenManager* this, Panel* item, int size);
void ScreenManager_insert(ScreenManager* this, Panel* item, int size, int idx);
Panel* ScreenManager_remove(ScreenManager* this, int idx);
void ScreenManager_resize(ScreenManager* this, int x1, int y1, int x2, int y2);

330
ScreensPanel.c Normal file
View File

@ -0,0 +1,330 @@
/*
htop - ScreensPanel.c
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ScreensPanel.h"
#include "Platform.h"
#include "StringUtils.h"
#include "CRT.h"
#include <assert.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
/*{
#include "Panel.h"
#include "ScreenManager.h"
#include "ColumnsPanel.h"
#include "Settings.h"
#include "ListItem.h"
#ifndef SCREEN_NAME_LEN
#define SCREEN_NAME_LEN 20
#endif
typedef struct ScreensPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
ColumnsPanel* columns;
char buffer[SCREEN_NAME_LEN + 1];
char* saved;
int cursor;
bool moving;
bool renaming;
} ScreensPanel;
typedef struct ScreenListItem_ {
ListItem super;
ScreenSettings* ss;
} ScreenListItem;
}*/
ObjectClass ScreenListItem_class = {
.extends = Class(ListItem),
.display = ListItem_display,
.delete = ListItem_delete,
.compare = ListItem_compare
};
ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss) {
ScreenListItem* this = AllocThis(ScreenListItem);
ListItem_init((ListItem*)this, value, 0);
this->ss = ss;
return this;
}
static const char* const ScreensFunctions[] = {" ", "Rename", " ", " ", "New ", " ", "MoveUp", "MoveDn", "Remove", "Done ", NULL};
static void ScreensPanel_delete(Object* object) {
Panel* super = (Panel*) object;
ScreensPanel* this = (ScreensPanel*) object;
Panel_done(super);
free(this);
}
static HandlerResult ScreensPanel_eventHandlerRenaming(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
//ListItem* item = (ListItem*)Panel_getSelected(super);
if (ch >= 32 && ch < 127 && ch != 61 && ch != 22) {
if (this->cursor < SCREEN_NAME_LEN - 1) {
this->buffer[this->cursor] = ch;
this->cursor++;
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
} else {
switch(ch) {
case 127:
case KEY_BACKSPACE:
{
if (this->cursor > 0) {
this->cursor--;
this->buffer[this->cursor] = '\0';
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
break;
}
case 0x0a:
case 0x0d:
case KEY_ENTER:
{
ListItem* item = (ListItem*) Panel_getSelected(super);
free(this->saved);
item->value = xStrdup(this->buffer);
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
ScreensPanel_update(super);
break;
}
case 27: // Esc
{
ListItem* item = (ListItem*) Panel_getSelected(super);
item->value = this->saved;
this->renaming = false;
super->cursorOn = false;
Panel_setSelectionColor(super, CRT_colors[PANEL_SELECTION_FOCUS]);
break;
}
}
}
return HANDLED;
}
static void startRenaming(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
ListItem* item = (ListItem*) Panel_getSelected(super);
this->renaming = true;
super->cursorOn = true;
char* name = item->value;
this->saved = name;
strncpy(this->buffer, name, SCREEN_NAME_LEN);
this->buffer[SCREEN_NAME_LEN] = '\0';
this->cursor = strlen(this->buffer);
item->value = this->buffer;
Panel_setSelectionColor(super, CRT_colors[PANEL_EDIT]);
super->selectedLen = strlen(this->buffer);
Panel_setCursorToSelection(super);
}
static void rebuildSettingsArray(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
int n = Panel_size(super);
free(this->settings->screens);
this->settings->screens = xMalloc(sizeof(ScreenSettings*) * (n + 1));
this->settings->screens[n] = NULL;
for (int i = 0; i < n; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
this->settings->screens[i] = item->ss;
}
}
static void addNewScreen(Panel* super) {
ScreensPanel* const this = (ScreensPanel*) super;
char* name = "New";
ScreenSettings* ss = Settings_newScreen(this->settings, name, "PID Command");
ScreenListItem* item = ScreenListItem_new(name, ss);
int idx = Panel_getSelectedIndex(super);
Panel_insert(super, idx + 1, (Object*) item);
Panel_setSelected(super, idx + 1);
}
static HandlerResult ScreensPanel_eventHandlerNormal(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
int selected = Panel_getSelectedIndex(super);
ScreenListItem* oldFocus = (ScreenListItem*) Panel_getSelected(super);
bool shouldRebuildArray = false;
HandlerResult result = IGNORED;
switch(ch) {
case 0x0a:
case 0x0d:
case KEY_ENTER:
case KEY_MOUSE:
case KEY_RECLICK:
{
this->moving = !(this->moving);
Panel_setSelectionColor(super, this->moving ? CRT_colors[PANEL_SELECTION_FOLLOW] : CRT_colors[PANEL_SELECTION_FOCUS]);
((ListItem*)Panel_getSelected(super))->moving = this->moving;
result = HANDLED;
break;
}
case EVENT_SET_SELECTED:
result = HANDLED;
break;
case KEY_NPAGE:
case KEY_PPAGE:
case KEY_HOME:
case KEY_END: {
Panel_onKey(super, ch);
break;
}
case KEY_F(2):
case KEY_CTRL('R'):
{
startRenaming(super);
result = HANDLED;
break;
}
case KEY_F(5):
case KEY_CTRL('N'):
{
addNewScreen(super);
startRenaming(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_UP:
{
if (!this->moving) {
Panel_onKey(super, ch);
break;
}
/* else fallthrough */
}
case KEY_F(7):
case '[':
case '-':
{
Panel_moveSelectedUp(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_DOWN:
{
if (!this->moving) {
Panel_onKey(super, ch);
break;
}
/* else fallthrough */
}
case KEY_F(8):
case ']':
case '+':
{
Panel_moveSelectedDown(super);
shouldRebuildArray = true;
result = HANDLED;
break;
}
case KEY_F(9):
//case KEY_DC:
{
if (Panel_size(super) > 1) {
Panel_remove(super, selected);
}
shouldRebuildArray = true;
result = HANDLED;
break;
}
default:
{
if (ch < 255 && isalpha(ch))
result = Panel_selectByTyping(super, ch);
if (result == BREAK_LOOP)
result = IGNORED;
break;
}
}
ScreenListItem* newFocus = (ScreenListItem*) Panel_getSelected(super);
if (oldFocus != newFocus) {
ColumnsPanel_fill(this->columns, newFocus->ss);
result = HANDLED;
}
if (shouldRebuildArray) {
rebuildSettingsArray(super);
}
if (result == HANDLED)
ScreensPanel_update(super);
return result;
}
static HandlerResult ScreensPanel_eventHandler(Panel* super, int ch) {
ScreensPanel* const this = (ScreensPanel*) super;
if (this->renaming) {
return ScreensPanel_eventHandlerRenaming(super, ch);
} else {
return ScreensPanel_eventHandlerNormal(super, ch);
}
}
PanelClass ScreensPanel_class = {
.super = {
.extends = Class(Panel),
.delete = ScreensPanel_delete
},
.eventHandler = ScreensPanel_eventHandler
};
ScreensPanel* ScreensPanel_new(Settings* settings) {
ScreensPanel* this = AllocThis(ScreensPanel);
Panel* super = (Panel*) this;
FunctionBar* fuBar = FunctionBar_new(ScreensFunctions, NULL, NULL);
Panel_init(super, 1, 1, 1, 1, Class(ListItem), true, fuBar);
this->settings = settings;
this->columns = ColumnsPanel_new(settings->screens[0], &(settings->changed));
this->moving = false;
this->renaming = false;
super->cursorOn = false;
this->cursor = 0;
Panel_setHeader(super, "Screens");
for (unsigned int i = 0; i < settings->nScreens; i++) {
ScreenSettings* ss = settings->screens[i];
char* name = ss->name;
Panel_add(super, (Object*) ScreenListItem_new(name, ss));
}
return this;
}
void ScreensPanel_update(Panel* super) {
ScreensPanel* this = (ScreensPanel*) super;
int size = Panel_size(super);
this->settings->changed = true;
this->settings->screens = xRealloc(this->settings->screens, sizeof(char*) * (size+1));
for (int i = 0; i < size; i++) {
ScreenListItem* item = (ScreenListItem*) Panel_get(super, i);
ScreenSettings* ss = item->ss;
free(ss->name);
this->settings->screens[i] = ss;
ss->name = xStrdup(((ListItem*) item)->value);
}
this->settings->screens[size] = NULL;
}

51
ScreensPanel.h Normal file
View File

@ -0,0 +1,51 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_ScreensPanel
#define HEADER_ScreensPanel
/*
htop - ScreensPanel.h
(C) 2004-2011 Hisham H. Muhammad
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Panel.h"
#include "ScreenManager.h"
#include "ColumnsPanel.h"
#include "Settings.h"
#include "ListItem.h"
#ifndef SCREEN_NAME_LEN
#define SCREEN_NAME_LEN 20
#endif
typedef struct ScreensPanel_ {
Panel super;
ScreenManager* scr;
Settings* settings;
ColumnsPanel* columns;
char buffer[SCREEN_NAME_LEN + 1];
char* saved;
int cursor;
bool moving;
bool renaming;
} ScreensPanel;
typedef struct ScreenListItem_ {
ListItem super;
ScreenSettings* ss;
} ScreenListItem;
extern ObjectClass ScreenListItem_class;
ScreenListItem* ScreenListItem_new(const char* value, ScreenSettings* ss);
extern PanelClass ScreensPanel_class;
ScreensPanel* ScreensPanel_new(Settings* settings);
void ScreensPanel_update(Panel* super);
#endif

View File

@ -23,29 +23,44 @@ in the source distribution for its full text.
#include "Process.h"
#include <stdbool.h>
typedef struct {
const char* name;
const char* columns;
const char* sortKey;
} ScreenDefaults;
typedef struct {
int len;
char** names;
int* modes;
} MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ {
char* filename;
MeterColumnSettings columns[2];
MeterColumnSettings meterColumns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme;
int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
@ -68,25 +83,82 @@ typedef struct Settings_ {
}*/
static void writeList(FILE* fd, char** list, int len) {
const char* sep = "";
for (int i = 0; i < len; i++) {
fprintf(fd, "%s%s", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
/*
static char** readQuotedList(char* line) {
int n = 0;
char** list = xCalloc(sizeof(char*), 1);
int start = 0;
for (;;) {
while (line[start] && line[start] == ' ') {
start++;
}
if (line[start] != '"') {
break;
}
start++;
int close = start;
while (line[close] && line[close] != '"') {
close++;
}
int len = close - start;
char* item = xMalloc(len + 1);
strncpy(item, line + start, len);
item[len] = '\0';
list[n] = item;
n++;
list = xRealloc(list, sizeof(char*) * (n + 1));
start = close + 1;
}
list[n] = NULL;
return list;
}
static void writeQuotedList(FILE* fd, char** list) {
const char* sep = "";
for (int i = 0; list[i]; i++) {
fprintf(fd, "%s\"%s\"", sep, list[i]);
sep = " ";
}
fprintf(fd, "\n");
}
*/
void Settings_delete(Settings* this) {
free(this->filename);
free(this->fields);
for (unsigned int i = 0; i < (sizeof(this->columns)/sizeof(MeterColumnSettings)); i++) {
String_freeArray(this->columns[i].names);
free(this->columns[i].modes);
for (unsigned int i = 0; i < (sizeof(this->meterColumns)/sizeof(MeterColumnSettings)); i++) {
String_freeArray(this->meterColumns[i].names);
free(this->meterColumns[i].modes);
}
if (this->screens) {
for (unsigned int i = 0; this->screens[i]; i++) {
free(this->screens[i]->name);
free(this->screens[i]->fields);
}
free(this->screens);
}
free(this);
}
static void Settings_readMeters(Settings* this, char* line, int column) {
static void Settings_readMeters(Settings* this, char* line, int side) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
free(trim);
this->columns[column].names = ids;
this->meterColumns[side].names = ids;
}
static void Settings_readMeterModes(Settings* this, char* line, int column) {
static void Settings_readMeterModes(Settings* this, char* line, int side) {
char* trim = String_trim(line);
int nIds;
char** ids = String_split(trim, ' ', &nIds);
@ -95,13 +167,13 @@ static void Settings_readMeterModes(Settings* this, char* line, int column) {
for (int i = 0; ids[i]; i++) {
len++;
}
this->columns[column].len = len;
this->meterColumns[side].len = len;
int* modes = xCalloc(len, sizeof(int));
for (int i = 0; i < len; i++) {
modes[i] = atoi(ids[i]);
}
String_freeArray(ids);
this->columns[column].modes = modes;
this->meterColumns[side].modes = modes;
}
static void Settings_defaultMeters(Settings* this) {
@ -110,37 +182,59 @@ static void Settings_defaultMeters(Settings* this) {
sizes[1]++;
}
for (int i = 0; i < 2; i++) {
this->columns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->columns[i].modes = xCalloc(sizes[i], sizeof(int));
this->columns[i].len = sizes[i];
this->meterColumns[i].names = xCalloc(sizes[i] + 1, sizeof(char*));
this->meterColumns[i].modes = xCalloc(sizes[i], sizeof(int));
this->meterColumns[i].len = sizes[i];
}
int r = 0;
if (this->cpuCount > 8) {
this->columns[0].names[0] = xStrdup("LeftCPUs2");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs2");
this->columns[1].modes[r++] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("LeftCPUs2");
this->meterColumns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs2");
this->meterColumns[1].modes[r++] = BAR_METERMODE;
} else if (this->cpuCount > 4) {
this->columns[0].names[0] = xStrdup("LeftCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("RightCPUs");
this->columns[1].modes[r++] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("LeftCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE;
this->meterColumns[1].names[r] = xStrdup("RightCPUs");
this->meterColumns[1].modes[r++] = BAR_METERMODE;
} else {
this->columns[0].names[0] = xStrdup("AllCPUs");
this->columns[0].modes[0] = BAR_METERMODE;
this->meterColumns[0].names[0] = xStrdup("AllCPUs");
this->meterColumns[0].modes[0] = BAR_METERMODE;
}
this->columns[0].names[1] = xStrdup("Memory");
this->columns[0].modes[1] = BAR_METERMODE;
this->columns[0].names[2] = xStrdup("Swap");
this->columns[0].modes[2] = BAR_METERMODE;
this->meterColumns[0].names[1] = xStrdup("Memory");
this->meterColumns[0].modes[1] = BAR_METERMODE;
this->meterColumns[0].names[2] = xStrdup("Swap");
this->meterColumns[0].modes[2] = BAR_METERMODE;
this->columns[1].names[r] = xStrdup("Tasks");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("LoadAverage");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->columns[1].names[r] = xStrdup("Uptime");
this->columns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Tasks");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("LoadAverage");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
this->meterColumns[1].names[r] = xStrdup("Uptime");
this->meterColumns[1].modes[r++] = TEXT_METERMODE;
}
static const char* toFieldName(int i) {
return Process_fields[i].name;
}
static int toFieldIndex(const char* str) {
if (isdigit(str[0])) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(str) + 1;
if (id < Platform_numberOfFields && toFieldName(id)) {
return id;
}
} else {
for (int p = 1; p < LAST_PROCESSFIELD; p++) {
const char* pName = toFieldName(p);
if (pName && strcmp(pName, str) == 0) {
return p;
}
}
}
return -1;
}
static void readFields(ProcessField* fields, int* flags, const char* line) {
@ -151,11 +245,10 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
int i, j;
*flags = 0;
for (j = 0, i = 0; i < Platform_numberOfFields && ids[i]; i++) {
// This "+1" is for compatibility with the older enum format.
int id = atoi(ids[i]) + 1;
if (id > 0 && Process_fields[id].name && id < Platform_numberOfFields) {
fields[j] = id;
*flags |= Process_fields[id].flags;
int idx = toFieldIndex(ids[i]);
if (idx != -1) {
fields[j] = idx;
*flags |= Process_fields[idx].flags;
j++;
}
}
@ -163,6 +256,29 @@ static void readFields(ProcessField* fields, int* flags, const char* line) {
String_freeArray(ids);
}
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line) {
ScreenSettings* ss = xCalloc(sizeof(ScreenSettings), 1);
ss->name = xStrdup(name);
ss->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
ss->flags = 0;
ss->direction = 1;
ss->treeView = 0;
readFields(ss->fields, &(ss->flags), line);
this->screens[this->nScreens] = ss;
this->nScreens++;
this->screens = xRealloc(this->screens, sizeof(ScreenSettings*) * (this->nScreens + 1));
this->screens[this->nScreens] = NULL;
return ss;
}
static void Settings_defaultScreens(Settings* this) {
for (unsigned int i = 0; i < Platform_numberOfDefaultScreens; i++) {
ScreenDefaults* defaults = &Platform_defaultScreens[i];
Settings_newScreen(this, defaults->name, defaults->columns);
this->screens[0]->sortKey = toFieldIndex(defaults->sortKey);
}
}
static bool Settings_read(Settings* this, const char* fileName) {
FILE* fd;
@ -174,6 +290,9 @@ static bool Settings_read(Settings* this, const char* fileName) {
bool didReadMeters = false;
bool didReadFields = false;
ProcessField* legacyFields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
int legacyFlags;
bool legacyFieldsRead = false;
for (;;) {
char* line = String_readLine(fd);
if (!line) {
@ -187,15 +306,8 @@ static bool Settings_read(Settings* this, const char* fileName) {
continue;
}
if (String_eq(option[0], "fields")) {
readFields(this->fields, &(this->flags), option[1]);
didReadFields = true;
} else if (String_eq(option[0], "sort_key")) {
// This "+1" is for compatibility with the older enum format.
this->sortKey = atoi(option[1]) + 1;
} else if (String_eq(option[0], "sort_direction")) {
this->direction = atoi(option[1]);
} else if (String_eq(option[0], "tree_view")) {
this->treeView = atoi(option[1]);
readFields(legacyFields, &legacyFlags, option[1]);
legacyFieldsRead = true;
} else if (String_eq(option[0], "hide_threads")) {
this->hideThreads = atoi(option[1]);
} else if (String_eq(option[0], "hide_kernel_threads")) {
@ -244,40 +356,60 @@ static bool Settings_read(Settings* this, const char* fileName) {
} else if (String_eq(option[0], "right_meter_modes")) {
Settings_readMeterModes(this, option[1], 1);
didReadMeters = true;
} else if (strncmp(option[0], "screen:", 7) == 0) {
Settings_newScreen(this, option[0] + 7, option[1]);
} else if (String_eq(option[0], ".tree_view")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->treeView = atoi(option[1]);
}
} else if (String_eq(option[0], ".sort_direction")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->direction = atoi(option[1]);
}
} else if (String_eq(option[0], ".sort_key")) {
if (this->nScreens > 0) {
this->screens[this->nScreens - 1]->sortKey = toFieldIndex(option[1]);
}
}
String_freeArray(option);
}
fclose(fd);
if (this->nScreens == 0) {
Settings_defaultScreens(this);
if (legacyFieldsRead) {
free(this->screens[0]->fields);
this->screens[0]->fields = legacyFields;
this->screens[0]->flags = legacyFlags;
}
}
if (!didReadMeters) {
Settings_defaultMeters(this);
}
return didReadFields;
}
static void writeFields(FILE* fd, ProcessField* fields, const char* name) {
fprintf(fd, "%s=", name);
static void writeFields(FILE* fd, ProcessField* fields, bool byName) {
const char* sep = "";
for (int i = 0; fields[i]; i++) {
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i]-1);
if (byName) {
fprintf(fd, "%s%s", sep, toFieldName(fields[i]));
} else {
// This " - 1" is for compatibility with the older enum format.
fprintf(fd, "%s%d", sep, (int) fields[i] - 1);
}
sep = " ";
}
fprintf(fd, "\n");
}
static void writeMeters(Settings* this, FILE* fd, int column) {
const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%s", sep, this->columns[column].names[i]);
sep = " ";
}
fprintf(fd, "\n");
static void writeMeters(Settings* this, FILE* fd, int side) {
writeList(fd, this->meterColumns[side].names, this->meterColumns[side].len);
}
static void writeMeterModes(Settings* this, FILE* fd, int column) {
static void writeMeterModes(Settings* this, FILE* fd, int side) {
const char* sep = "";
for (int i = 0; i < this->columns[column].len; i++) {
fprintf(fd, "%s%d", sep, this->columns[column].modes[i]);
for (int i = 0; i < this->meterColumns[side].len; i++) {
fprintf(fd, "%s%d", sep, this->meterColumns[side].modes[i]);
sep = " ";
}
fprintf(fd, "\n");
@ -295,10 +427,7 @@ bool Settings_write(Settings* this) {
}
fprintf(fd, "# Beware! This file is rewritten by htop when settings are changed in the interface.\n");
fprintf(fd, "# The parser is also very primitive, and not human-friendly.\n");
writeFields(fd, this->fields, "fields");
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->direction);
fprintf(fd, "fields="); writeFields(fd, this->screens[0]->fields, false);
fprintf(fd, "hide_threads=%d\n", (int) this->hideThreads);
fprintf(fd, "hide_kernel_threads=%d\n", (int) this->hideKernelThreads);
fprintf(fd, "hide_userland_threads=%d\n", (int) this->hideUserlandThreads);
@ -308,7 +437,6 @@ bool Settings_write(Settings* this) {
fprintf(fd, "highlight_base_name=%d\n", (int) this->highlightBaseName);
fprintf(fd, "highlight_megabytes=%d\n", (int) this->highlightMegabytes);
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
fprintf(fd, "cpu_count_from_zero=%d\n", (int) this->countCPUsFromZero);
@ -320,6 +448,23 @@ bool Settings_write(Settings* this) {
fprintf(fd, "left_meter_modes="); writeMeterModes(this, fd, 0);
fprintf(fd, "right_meters="); writeMeters(this, fd, 1);
fprintf(fd, "right_meter_modes="); writeMeterModes(this, fd, 1);
// Legacy compatibility with older versions of htop
fprintf(fd, "tree_view=%d\n", (int) this->screens[0]->treeView);
// This "-1" is for compatibility with the older enum format.
fprintf(fd, "sort_key=%d\n", (int) this->screens[0]->sortKey-1);
fprintf(fd, "sort_direction=%d\n", (int) this->screens[0]->direction);
if (this->screens && this->screens[0]) {
for (unsigned int i = 0; i < this->nScreens; i++) {
ScreenSettings* ss = this->screens[i];
fprintf(fd, "screen:%s=", ss->name);
writeFields(fd, ss->fields, true);
fprintf(fd, ".tree_view=%d\n", (int) ss->treeView);
fprintf(fd, ".sort_key=%s\n", toFieldName(ss->sortKey));
fprintf(fd, ".sort_direction=%d\n", (int) ss->direction);
}
}
fclose(fd);
return true;
}
@ -328,14 +473,11 @@ Settings* Settings_new(int cpuCount) {
Settings* this = xCalloc(1, sizeof(Settings));
this->sortKey = PERCENT_CPU;
this->direction = 1;
this->hideThreads = false;
this->shadowOtherUsers = false;
this->showThreadNames = false;
this->hideKernelThreads = false;
this->hideUserlandThreads = false;
this->treeView = false;
this->highlightBaseName = false;
this->highlightMegabytes = false;
this->detailedCPUTime = false;
@ -345,15 +487,8 @@ Settings* Settings_new(int cpuCount) {
this->showProgramPath = true;
this->highlightThreads = true;
this->fields = xCalloc(Platform_numberOfFields+1, sizeof(ProcessField));
// TODO: turn 'fields' into a Vector,
// (and ProcessFields into proper objects).
this->flags = 0;
ProcessField* defaults = Platform_defaultFields;
for (int i = 0; defaults[i]; i++) {
this->fields[i] = defaults[i];
this->flags |= Process_fields[defaults[i]].flags;
}
this->screens = xCalloc(sizeof(ScreenSettings*), 1);
this->nScreens = 0;
char* legacyDotfile = NULL;
char* rcfile = getenv("HTOPRC");
@ -414,15 +549,20 @@ Settings* Settings_new(int cpuCount) {
}
if (!ok) {
Settings_defaultMeters(this);
Settings_defaultScreens(this);
this->hideKernelThreads = true;
this->highlightMegabytes = true;
this->highlightThreads = true;
this->headerMargin = true;
}
this->ssIndex = 0;
this->ss = this->screens[this->ssIndex];
return this;
}
void Settings_invertSortOrder(Settings* this) {
void ScreenSettings_invertSortOrder(ScreenSettings* this) {
if (this->direction == 1)
this->direction = -1;
else

View File

@ -14,29 +14,44 @@ in the source distribution for its full text.
#include "Process.h"
#include <stdbool.h>
typedef struct {
const char* name;
const char* columns;
const char* sortKey;
} ScreenDefaults;
typedef struct {
int len;
char** names;
int* modes;
} MeterColumnSettings;
typedef struct {
char* name;
ProcessField* fields;
int flags;
int direction;
ProcessField sortKey;
bool treeView;
} ScreenSettings;
typedef struct Settings_ {
char* filename;
MeterColumnSettings columns[2];
MeterColumnSettings meterColumns[2];
ScreenSettings** screens;
unsigned int nScreens;
unsigned int ssIndex;
ScreenSettings* ss;
ProcessField* fields;
int flags;
int colorScheme;
int delay;
int cpuCount;
int direction;
ProcessField sortKey;
bool countCPUsFromZero;
bool detailedCPUTime;
bool treeView;
bool showProgramPath;
bool hideThreads;
bool shadowOtherUsers;
@ -58,12 +73,18 @@ typedef struct Settings_ {
#endif
/*
*/
void Settings_delete(Settings* this);
ScreenSettings* Settings_newScreen(Settings* this, const char* name, const char* line);
bool Settings_write(Settings* this);
Settings* Settings_new(int cpuCount);
void Settings_invertSortOrder(Settings* this);
void ScreenSettings_invertSortOrder(ScreenSettings* this);
#endif

View File

@ -41,10 +41,10 @@ Panel* SignalsPanel_new() {
}
#if (defined(SIGRTMIN) && defined(SIGRTMAX))
if (SIGRTMAX - SIGRTMIN <= 100) {
static char buf[15];
static char buf[16];
for (int sig = SIGRTMIN; sig <= SIGRTMAX; i++, sig++) {
int n = sig - SIGRTMIN;
xSnprintf(buf, 15, "%2d SIGRTMIN%-+3d", sig, n);
xSnprintf(buf, 16, "%2d SIGRTMIN%-+3d", sig, n);
if (n == 0) {
buf[11] = '\0';
}

View File

@ -27,11 +27,11 @@ in the source distribution for its full text.
*/
char* String_cat(const char* s1, const char* s2) {
int l1 = strlen(s1);
int l2 = strlen(s2);
size_t l1 = strlen(s1);
size_t l2 = strlen(s2);
char* out = xMalloc(l1 + l2 + 1);
strncpy(out, s1, l1);
strncpy(out+l1, s2, l2+1);
strncpy(out + l1, s2, l2 + 1);
return out;
}
@ -39,7 +39,7 @@ char* String_trim(const char* in) {
while (in[0] == ' ' || in[0] == '\t' || in[0] == '\n') {
in++;
}
int len = strlen(in);
size_t len = strlen(in);
while (len > 0 && (in[len-1] == ' ' || in[len-1] == '\t' || in[len-1] == '\n')) {
len--;
}
@ -80,7 +80,7 @@ char** String_split(const char* s, char sep, int* n) {
s += size + 1;
}
if (s[0] != '\0') {
int size = strlen(s);
size_t size = strlen(s);
char* token = xMalloc(size + 1);
strncpy(token, s, size + 1);
out[ctr] = token;

View File

@ -86,7 +86,7 @@ void TraceScreen_draw(InfoScreen* this) {
mvhline(0, 0, ' ', COLS);
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm);
attrset(CRT_colors[DEFAULT_COLOR]);
IncSet_drawBar(this->inc);
IncSet_drawBar(this->inc, CRT_colors[FUNCTION_BAR]);
}
bool TraceScreen_forkTracer(TraceScreen* this) {
@ -101,7 +101,7 @@ bool TraceScreen_forkTracer(TraceScreen* this) {
int ok = fcntl(this->fdpair[1], F_SETFL, O_NONBLOCK);
if (ok != -1) {
xSnprintf(buffer, sizeof(buffer), "%d", this->super.process->pid);
execlp("strace", "strace", "-p", buffer, NULL);
execlp("strace", "strace", "-s", "512", "-p", buffer, NULL);
}
const char* message = "Could not execute 'strace'. Please make sure it is available in your $PATH.";
ssize_t written = write(this->fdpair[1], message, strlen(message));

View File

@ -2,7 +2,7 @@
# Process this file with autoconf to produce a configure script.
AC_PREREQ(2.65)
AC_INIT([htop],[2.1.0],[hisham@gobolinux.org])
AC_INIT([htop],[3.0.0beta4],[hisham@gobolinux.org])
SOURCE_DATE_EPOCH="${SOURCE_DATE_EPOCH:-$(date +%s)}"
year=$(date -u -d "@$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u -r "$SOURCE_DATE_EPOCH" "+%Y" 2>/dev/null || date -u "+%Y")
@ -43,6 +43,9 @@ dragonfly*)
darwin*)
my_htop_platform=darwin
;;
solaris*)
my_htop_platform=solaris
;;
*)
my_htop_platform=unsupported
;;
@ -238,6 +241,12 @@ if test "$my_htop_platform" = "openbsd"; then
AC_CHECK_LIB([kvm], [kvm_open], [], [missing_libraries="$missing_libraries libkvm"])
fi
if test "$my_htop_platform" = "solaris"; then
AC_CHECK_LIB([kstat], [kstat_open], [], [missing_libraries="$missing_libraries libkstat"])
AC_CHECK_LIB([proc], [Pgrab_error], [], [missing_libraries="$missing_libraries libproc"])
AC_CHECK_LIB([malloc], [free], [], [missing_libraries="$missing_libraries libmalloc"])
fi
AC_ARG_ENABLE(linux_affinity, [AS_HELP_STRING([--enable-linux-affinity], [enable Linux sched_setaffinity and sched_getaffinity for affinity support, disables hwloc])], ,enable_linux_affinity="yes")
if test "x$enable_linux_affinity" = xyes -a "x$cross_compiling" = xno; then
AC_MSG_CHECKING([for usable sched_setaffinity])
@ -272,6 +281,52 @@ then
AC_DEFINE(HAVE_SETUID_ENABLED, 1, [Define if setuid support should be enabled.])
fi
AC_ARG_ENABLE(perfcounters, [AS_HELP_STRING([--enable-perfcounters], [enable hardware performance counters])],, enable_perfcounters="yes")
if test "x$enable_perfcounters" = "xyes" -a "$my_htop_platform" = "linux"
then
AC_DEFINE(HAVE_PERFCOUNTERS, 1, [Define if hardware performance counter support should be enabled.])
AC_CHECK_HEADERS([linux/perf_counter.h], [have_perf_counter=yes],
[have_perf_counter=no])
AC_CHECK_HEADERS([linux/perf_event.h], [have_perf_event=yes],
[have_perf_event=no])
if test "x${have_perf_counter}" = xno -a "x${have_perf_event}" = xno; then
os=`uname -s -r`
AC_MSG_FAILURE([
------------------------------------------------------------
Could not locate linux/perf_count.h or linux/perf_event.h.
Are performance counters supported on this machine?
Linux 2.6.31+ is required.
uname reports: ${os}
------------------------------------------------------------])
fi
# Check for hardware architecture
no_target=yes
AC_MSG_CHECKING([hardware])
hw=`uname -m`
case $hw in
x86_64 | i386 | i686 ) :
AC_MSG_RESULT([x86])
AC_DEFINE([TARGET_X86], [1], [Define to 1 if the target is x86.])
no_target=no
;;
unknown ) :
AC_MSG_RESULT([unknown])
AC_MSG_WARN([Could not detect architecture])
;;
* ) :
AC_MSG_RESULT([$hw])
;;
esac
if test x$no_target = xyes; then
AC_DEFINE([NOTARGET], [1], [Define to 1 when no specific target is supported.])
fi
fi
AC_ARG_ENABLE(delayacct, [AS_HELP_STRING([--enable-delayacct], [enable linux delay accounting])],, enable_delayacct="no")
if test "x$enable_delayacct" = xyes
then
@ -302,6 +357,7 @@ AM_CONDITIONAL([HTOP_FREEBSD], [test "$my_htop_platform" = freebsd])
AM_CONDITIONAL([HTOP_DRAGONFLYBSD], [test "$my_htop_platform" = dragonflybsd])
AM_CONDITIONAL([HTOP_OPENBSD], [test "$my_htop_platform" = openbsd])
AM_CONDITIONAL([HTOP_DARWIN], [test "$my_htop_platform" = darwin])
AM_CONDITIONAL([HTOP_SOLARIS], [test "$my_htop_platform" = solaris])
AM_CONDITIONAL([HTOP_UNSUPPORTED], [test "$my_htop_platform" = unsupported])
AC_SUBST(my_htop_platform)
AC_CONFIG_FILES([Makefile htop.1])

View File

@ -18,6 +18,8 @@ in the source distribution for its full text.
#include <sys/mman.h>
#include <utmpx.h>
#include <err.h>
#include <sys/sysctl.h>
#include <stdbool.h>
/*{
#include "ProcessList.h"
@ -38,19 +40,51 @@ typedef struct DarwinProcessList_ {
}*/
typedef struct kern {
short int version[3];
} kern_;
static void getKernelVersion(struct kern *k) {
static short int version_[3] = {0};
if (!version_[0]) {
// just in case it fails someday
version_[0] = version_[1] = version_[2] = -1;
char str[256] = {0};
size_t size = sizeof(str);
int ret = sysctlbyname("kern.osrelease", str, &size, NULL, 0);
if (ret == 0) sscanf(str, "%hd.%hd.%hd", &version_[0], &version_[1], &version_[2]);
}
memcpy(k->version, version_, sizeof(version_));
}
static int compareKernelVersion(short int major, short int minor, short int component) {
/*
compare the given os version with the one installed returns:
0 if equals the installed version
positive value if less than the installed version
negative value if more than the installed version
*/
struct kern k;
getKernelVersion(&k);
if ( k.version[0] != major) return k.version[0] - major;
if ( k.version[1] != minor) return k.version[1] - minor;
if ( k.version[2] != component) return k.version[2] - component;
return 0;
}
void ProcessList_getHostInfo(host_basic_info_data_t *p) {
mach_msg_type_number_t info_size = HOST_BASIC_INFO_COUNT;
if(0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
CRT_fatalError("Unable to retrieve host info\n");
if (0 != host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)p, &info_size)) {
CRT_fatalError("Unable to retrieve host info\n");
}
}
void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p) {
if(NULL != p && NULL != *p) {
if(0 != munmap(*p, vm_page_size)) {
CRT_fatalError("Unable to free old CPU load information\n");
}
if (NULL != p && NULL != *p) {
if (0 != munmap(*p, vm_page_size)) {
CRT_fatalError("Unable to free old CPU load information\n");
}
}
*p = NULL;
@ -61,7 +95,7 @@ unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
unsigned cpu_count;
// TODO Improving the accuracy of the load counts woule help a lot.
if(0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) {
if (0 != host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, &cpu_count, (processor_info_array_t *)p, &info_size)) {
CRT_fatalError("Unable to retrieve CPU info\n");
}
@ -69,10 +103,10 @@ unsigned ProcessList_allocateCPULoadInfo(processor_cpu_load_info_t *p) {
}
void ProcessList_getVMStats(vm_statistics_t p) {
mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
mach_msg_type_number_t info_size = HOST_VM_INFO_COUNT;
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0)
CRT_fatalError("Unable to retrieve VM statistics\n");
if (host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)p, &info_size) != 0)
CRT_fatalError("Unable to retrieve VM statistics\n");
}
struct kinfo_proc *ProcessList_getKInfoProcs(size_t *count) {
@ -127,61 +161,65 @@ void ProcessList_delete(ProcessList* this) {
}
void ProcessList_goThroughEntries(ProcessList* super) {
DarwinProcessList *dpl = (DarwinProcessList *)super;
bool preExisting = true;
struct kinfo_proc *ps;
size_t count;
DarwinProcess *proc;
struct timeval tv;
DarwinProcessList *dpl = (DarwinProcessList *)super;
bool preExisting = true;
struct kinfo_proc *ps;
size_t count;
DarwinProcess *proc;
struct timeval tv;
gettimeofday(&tv, NULL); /* Start processing time */
gettimeofday(&tv, NULL); /* Start processing time */
/* Update the global data (CPU times and VM stats) */
ProcessList_freeCPULoadInfo(&dpl->prev_load);
dpl->prev_load = dpl->curr_load;
ProcessList_allocateCPULoadInfo(&dpl->curr_load);
ProcessList_getVMStats(&dpl->vm_stats);
/* Update the global data (CPU times and VM stats) */
ProcessList_freeCPULoadInfo(&dpl->prev_load);
dpl->prev_load = dpl->curr_load;
ProcessList_allocateCPULoadInfo(&dpl->curr_load);
ProcessList_getVMStats(&dpl->vm_stats);
/* Get the time difference */
dpl->global_diff = 0;
for(int i = 0; i < dpl->super.cpuCount; ++i) {
for(size_t j = 0; j < CPU_STATE_MAX; ++j) {
dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j];
}
}
/* Get the time difference */
dpl->global_diff = 0;
for(int i = 0; i < dpl->super.cpuCount; ++i) {
for(size_t j = 0; j < CPU_STATE_MAX; ++j) {
dpl->global_diff += dpl->curr_load[i].cpu_ticks[j] - dpl->prev_load[i].cpu_ticks[j];
}
}
/* Clear the thread counts */
super->kernelThreads = 0;
super->userlandThreads = 0;
super->totalTasks = 0;
super->runningTasks = 0;
/* Clear the thread counts */
super->kernelThreads = 0;
super->userlandThreads = 0;
super->totalTasks = 0;
super->runningTasks = 0;
/* We use kinfo_procs for initial data since :
*
* 1) They always succeed.
* 2) The contain the basic information.
*
* We attempt to fill-in additional information with libproc.
*/
ps = ProcessList_getKInfoProcs(&count);
/* We use kinfo_procs for initial data since :
*
* 1) They always succeed.
* 2) The contain the basic information.
*
* We attempt to fill-in additional information with libproc.
*/
ps = ProcessList_getKInfoProcs(&count);
for(size_t i = 0; i < count; ++i) {
proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new);
for(size_t i = 0; i < count; ++i) {
proc = (DarwinProcess *)ProcessList_getProcess(super, ps[i].kp_proc.p_pid, &preExisting, (Process_New)DarwinProcess_new);
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl);
DarwinProcess_setFromKInfoProc(&proc->super, &ps[i], tv.tv_sec, preExisting);
DarwinProcess_setFromLibprocPidinfo(proc, dpl);
// Disabled due to bug in macOS High Sierra
// DarwinProcess_scanThreads(proc);
// Disabled for High Sierra due to bug in macOS High Sierra
bool isScanThreadSupported = ! ( compareKernelVersion(17, 0, 0) >= 0 && compareKernelVersion(17, 5, 0) < 0);
super->totalTasks += 1;
if (isScanThreadSupported){
DarwinProcess_scanThreads(proc);
}
if(!preExisting) {
proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid);
super->totalTasks += 1;
ProcessList_add(super, &proc->super);
}
}
if (!preExisting) {
proc->super.user = UsersTable_getRef(super->usersTable, proc->super.st_uid);
free(ps);
ProcessList_add(super, &proc->super);
}
}
free(ps);
}

View File

@ -26,6 +26,7 @@ typedef struct DarwinProcessList_ {
} DarwinProcessList;
void ProcessList_getHostInfo(host_basic_info_data_t *p);
void ProcessList_freeCPULoadInfo(processor_cpu_load_info_t *p);

View File

@ -25,13 +25,26 @@ in the source distribution for its full text.
#include "CPUMeter.h"
#include "BatteryMeter.h"
#include "DarwinProcess.h"
typedef enum DarwinProcessFields {
LAST_PROCESSFIELD = 100,
} DarwinProcessField;
}*/
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
@ -97,7 +110,7 @@ ProcessFieldData Process_fields[] = {
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[100] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
MeterClass* Platform_meterTypes[] = {

View File

@ -16,11 +16,18 @@ in the source distribution for its full text.
#include "BatteryMeter.h"
#include "DarwinProcess.h"
typedef enum DarwinProcessFields {
LAST_PROCESSFIELD = 100,
} DarwinProcessField;
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
extern ProcessField Platform_defaultFields[];
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern const SignalItem Platform_signals[];

View File

@ -40,7 +40,15 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
int Platform_numberOfFields = LAST_PROCESSFIELD;

View File

@ -21,7 +21,9 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
extern ProcessField Platform_defaultFields[];
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern int Platform_numberOfFields;

View File

@ -39,6 +39,16 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;

View File

@ -20,6 +20,10 @@ extern ProcessFieldData Process_fields[];
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;

19
htop.c
View File

@ -42,6 +42,7 @@ static void printHelpFlag() {
"-d --delay=DELAY Set the delay between updates, in tenths of seconds\n"
"-h --help Print this help screen\n"
"-s --sort-key=COLUMN Sort by COLUMN (try --sort-key=help for a list)\n"
"-t --tree Show the tree view by default\n"
"-u --user=USERNAME Show only processes of a given user\n"
"-p --pid=PID,[,PID,PID...] Show only the given PIDs\n"
"-v --version Print version info\n"
@ -61,6 +62,7 @@ typedef struct CommandLineSettings_ {
int sortKey;
int delay;
bool useColors;
bool treeView;
} CommandLineSettings;
static CommandLineSettings parseArguments(int argc, char** argv) {
@ -71,6 +73,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
.sortKey = 0,
.delay = -1,
.useColors = true,
.treeView = false,
};
static struct option long_opts[] =
@ -82,6 +85,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
{"user", required_argument, 0, 'u'},
{"no-color", no_argument, 0, 'C'},
{"no-colour",no_argument, 0, 'C'},
{"tree", no_argument, 0, 't'},
{"pid", required_argument, 0, 'p'},
{"io", no_argument, 0, 'i'},
{0,0,0,0}
@ -89,7 +93,7 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
int opt, opti=0;
/* Parse arguments */
while ((opt = getopt_long(argc, argv, "hvCs:d:u:p:i", long_opts, &opti))) {
while ((opt = getopt_long(argc, argv, "hvCst::d:u:p:i", long_opts, &opti))) {
if (opt == EOF) break;
switch (opt) {
case 'h':
@ -127,6 +131,9 @@ static CommandLineSettings parseArguments(int argc, char** argv) {
case 'C':
flags.useColors = false;
break;
case 't':
flags.treeView = true;
break;
case 'p': {
char* argCopy = xStrdup(optarg);
char* saveptr;
@ -197,18 +204,20 @@ int main(int argc, char** argv) {
settings->delay = flags.delay;
if (!flags.useColors)
settings->colorScheme = COLORSCHEME_MONOCHROME;
if (flags.treeView)
settings->screens[0]->treeView = true;
CRT_init(settings->delay, settings->colorScheme);
MainPanel* panel = MainPanel_new();
ProcessList_setPanel(pl, (Panel*) panel);
MainPanel_updateTreeFunctions(panel, settings->treeView);
MainPanel_updateTreeFunctions(panel, settings->screens[0]->treeView);
if (flags.sortKey > 0) {
settings->sortKey = flags.sortKey;
settings->treeView = false;
settings->direction = 1;
settings->screens[0]->sortKey = flags.sortKey;
settings->screens[0]->treeView = false;
settings->screens[0]->direction = 1;
}
ProcessList_printHeader(pl, Panel_getHeader((Panel*)panel));

View File

@ -18,11 +18,24 @@ in the source distribution for its full text.
/*{
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_OOM 0x1000
#include "PerfCounter.h"
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100L
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200L
#define PROCESS_FLAG_LINUX_VSERVER 0x0400L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800L
#define PROCESS_FLAG_LINUX_OOM 0x1000L
#define PROCESS_FLAG_LINUX_HPC 0xff0000L
#define PROCESS_FLAG_LINUX_HPC_CYCLE 0x10000L
#define PROCESS_FLAG_LINUX_HPC_INSN 0x20000L
#define PROCESS_FLAG_LINUX_HPC_MISS 0x40000L
#define PROCESS_FLAG_LINUX_HPC_BMISS 0x80000L
#define PROCESS_FLAG_LINUX_HPC_L1DR 0x100000L
#define PROCESS_FLAG_LINUX_HPC_L1DW 0x200000L
#define PROCESS_FLAG_LINUX_HPC_L1DRM 0x400000L
#define PROCESS_FLAG_LINUX_HPC_L1DWM 0x800000L
typedef enum UnsupportedProcessFields {
FLAGS = 9,
@ -86,13 +99,25 @@ typedef enum LinuxProcessFields {
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
LAST_PROCESSFIELD = 119,
#ifdef HAVE_PERFCOUNTERS
IPC = 119,
MCYCLE = 120,
MINSTR = 121,
PERCENT_MISS = 122,
PERCENT_BMISS = 123,
L1DREADS = 124,
L1DRMISSES = 125,
L1DWRITES = 126,
L1DWMISSES = 127,
#endif
LAST_PROCESSFIELD = 128,
} LinuxProcessField;
#include "IOPriority.h"
typedef struct LinuxProcess_ {
Process super;
bool isKernelThread;
IOPriority ioPriority;
unsigned long int cminflt;
unsigned long int cmajflt;
@ -139,10 +164,29 @@ typedef struct LinuxProcess_ {
float blkio_delay_percent;
float swapin_delay_percent;
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter* cycleCounter;
PerfCounter* insnCounter;
PerfCounter* missCounter;
PerfCounter* brCounter;
PerfCounter* l1drCounter;
PerfCounter* l1drmCounter;
PerfCounter* l1dwCounter;
PerfCounter* l1dwmCounter;
float ipc;
float mcycle;
float minstr;
float pMiss;
float pBMiss;
float l1dr;
float l1drm;
float l1dw;
float l1dwm;
#endif
} LinuxProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->pgrp == 0)
#define Process_isKernelThread(_process) (((LinuxProcess*)(_process))->isKernelThread)
#endif
#ifndef Process_isUserlandThread
@ -230,9 +274,20 @@ ProcessFieldData Process_fields[] = {
[OOM] = { .name = "OOM", .title = " OOM ", .description = "OOM (Out-of-Memory) killer score", .flags = PROCESS_FLAG_LINUX_OOM, },
[IO_PRIORITY] = { .name = "IO_PRIORITY", .title = "IO ", .description = "I/O priority", .flags = PROCESS_FLAG_LINUX_IOPRIO, },
#ifdef HAVE_DELAYACCT
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPUD% ", .description = "CPU delay %", .flags = 0, },
[PERCENT_CPU_DELAY] = { .name = "PERCENT_CPU_DELAY", .title = "CPD% ", .description = "CPU delay %", .flags = 0, },
[PERCENT_IO_DELAY] = { .name = "PERCENT_IO_DELAY", .title = "IOD% ", .description = "Block I/O delay %", .flags = 0, },
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWAPD% ", .description = "Swapin delay %", .flags = 0, },
[PERCENT_SWAP_DELAY] = { .name = "PERCENT_SWAP_DELAY", .title = "SWD% ", .description = "Swapin delay %", .flags = 0, },
#endif
#ifdef HAVE_PERFCOUNTERS
[IPC] = { .name = "IPC", .title = " IPC ", .description = "Executed instructions per cycle", .flags = PROCESS_FLAG_LINUX_HPC_CYCLE | PROCESS_FLAG_LINUX_HPC_INSN, },
[MCYCLE] = { .name = "MCYCLE", .title = " Mcycle ", .description = "Cycles (millions)", .flags = PROCESS_FLAG_LINUX_HPC_CYCLE, },
[MINSTR] = { .name = "MINSTR", .title = " Minstr ", .description = "Instructions (millions)", .flags = PROCESS_FLAG_LINUX_HPC_INSN, },
[PERCENT_MISS] = { .name = "PERCENT_MISS", .title = "MIS% ", .description = "Cache misses per 100 instructions", .flags = PROCESS_FLAG_LINUX_HPC_MISS | PROCESS_FLAG_LINUX_HPC_INSN, },
[PERCENT_BMISS] = { .name = "PERCENT_BMISS", .title = "BrM% ", .description = "Branch misprediction per 100 instructions", .flags = PROCESS_FLAG_LINUX_HPC_BMISS | PROCESS_FLAG_LINUX_HPC_INSN, },
[L1DREADS] = { .name = "L1DREADS", .title = " L1Dread ", .description = "L1 data cache: reads (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DR, },
[L1DRMISSES] = { .name = "L1DRMISSES", .title = " R miss ", .description = "L1 data cache: reads misses (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DRM, },
[L1DWRITES] = { .name = "L1DWRITES", .title = " L1Dwrite ", .description = "L1D data cache: writes (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DW, },
[L1DWMISSES] = { .name = "L1DWMISSES", .title = " W miss ", .description = "L1D data cache: write misses (thousands)", .flags = PROCESS_FLAG_LINUX_HPC_L1DWM, },
#endif
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
@ -273,6 +328,10 @@ void Process_delete(Object* cast) {
Process_done((Process*)cast);
#ifdef HAVE_CGROUP
free(this->cgroup);
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter_delete(this->cycleCounter);
PerfCounter_delete(this->insnCounter);
#endif
free(this->ttyDevice);
free(this);
@ -306,14 +365,43 @@ bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio) {
return (LinuxProcess_updateIOPriority(this) == ioprio);
}
#ifdef HAVE_DELAYACCT
void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) {
if (delay_percent == -1LL) {
xSnprintf(buffer, n, " N/A ");
} else {
xSnprintf(buffer, n, "%4.1f ", delay_percent);
}
#if HAVE_DELAYACCT || HAVE_PERFCOUNTERS
static char* perfFmt[] = {
"%6.2f ",
NULL,
NULL,
NULL,
NULL,
NULL,
"%6.1f ",
"%7.1f ",
"%8.2f ",
"%9.1f ",
};
static char* perfNA[] = {
" N/A ",
NULL,
NULL,
NULL,
NULL,
NULL,
" N/A ",
" N/A ",
" N/A ",
" N/A ",
};
static inline void LinuxProcess_printPerfCounter(float val, int len, char* buffer, int n, int* attr) {
if (val != -1) {
xSnprintf(buffer, n, perfFmt[len], val);
} else {
xSnprintf(buffer, n, perfNA[len]);
*attr = CRT_colors[PROCESS_SHADOW];
}
}
#endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field) {
@ -390,9 +478,20 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
break;
}
#ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY: LinuxProcess_printDelay(lp->cpu_delay_percent, buffer, n); break;
case PERCENT_IO_DELAY: LinuxProcess_printDelay(lp->blkio_delay_percent, buffer, n); break;
case PERCENT_SWAP_DELAY: LinuxProcess_printDelay(lp->swapin_delay_percent, buffer, n); break;
case PERCENT_CPU_DELAY: Process_printPercentage(lp->cpu_delay_percent, buffer, n, &attr); break;
case PERCENT_IO_DELAY: Process_printPercentage(lp->blkio_delay_percent, buffer, n, &attr); break;
case PERCENT_SWAP_DELAY: Process_printPercentage(lp->swapin_delay_percent, buffer, n, &attr); break;
#endif
#ifdef HAVE_PERFCOUNTERS
case PERCENT_MISS: Process_printPercentage(lp->pMiss, buffer, n, &attr); break;
case PERCENT_BMISS: Process_printPercentage(lp->pBMiss, buffer, n, &attr); break;
case IPC: LinuxProcess_printPerfCounter(lp->ipc, 0, buffer, n, &attr); break;
case MCYCLE: LinuxProcess_printPerfCounter(lp->mcycle, 8, buffer, n, &attr); break;
case MINSTR: LinuxProcess_printPerfCounter(lp->minstr, 8, buffer, n, &attr); break;
case L1DREADS: LinuxProcess_printPerfCounter(lp->l1dr, 9, buffer, n, &attr); break;
case L1DRMISSES: LinuxProcess_printPerfCounter(lp->l1drm, 9, buffer, n, &attr); break;
case L1DWRITES: LinuxProcess_printPerfCounter(lp->l1dw, 9, buffer, n, &attr); break;
case L1DWMISSES: LinuxProcess_printPerfCounter(lp->l1dwm, 9, buffer, n, &attr); break;
#endif
default:
Process_writeField((Process*)this, str, field);
@ -401,10 +500,12 @@ void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field)
RichString_append(str, attr, buffer);
}
#define COMPARE_FIELD(_f) return (p2->_f > p1->_f ? 1 : -1)
long LinuxProcess_compare(const void* v1, const void* v2) {
LinuxProcess *p1, *p2;
Settings *settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
if (settings->ss->direction == 1) {
p1 = (LinuxProcess*)v1;
p2 = (LinuxProcess*)v2;
} else {
@ -412,7 +513,7 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
p1 = (LinuxProcess*)v2;
}
long long diff;
switch ((int)settings->sortKey) {
switch ((int)settings->ss->sortKey) {
case M_DRS:
return (p2->m_drs - p1->m_drs);
case M_DT:
@ -456,12 +557,20 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
case OOM:
return (p2->oom - p1->oom);
#ifdef HAVE_DELAYACCT
case PERCENT_CPU_DELAY:
return (p2->cpu_delay_percent > p1->cpu_delay_percent ? 1 : -1);
case PERCENT_IO_DELAY:
return (p2->blkio_delay_percent > p1->blkio_delay_percent ? 1 : -1);
case PERCENT_SWAP_DELAY:
return (p2->swapin_delay_percent > p1->swapin_delay_percent ? 1 : -1);
case PERCENT_CPU_DELAY: COMPARE_FIELD(cpu_delay_percent);
case PERCENT_IO_DELAY: COMPARE_FIELD(blkio_delay_percent);
case PERCENT_SWAP_DELAY: COMPARE_FIELD(swapin_delay_percent);
#endif
#ifdef HAVE_PERFCOUNTERS
case PERCENT_MISS: COMPARE_FIELD(pMiss);
case PERCENT_BMISS: COMPARE_FIELD(pBMiss);
case IPC: COMPARE_FIELD(ipc);
case MCYCLE: COMPARE_FIELD(mcycle);
case MINSTR: COMPARE_FIELD(minstr);
case L1DREADS: COMPARE_FIELD(l1dr);
case L1DRMISSES: COMPARE_FIELD(l1drm);
case L1DWRITES: COMPARE_FIELD(l1dw);
case L1DWMISSES: COMPARE_FIELD(l1dwm);
#endif
case IO_PRIORITY:
return LinuxProcess_effectiveIOPriority(p1) - LinuxProcess_effectiveIOPriority(p2);
@ -475,4 +584,3 @@ long LinuxProcess_compare(const void* v1, const void* v2) {
bool Process_isThread(Process* this) {
return (Process_isUserlandThread(this) || Process_isKernelThread(this));
}

View File

@ -10,11 +10,24 @@ in the source distribution for its full text.
*/
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200
#define PROCESS_FLAG_LINUX_VSERVER 0x0400
#define PROCESS_FLAG_LINUX_CGROUP 0x0800
#define PROCESS_FLAG_LINUX_OOM 0x1000
#include "PerfCounter.h"
#define PROCESS_FLAG_LINUX_IOPRIO 0x0100L
#define PROCESS_FLAG_LINUX_OPENVZ 0x0200L
#define PROCESS_FLAG_LINUX_VSERVER 0x0400L
#define PROCESS_FLAG_LINUX_CGROUP 0x0800L
#define PROCESS_FLAG_LINUX_OOM 0x1000L
#define PROCESS_FLAG_LINUX_HPC 0xff0000L
#define PROCESS_FLAG_LINUX_HPC_CYCLE 0x10000L
#define PROCESS_FLAG_LINUX_HPC_INSN 0x20000L
#define PROCESS_FLAG_LINUX_HPC_MISS 0x40000L
#define PROCESS_FLAG_LINUX_HPC_BMISS 0x80000L
#define PROCESS_FLAG_LINUX_HPC_L1DR 0x100000L
#define PROCESS_FLAG_LINUX_HPC_L1DW 0x200000L
#define PROCESS_FLAG_LINUX_HPC_L1DRM 0x400000L
#define PROCESS_FLAG_LINUX_HPC_L1DWM 0x800000L
typedef enum UnsupportedProcessFields {
FLAGS = 9,
@ -78,13 +91,25 @@ typedef enum LinuxProcessFields {
PERCENT_IO_DELAY = 117,
PERCENT_SWAP_DELAY = 118,
#endif
LAST_PROCESSFIELD = 119,
#ifdef HAVE_PERFCOUNTERS
IPC = 119,
MCYCLE = 120,
MINSTR = 121,
PERCENT_MISS = 122,
PERCENT_BMISS = 123,
L1DREADS = 124,
L1DRMISSES = 125,
L1DWRITES = 126,
L1DWMISSES = 127,
#endif
LAST_PROCESSFIELD = 128,
} LinuxProcessField;
#include "IOPriority.h"
typedef struct LinuxProcess_ {
Process super;
bool isKernelThread;
IOPriority ioPriority;
unsigned long int cminflt;
unsigned long int cmajflt;
@ -131,10 +156,29 @@ typedef struct LinuxProcess_ {
float blkio_delay_percent;
float swapin_delay_percent;
#endif
#ifdef HAVE_PERFCOUNTERS
PerfCounter* cycleCounter;
PerfCounter* insnCounter;
PerfCounter* missCounter;
PerfCounter* brCounter;
PerfCounter* l1drCounter;
PerfCounter* l1drmCounter;
PerfCounter* l1dwCounter;
PerfCounter* l1dwmCounter;
float ipc;
float mcycle;
float minstr;
float pMiss;
float pBMiss;
float l1dr;
float l1drm;
float l1dw;
float l1dwm;
#endif
} LinuxProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->pgrp == 0)
#define Process_isKernelThread(_process) (((LinuxProcess*)(_process))->isKernelThread)
#endif
#ifndef Process_isUserlandThread
@ -166,15 +210,16 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
bool LinuxProcess_setIOPriority(LinuxProcess* this, IOPriority ioprio);
#ifdef HAVE_DELAYACCT
void LinuxProcess_printDelay(float delay_percent, char* buffer, int n);
#if HAVE_DELAYACCT || HAVE_PERFCOUNTERS
#endif
void LinuxProcess_writeField(Process* this, RichString* str, ProcessField field);
#define COMPARE_FIELD(_f) return (p2->_f > p1->_f ? 1 : -1)
long LinuxProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this);
#endif

View File

@ -653,9 +653,9 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
}
if (nl_send_sync(this->netlink_socket, msg) < 0) {
process->swapin_delay_percent = -1LL;
process->blkio_delay_percent = -1LL;
process->cpu_delay_percent = -1LL;
process->swapin_delay_percent = -1;
process->blkio_delay_percent = -1;
process->cpu_delay_percent = -1;
return;
}
@ -666,6 +666,77 @@ static void LinuxProcessList_readDelayAcctData(LinuxProcessList* this, LinuxProc
#endif
#ifdef HAVE_PERFCOUNTERS
#define READ_COUNTER(_b, _var, _flag, _type, _config) \
bool _b ## Ok = false; \
uint64_t _b ## Delta = 0; \
if (flags & _flag && lp->super.show) { \
if (!_var) { \
_var = PerfCounter_new(lp->super.pid, _type, _config); \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = 0; \
} else { \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = PerfCounter_delta(_var); \
} \
if (_b ## Ok) { \
} \
} else { \
if (_var) { \
PerfCounter_delete(_var); \
_var = NULL; \
} \
}
#define SET_IF(_ok, _var, _exp) \
if (_ok) { \
_var = _exp; \
} else { \
_var = -1; \
}
#define SET_IFNZ(_ok, _z, _var, _exp) \
if (_ok) { \
if (_z > 0) { \
_var = _exp; \
} else { \
_var = 0; \
} \
} else { \
_var = -1; \
}
#define L1DR (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DRM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#define L1DW (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DWM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
static void LinuxProcessList_readPerfCounters(LinuxProcess* lp, uint64_t flags) {
READ_COUNTER(c, lp->cycleCounter, PROCESS_FLAG_LINUX_HPC_CYCLE, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CPU_CYCLES);
READ_COUNTER(i, lp->insnCounter, PROCESS_FLAG_LINUX_HPC_INSN, PERF_TYPE_HARDWARE, PERF_COUNT_HW_INSTRUCTIONS);
READ_COUNTER(m, lp->missCounter, PROCESS_FLAG_LINUX_HPC_MISS, PERF_TYPE_HARDWARE, PERF_COUNT_HW_CACHE_MISSES);
READ_COUNTER(b, lp->brCounter, PROCESS_FLAG_LINUX_HPC_BMISS, PERF_TYPE_HARDWARE, PERF_COUNT_HW_BRANCH_MISSES);
READ_COUNTER(r, lp->l1drCounter, PROCESS_FLAG_LINUX_HPC_L1DR, PERF_TYPE_HW_CACHE, L1DR);
READ_COUNTER(R, lp->l1drmCounter, PROCESS_FLAG_LINUX_HPC_L1DRM, PERF_TYPE_HW_CACHE, L1DRM);
READ_COUNTER(w, lp->l1dwCounter, PROCESS_FLAG_LINUX_HPC_L1DW, PERF_TYPE_HW_CACHE, L1DW);
READ_COUNTER(W, lp->l1dwmCounter, PROCESS_FLAG_LINUX_HPC_L1DWM, PERF_TYPE_HW_CACHE, L1DWM);
SET_IF(cOk, lp->mcycle, (double)cDelta / 1000000);
SET_IF(iOk, lp->minstr, (double)iDelta / 1000000);
SET_IFNZ(cOk && iOk, cDelta, lp->ipc, (double)iDelta / cDelta);
SET_IFNZ(mOk && iOk, iDelta, lp->pMiss, 100 * ((double)mDelta / iDelta));
SET_IFNZ(bOk && iOk, iDelta, lp->pBMiss, 100 * ((double)bDelta / iDelta));
SET_IF(rOk, lp->l1dr, (double)rDelta / 1000);
SET_IF(ROk, lp->l1drm, (double)RDelta / 1000);
SET_IF(wOk, lp->l1dw, (double)wDelta / 1000);
SET_IF(WOk, lp->l1dwm, (double)WDelta / 1000);
}
#endif
static void setCommand(Process* process, const char* command, int len) {
if (process->comm && process->commLen >= len) {
strncpy(process->comm, command, len + 1);
@ -677,9 +748,6 @@ static void setCommand(Process* process, const char* command, int len) {
}
static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
if (Process_isKernelThread(process))
return true;
char filename[MAX_NAME+1];
xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
int fd = open(filename, O_RDONLY);
@ -691,7 +759,10 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
close(fd);
int tokenEnd = 0;
int lastChar = 0;
if (amtRead <= 0) {
if (amtRead == 0) {
((LinuxProcess*)process)->isKernelThread = true;
return true;
} else if (amtRead < 0) {
return false;
}
for (int i = 0; i < amtRead; i++) {
@ -761,6 +832,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
DIR* dir;
struct dirent* entry;
Settings* settings = pl->settings;
ScreenSettings* ss = settings->ss;
time_t curTime = tv.tv_sec;
#ifdef HAVE_TASKSTATS
@ -806,7 +878,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
LinuxProcessList_recurseProcTree(this, subdirname, proc, period, tv);
#ifdef HAVE_TASKSTATS
if (settings->flags & PROCESS_FLAG_IO)
if (ss->flags & PROCESS_FLAG_IO)
LinuxProcessList_readIoFile(lp, dirname, name, now);
#endif
@ -825,7 +897,7 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
free(lp->ttyDevice);
lp->ttyDevice = LinuxProcessList_updateTtyDevice(this->ttyDrivers, proc->tty_nr);
}
if (settings->flags & PROCESS_FLAG_LINUX_IOPRIO)
if (ss->flags & PROCESS_FLAG_LINUX_IOPRIO)
LinuxProcess_updateIOPriority(lp);
float percent_cpu = (lp->utime + lp->stime - lasttimes) / period * 100.0;
proc->percent_cpu = CLAMP(percent_cpu, 0.0, cpus * 100.0);
@ -840,13 +912,13 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
#ifdef HAVE_OPENVZ
if (settings->flags & PROCESS_FLAG_LINUX_OPENVZ) {
if (ss->flags & PROCESS_FLAG_LINUX_OPENVZ) {
LinuxProcessList_readOpenVZData(lp, dirname, name);
}
#endif
#ifdef HAVE_VSERVER
if (settings->flags & PROCESS_FLAG_LINUX_VSERVER) {
if (ss->flags & PROCESS_FLAG_LINUX_VSERVER) {
LinuxProcessList_readVServerData(lp, dirname, name);
}
#endif
@ -869,13 +941,18 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
#endif
#ifdef HAVE_CGROUP
if (settings->flags & PROCESS_FLAG_LINUX_CGROUP)
if (ss->flags & PROCESS_FLAG_LINUX_CGROUP)
LinuxProcessList_readCGroupFile(lp, dirname, name);
#endif
if (settings->flags & PROCESS_FLAG_LINUX_OOM)
if (ss->flags & PROCESS_FLAG_LINUX_OOM)
LinuxProcessList_readOomData(lp, dirname, name);
#ifdef HAVE_PERFCOUNTERS
if (ss->flags & PROCESS_FLAG_LINUX_HPC)
LinuxProcessList_readPerfCounters(lp, ss->flags);
#endif
if (proc->state == 'Z' && (proc->basenameOffset == 0)) {
proc->basenameOffset = -1;
setCommand(proc, command, commLen);

View File

@ -121,6 +121,54 @@ void ProcessList_delete(ProcessList* pl);
#endif
#ifdef HAVE_PERFCOUNTERS
#define READ_COUNTER(_b, _var, _flag, _type, _config) \
bool _b ## Ok = false; \
uint64_t _b ## Delta = 0; \
if (flags & _flag && lp->super.show) { \
if (!_var) { \
_var = PerfCounter_new(lp->super.pid, _type, _config); \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = 0; \
} else { \
_b ## Ok = PerfCounter_read(_var); \
_b ## Delta = PerfCounter_delta(_var); \
} \
if (_b ## Ok) { \
} \
} else { \
if (_var) { \
PerfCounter_delete(_var); \
_var = NULL; \
} \
}
#define SET_IF(_ok, _var, _exp) \
if (_ok) { \
_var = _exp; \
} else { \
_var = -1; \
}
#define SET_IFNZ(_ok, _z, _var, _exp) \
if (_ok) { \
if (_z > 0) { \
_var = _exp; \
} else { \
_var = 0; \
} \
} else { \
_var = -1; \
}
#define L1DR (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DRM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_READ << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#define L1DW (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_ACCESS << 16))
#define L1DWM (PERF_COUNT_HW_CACHE_L1D | (PERF_COUNT_HW_CACHE_OP_WRITE << 8) | (PERF_COUNT_HW_CACHE_RESULT_MISS << 16))
#endif
void ProcessList_goThroughEntries(ProcessList* super);
#endif

135
linux/PerfCounter.c Normal file
View File

@ -0,0 +1,135 @@
/*
* This file is based on tiptop.
* by Erven ROHOU
* Copyright (c) 2011, 2012, 2014 Inria
* License: GNU General Public License version 2.
*/
#include "PerfCounter.h"
#include <errno.h>
#include <stdlib.h>
#include <stdio.h>
#include "XAlloc.h"
/*{
#include <config.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <inttypes.h>
#include <stdbool.h>
// The sys_perf_counter_open syscall and header files changed names
// between Linux 2.6.31 and 2.6.32. Do the mangling here.
#ifdef HAVE_LINUX_PERF_COUNTER_H
#include <linux/perf_counter.h>
#define STRUCT_NAME perf_counter_attr
#define SYSCALL_NUM __NR_perf_counter_open
#elif HAVE_LINUX_PERF_EVENT_H
#include <linux/perf_event.h>
#define STRUCT_NAME perf_event_attr
#define SYSCALL_NUM __NR_perf_event_open
#else
#error Sorry, performance counters not supported on this system.
#endif
typedef struct PerfCounter_ {
struct STRUCT_NAME events;
pid_t pid;
int fd;
uint64_t prevValue;
uint64_t value;
} PerfCounter;
#define PerfCounter_delta(pc_) ((pc_)->value - (pc_)->prevValue)
}*/
int PerfCounter_openFds = 0;
static int PerfCounter_fdLimit = -1;
static void PerfCounter_initFdLimit() {
char name[100] = { 0 }; /* needs to fit the name /proc/xxxx/limits */
snprintf(name, sizeof(name) - 1, "/proc/%d/limits", getpid());
FILE* f = fopen(name, "r");
if (f) {
char line[100];
while (fgets(line, 100, f)) {
int n = sscanf(line, "Max open files %d", &PerfCounter_fdLimit);
if (n) {
break;
}
}
fclose(f);
}
PerfCounter_fdLimit -= 20; /* keep some slack */
if (PerfCounter_fdLimit == 0) { /* something went wrong */
PerfCounter_fdLimit = 200; /* reasonable default? */
}
}
static long perf_event_open(struct STRUCT_NAME *hw_event, pid_t pid, int cpu, int group_fd, unsigned long flags) {
int ret = syscall(SYSCALL_NUM, hw_event, pid, cpu, group_fd, flags);
#if defined(__x86_64__) || defined(__i386__)
if (ret < 0 && ret > -4096) {
errno = -ret;
ret = -1;
}
#endif
return ret;
}
PerfCounter* PerfCounter_new(pid_t pid, uint32_t type, uint64_t config) {
if (PerfCounter_fdLimit == -1) {
PerfCounter_initFdLimit();
}
PerfCounter* this = xCalloc(sizeof(PerfCounter), 1);
this->pid = pid;
this->events.disabled = 0;
this->events.pinned = 1;
this->events.exclude_hv = 1;
this->events.exclude_kernel = 1;
this->events.type = type;
this->events.config = config;
if (PerfCounter_openFds < PerfCounter_fdLimit) {
this->fd = perf_event_open(&this->events, pid, -1, -1, 0);
} else {
this->fd = -1;
}
if (this->fd != -1) {
PerfCounter_openFds++;
}
return this;
}
void PerfCounter_delete(PerfCounter* this) {
if (!this) {
return;
}
if (this->fd != -1) {
PerfCounter_openFds--;
}
close(this->fd);
free(this);
}
bool PerfCounter_read(PerfCounter* this) {
if (this->fd == -1) {
return false;
}
uint64_t value;
int r = read(this->fd, &value, sizeof(value));
if (r != sizeof(value)) {
close(this->fd);
this->fd = perf_event_open(&this->events, this->pid, -1, -1, 0);
return false;
}
this->prevValue = this->value;
this->value = value;
return true;
}

54
linux/PerfCounter.h Normal file
View File

@ -0,0 +1,54 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_PerfCounter
#define HEADER_PerfCounter
/*
* This file is based on tiptop.
* by Erven ROHOU
* Copyright (c) 2011, 2012, 2014 Inria
* License: GNU General Public License version 2.
*/
#include <config.h>
#include <sys/types.h>
#include <unistd.h>
#include <asm/unistd.h>
#include <inttypes.h>
#include <stdbool.h>
// The sys_perf_counter_open syscall and header files changed names
// between Linux 2.6.31 and 2.6.32. Do the mangling here.
#ifdef HAVE_LINUX_PERF_COUNTER_H
#include <linux/perf_counter.h>
#define STRUCT_NAME perf_counter_attr
#define SYSCALL_NUM __NR_perf_counter_open
#elif HAVE_LINUX_PERF_EVENT_H
#include <linux/perf_event.h>
#define STRUCT_NAME perf_event_attr
#define SYSCALL_NUM __NR_perf_event_open
#else
#error Sorry, performance counters not supported on this system.
#endif
typedef struct PerfCounter_ {
struct STRUCT_NAME events;
pid_t pid;
int fd;
uint64_t prevValue;
uint64_t value;
} PerfCounter;
#define PerfCounter_delta(pc_) ((pc_)->value - (pc_)->prevValue)
extern int PerfCounter_openFds;
PerfCounter* PerfCounter_new(pid_t pid, uint32_t type, uint64_t config);
void PerfCounter_delete(PerfCounter* this);
bool PerfCounter_read(PerfCounter* this);
#endif

View File

@ -22,6 +22,7 @@ in the source distribution for its full text.
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "LinuxProcess.h"
#include "CRT.h"
#include <math.h>
#include <assert.h>
@ -35,16 +36,13 @@ in the source distribution for its full text.
#include "BatteryMeter.h"
#include "LinuxProcess.h"
#include "SignalsPanel.h"
#include "Settings.h"
}*/
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
//static ProcessField defaultIoFields[] = { PID, IO_PRIORITY, USER, IO_READ_RATE, IO_WRITE_RATE, IO_RATE, COMM, 0 };
int Platform_numberOfFields = LAST_PROCESSFIELD;
const SignalItem Platform_signals[] = {
@ -84,6 +82,31 @@ const SignalItem Platform_signals[] = {
{ .name = "31 SIGSYS", .number = 31 },
};
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT M_SHARE STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
{
.name = "I/O",
.columns = "PID USER IO_PRIORITY IO_RATE IO_READ_RATE IO_WRITE_RATE PERCENT_SWAP_DELAY PERCENT_IO_DELAY Command",
.sortKey = "IO_RATE",
},
{
.name = "Perf Counters",
.columns = "PID USER PERCENT_CPU PROCESSOR MCYCLE MINSTR IPC PERCENT_MISS PERCENT_BMISS Command",
.sortKey = "MCYCLE",
},
{
.name = "L1 Data Cache",
.columns = "PID USER PERCENT_CPU L1DREADS L1DRMISSES L1DWRITES L1DWMISSES Command",
.sortKey = "LD1READS",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
static Htop_Reaction Platform_actionSetIOPriority(State* st) {

View File

@ -14,17 +14,20 @@ in the source distribution for its full text.
#include "BatteryMeter.h"
#include "LinuxProcess.h"
#include "SignalsPanel.h"
#include "Settings.h"
#ifndef CLAMP
#define CLAMP(x,low,high) (((x)>(high))?(high):(((x)<(low))?(low):(x)))
#endif
extern ProcessField Platform_defaultFields[];
extern int Platform_numberOfFields;
extern const SignalItem Platform_signals[];
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern const unsigned int Platform_numberOfSignals;
void Platform_setBindings(Htop_Action* keys);

View File

@ -7,10 +7,65 @@ in the source distribution for its full text.
*/
#include "BatteryMeter.h"
#include <sys/sysctl.h>
#include <sys/sensors.h>
#include <errno.h>
void Battery_getData(double* level, ACPresence* isOnAC) {
// TODO
*level = -1;
*isOnAC = AC_ERROR;
static bool findDevice(const char* name, int* mib, struct sensordev* snsrdev, size_t* sdlen) {
for (int devn = 0;; devn++) {
mib[2] = devn;
if (sysctl(mib, 3, snsrdev, sdlen, NULL, 0) == -1) {
if (errno == ENXIO)
continue;
if (errno == ENOENT)
return false;
}
if (strcmp(name, snsrdev->xname) == 0) {
return true;
}
}
}
void Battery_getData(double* level, ACPresence* isOnAC) {
static int mib[] = {CTL_HW, HW_SENSORS, 0, 0, 0};
struct sensor s;
size_t slen = sizeof(struct sensor);
struct sensordev snsrdev;
size_t sdlen = sizeof(struct sensordev);
bool found = findDevice("acpibat0", mib, &snsrdev, &sdlen);
*level = -1;
if (found) {
/* last full capacity */
mib[3] = 7;
mib[4] = 0;
double last_full_capacity = 0;
if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
last_full_capacity = s.value;
}
if (last_full_capacity > 0) {
/* remaining capacity */
mib[3] = 7;
mib[4] = 3;
if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
double charge = s.value;
*level = 100*(charge / last_full_capacity);
if (charge >= last_full_capacity) {
*level = 100;
}
}
}
}
found = findDevice("acpiac0", mib, &snsrdev, &sdlen);
*isOnAC = AC_ERROR;
if (found) {
mib[3] = 9;
mib[4] = 0;
if (sysctl(mib, 5, &s, &slen, NULL, 0) != -1) {
*isOnAC = s.value;
}
}
}

View File

@ -12,5 +12,4 @@ in the source distribution for its full text.
void Battery_getData(double* level, ACPresence* isOnAC);
#endif

View File

@ -92,7 +92,15 @@ static int percentages(int cnt, int64_t *out, int64_t *new, int64_t *old, int64_
return (total_change);
}
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
int Platform_numberOfFields = LAST_PROCESSFIELD;

View File

@ -32,7 +32,9 @@ extern ProcessFieldData Process_fields[];
* The routine assumes modulo arithmetic. This function is especially
* useful on BSD machines for calculating cpu state percentages.
*/
extern ProcessField Platform_defaultFields[];
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern int Platform_numberOfFields;

View File

@ -12,6 +12,7 @@ ANY=1
COPY=2
SKIP=3
SKIPONE=4
COPYDEFINE=5
state = ANY
static = 0
@ -49,7 +50,11 @@ for line in file.readlines():
elif len(line) > 1:
static = 0
equals = line.find(" = ")
if line[-3:] == "= {":
if line[:7] == "#define":
if line[-1:] == "\\":
state = COPYDEFINE
out.write( line + "\n")
elif line[-3:] == "= {":
out.write( "extern " + line[:-4] + ";\n" )
state = SKIP
elif equals != -1:
@ -60,7 +65,7 @@ for line in file.readlines():
out.write( line[:-2].replace("inline", "extern") + ";\n" )
state = SKIP
else:
out.write( line + "\n")
out.write( line + "\n" )
is_blank = False
elif line == "":
if not is_blank:
@ -69,6 +74,11 @@ for line in file.readlines():
else:
out.write( line + "\n")
is_blank = False
elif state == COPYDEFINE:
is_blank = False
out.write( line + "\n")
if line[-1:] != "\\":
state = ANY
elif state == COPY:
is_blank = False
if line == "}*/":

8
solaris/Battery.c Normal file
View File

@ -0,0 +1,8 @@
#include "BatteryMeter.h"
void Battery_getData(double* level, ACPresence* isOnAC) {
*level = -1;
*isOnAC = AC_ERROR;
}

9
solaris/Battery.h Normal file
View File

@ -0,0 +1,9 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Battery
#define HEADER_Battery
void Battery_getData(double* level, ACPresence* isOnAC);
#endif

265
solaris/Platform.c Normal file
View File

@ -0,0 +1,265 @@
/*
htop - solaris/Platform.c
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Platform.h"
#include "Meter.h"
#include "CPUMeter.h"
#include "MemoryMeter.h"
#include "SwapMeter.h"
#include "TasksMeter.h"
#include "LoadAverageMeter.h"
#include "ClockMeter.h"
#include "HostnameMeter.h"
#include "UptimeMeter.h"
#include "SolarisProcess.h"
#include "SolarisProcessList.h"
#include <sys/types.h>
#include <sys/time.h>
#include <sys/resource.h>
#include <utmpx.h>
#include <sys/loadavg.h>
#include <string.h>
#include <kstat.h>
#include <time.h>
#include <math.h>
#include <sys/var.h>
/*{
#include "Action.h"
#include "BatteryMeter.h"
#include "SignalsPanel.h"
#include <signal.h>
#include <sys/mkdev.h>
#include <sys/proc.h>
#include <libproc.h>
#define kill(pid, signal) kill(pid / 1024, signal)
extern ProcessFieldData Process_fields[];
typedef struct var kvar_t;
typedef struct envAccum_ {
size_t capacity;
size_t size;
size_t bytes;
char *env;
} envAccum;
}*/
double plat_loadavg[3] = {0};
const SignalItem Platform_signals[] = {
{ .name = " 0 Cancel", .number = 0 },
{ .name = " 1 SIGHUP", .number = 1 },
{ .name = " 2 SIGINT", .number = 2 },
{ .name = " 3 SIGQUIT", .number = 3 },
{ .name = " 4 SIGILL", .number = 4 },
{ .name = " 5 SIGTRAP", .number = 5 },
{ .name = " 6 SIGABRT/IOT", .number = 6 },
{ .name = " 7 SIGEMT", .number = 7 },
{ .name = " 8 SIGFPE", .number = 8 },
{ .name = " 9 SIGKILL", .number = 9 },
{ .name = "10 SIGBUS", .number = 10 },
{ .name = "11 SIGSEGV", .number = 11 },
{ .name = "12 SIGSYS", .number = 12 },
{ .name = "13 SIGPIPE", .number = 13 },
{ .name = "14 SIGALRM", .number = 14 },
{ .name = "15 SIGTERM", .number = 15 },
{ .name = "16 SIGUSR1", .number = 16 },
{ .name = "17 SIGUSR2", .number = 17 },
{ .name = "18 SIGCHLD/CLD", .number = 18 },
{ .name = "19 SIGPWR", .number = 19 },
{ .name = "20 SIGWINCH", .number = 20 },
{ .name = "21 SIGURG", .number = 21 },
{ .name = "22 SIGPOLL/IO", .number = 22 },
{ .name = "23 SIGSTOP", .number = 23 },
{ .name = "24 SIGTSTP", .number = 24 },
{ .name = "25 SIGCONT", .number = 25 },
{ .name = "26 SIGTTIN", .number = 26 },
{ .name = "27 SIGTTOU", .number = 27 },
{ .name = "28 SIGVTALRM", .number = 28 },
{ .name = "29 SIGPROF", .number = 29 },
{ .name = "30 SIGXCPU", .number = 30 },
{ .name = "31 SIGXFSZ", .number = 31 },
{ .name = "32 SIGWAITING", .number = 32 },
{ .name = "33 SIGLWP", .number = 33 },
{ .name = "34 SIGFREEZE", .number = 34 },
{ .name = "35 SIGTHAW", .number = 35 },
{ .name = "36 SIGCANCEL", .number = 36 },
{ .name = "37 SIGLOST", .number = 37 },
{ .name = "38 SIGXRES", .number = 38 },
{ .name = "39 SIGJVM1", .number = 39 },
{ .name = "40 SIGJVM2", .number = 40 },
{ .name = "41 SIGINFO", .number = 41 },
};
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID LWPID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
MeterClass* Platform_meterTypes[] = {
&CPUMeter_class,
&ClockMeter_class,
&LoadAverageMeter_class,
&LoadMeter_class,
&MemoryMeter_class,
&SwapMeter_class,
&TasksMeter_class,
&BatteryMeter_class,
&HostnameMeter_class,
&UptimeMeter_class,
&AllCPUsMeter_class,
&AllCPUs2Meter_class,
&LeftCPUsMeter_class,
&RightCPUsMeter_class,
&LeftCPUs2Meter_class,
&RightCPUs2Meter_class,
&BlankMeter_class,
NULL
};
void Platform_setBindings(Htop_Action* keys) {
(void) keys;
}
int Platform_numberOfFields = LAST_PROCESSFIELD;
extern char Process_pidFormat[20];
int Platform_getUptime() {
int boot_time = 0;
int curr_time = time(NULL);
struct utmpx * ent;
while (( ent = getutxent() )) {
if ( !strcmp("system boot", ent->ut_line )) {
boot_time = ent->ut_tv.tv_sec;
}
}
endutxent();
return (curr_time-boot_time);
}
void Platform_getLoadAverage(double* one, double* five, double* fifteen) {
getloadavg( plat_loadavg, 3 );
*one = plat_loadavg[LOADAVG_1MIN];
*five = plat_loadavg[LOADAVG_5MIN];
*fifteen = plat_loadavg[LOADAVG_15MIN];
}
int Platform_getMaxPid() {
kstat_ctl_t *kc = NULL;
kstat_t *kshandle = NULL;
kvar_t *ksvar = NULL;
int vproc = 32778; // Reasonable Solaris default
kc = kstat_open();
if (kc != NULL) { kshandle = kstat_lookup(kc,"unix",0,"var"); }
if (kshandle != NULL) { kstat_read(kc,kshandle,NULL); }
ksvar = kshandle->ks_data;
if (ksvar->v_proc > 0 ) {
vproc = ksvar->v_proc;
}
if (kc != NULL) { kstat_close(kc); }
return vproc;
}
double Platform_setCPUValues(Meter* this, int cpu) {
SolarisProcessList* spl = (SolarisProcessList*) this->pl;
int cpus = this->pl->cpuCount;
CPUData* cpuData = NULL;
if (cpus == 1) {
// single CPU box has everything in spl->cpus[0]
cpuData = &(spl->cpus[0]);
} else {
cpuData = &(spl->cpus[cpu]);
}
double percent;
double* v = this->values;
v[CPU_METER_NICE] = cpuData->nicePercent;
v[CPU_METER_NORMAL] = cpuData->userPercent;
if (this->pl->settings->detailedCPUTime) {
v[CPU_METER_KERNEL] = cpuData->systemPercent;
v[CPU_METER_IRQ] = cpuData->irqPercent;
Meter_setItems(this, 4);
percent = v[0]+v[1]+v[2]+v[3];
} else {
v[2] = cpuData->systemAllPercent;
Meter_setItems(this, 3);
percent = v[0]+v[1]+v[2];
}
percent = CLAMP(percent, 0.0, 100.0);
if (isnan(percent)) percent = 0.0;
return percent;
}
void Platform_setMemoryValues(Meter* this) {
ProcessList* pl = (ProcessList*) this->pl;
this->total = pl->totalMem;
this->values[0] = pl->usedMem;
this->values[1] = pl->buffersMem;
this->values[2] = pl->cachedMem;
}
void Platform_setSwapValues(Meter* this) {
ProcessList* pl = (ProcessList*) this->pl;
this->total = pl->totalSwap;
this->values[0] = pl->usedSwap;
}
static int Platform_buildenv(void *accum, struct ps_prochandle *Phandle, uintptr_t addr, const char *str) {
envAccum *accump = accum;
(void) Phandle;
(void) addr;
size_t thissz = strlen(str);
if ((thissz + 2) > (accump->capacity - accump->size))
accump->env = xRealloc(accump->env, accump->capacity *= 2);
if ((thissz + 2) > (accump->capacity - accump->size))
return 1;
strlcpy( accump->env + accump->size, str, (accump->capacity - accump->size));
strncpy( accump->env + accump->size + thissz + 1, "\n", 1);
accump->size = accump->size + thissz + 1;
return 0;
}
char* Platform_getProcessEnv(pid_t pid) {
envAccum envBuilder;
pid_t realpid = pid / 1024;
int graberr;
struct ps_prochandle *Phandle;
if ((Phandle = Pgrab(realpid,PGRAB_RDONLY,&graberr)) == NULL)
return "Unable to read process environment.";
envBuilder.capacity = 4096;
envBuilder.size = 0;
envBuilder.env = xMalloc(envBuilder.capacity);
(void) Penv_iter(Phandle,Platform_buildenv,&envBuilder);
Prelease(Phandle, 0);
strncpy( envBuilder.env + envBuilder.size, "\0", 1);
return envBuilder.env;
}

67
solaris/Platform.h Normal file
View File

@ -0,0 +1,67 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_Platform
#define HEADER_Platform
/*
htop - solaris/Platform.h
(C) 2014 Hisham H. Muhammad
(C) 2015 David C. Hunt
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Action.h"
#include "BatteryMeter.h"
#include "SignalsPanel.h"
#include <signal.h>
#include <sys/mkdev.h>
#include <sys/proc.h>
#include <libproc.h>
#define kill(pid, signal) kill(pid / 1024, signal)
extern ProcessFieldData Process_fields[];
typedef struct var kvar_t;
typedef struct envAccum_ {
size_t capacity;
size_t size;
size_t bytes;
char *env;
} envAccum;
extern double plat_loadavg[3];
extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern MeterClass* Platform_meterTypes[];
void Platform_setBindings(Htop_Action* keys);
extern int Platform_numberOfFields;
extern char Process_pidFormat[20];
int Platform_getUptime();
void Platform_getLoadAverage(double* one, double* five, double* fifteen);
int Platform_getMaxPid();
double Platform_setCPUValues(Meter* this, int cpu);
void Platform_setMemoryValues(Meter* this);
void Platform_setSwapValues(Meter* this);
char* Platform_getProcessEnv(pid_t pid);
#endif

32
solaris/SolarisCRT.c Normal file
View File

@ -0,0 +1,32 @@
/*
htop - SolarisCRT.c
(C) 2014 Hisham H. Muhammad
(C) 2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "config.h"
#include "CRT.h"
#include <stdio.h>
#include <stdlib.h>
#ifdef HAVE_EXECINFO_H
#include <execinfo.h>
#endif
void CRT_handleSIGSEGV(int sgn) {
(void) sgn;
CRT_done();
fprintf(stderr, "\n\nhtop " VERSION " aborting. Please report bug at http://hisham.hm/htop\n");
#ifdef HAVE_EXECINFO_H
size_t size = backtrace(backtraceArray, sizeof(backtraceArray) / sizeof(void *));
fprintf(stderr, "\n Please include in your report the following backtrace: \n");
backtrace_symbols_fd(backtraceArray, size, 2);
fprintf(stderr, "\nAdditionally, in order to make the above backtrace useful,");
fprintf(stderr, "\nplease also run the following command to generate a disassembly of your binary:");
fprintf(stderr, "\n\n objdump -d `which htop` > ~/htop.objdump");
fprintf(stderr, "\n\nand then attach the file ~/htop.objdump to your bug report.");
fprintf(stderr, "\n\nThank you for helping to improve htop!\n\n");
#endif
abort();
}

18
solaris/SolarisCRT.h Normal file
View File

@ -0,0 +1,18 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_SolarisCRT
#define HEADER_SolarisCRT
/*
htop - SolarisCRT.h
(C) 2014 Hisham H. Muhammad
(C) 2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#ifdef HAVE_EXECINFO_H
#endif
void CRT_handleSIGSEGV(int sgn);
#endif

214
solaris/SolarisProcess.c Normal file
View File

@ -0,0 +1,214 @@
/*
htop - SolarisProcess.c
(C) 2015 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Process.h"
#include "ProcessList.h"
#include "SolarisProcess.h"
#include "Platform.h"
#include "CRT.h"
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/syscall.h>
/*{
#include "Settings.h"
#include <zone.h>
#include <sys/proc.h>
#include <libproc.h>
typedef enum SolarisProcessFields {
// Add platform-specific fields here, with ids >= 100
ZONEID = 100,
ZONE = 101,
PROJID = 102,
TASKID = 103,
POOLID = 104,
CONTID = 105,
LWPID = 106,
LAST_PROCESSFIELD = 107,
} SolarisProcessField;
typedef struct SolarisProcess_ {
Process super;
int kernel;
zoneid_t zoneid;
char* zname;
taskid_t taskid;
projid_t projid;
poolid_t poolid;
ctid_t contid;
bool is_lwp;
pid_t realpid;
pid_t realppid;
pid_t lwpid;
} SolarisProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->kernel == 1)
#endif
#ifndef Process_isUserlandThread
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#endif
}*/
ProcessClass SolarisProcess_class = {
.super = {
.extends = Class(Process),
.display = Process_display,
.delete = Process_delete,
.compare = SolarisProcess_compare
},
.writeField = (Process_WriteField) SolarisProcess_writeField,
};
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
[COMM] = { .name = "Command", .title = "Command ", .description = "Command line", .flags = 0, },
[STATE] = { .name = "STATE", .title = "S ", .description = "Process state (S sleeping, R running, O onproc, Z zombie, T stopped, W waiting)", .flags = 0, },
[PPID] = { .name = "PPID", .title = " PPID ", .description = "Parent process ID", .flags = 0, },
[PGRP] = { .name = "PGRP", .title = " PGRP ", .description = "Process group ID", .flags = 0, },
[SESSION] = { .name = "SESSION", .title = " SID ", .description = "Process's session ID", .flags = 0, },
[TTY_NR] = { .name = "TTY_NR", .title = " TTY ", .description = "Controlling terminal", .flags = 0, },
[TPGID] = { .name = "TPGID", .title = " TPGID ", .description = "Process ID of the fg process group of the controlling terminal", .flags = 0, },
[MINFLT] = { .name = "MINFLT", .title = " MINFLT ", .description = "Number of minor faults which have not required loading a memory page from disk", .flags = 0, },
[MAJFLT] = { .name = "MAJFLT", .title = " MAJFLT ", .description = "Number of major faults which have required loading a memory page from disk", .flags = 0, },
[PRIORITY] = { .name = "PRIORITY", .title = "PRI ", .description = "Kernel's internal priority for the process", .flags = 0, },
[NICE] = { .name = "NICE", .title = " NI ", .description = "Nice value (the higher the value, the more it lets other processes take priority)", .flags = 0, },
[STARTTIME] = { .name = "STARTTIME", .title = "START ", .description = "Time the process was started", .flags = 0, },
[PROCESSOR] = { .name = "PROCESSOR", .title = "CPU ", .description = "Id of the CPU the process last executed on", .flags = 0, },
[M_SIZE] = { .name = "M_SIZE", .title = " VIRT ", .description = "Total program size in virtual memory", .flags = 0, },
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, },
[ST_UID] = { .name = "ST_UID", .title = " UID ", .description = "User ID of the process owner", .flags = 0, },
[PERCENT_CPU] = { .name = "PERCENT_CPU", .title = "CPU% ", .description = "Percentage of the CPU time the process used in the last sampling", .flags = 0, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, },
[USER] = { .name = "USER", .title = "USER ", .description = "Username of the process owner (or user ID if name cannot be determined)", .flags = 0, },
[TIME] = { .name = "TIME", .title = " TIME+ ", .description = "Total time the process has spent in user and system time", .flags = 0, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
[TGID] = { .name = "TGID", .title = " TGID ", .description = "Thread group ID (i.e. process ID)", .flags = 0, },
[ZONEID] = { .name = "ZONEID", .title = " ZONEID ", .description = "Zone ID", .flags = 0, },
[ZONE] = { .name = "ZONE", .title = "ZONE ", .description = "Zone name", .flags = 0, },
[PROJID] = { .name = "PROJID", .title = " PRJID ", .description = "Project ID", .flags = 0, },
[TASKID] = { .name = "TASKID", .title = " TSKID ", .description = "Task ID", .flags = 0, },
[POOLID] = { .name = "POOLID", .title = " POLID ", .description = "Pool ID", .flags = 0, },
[CONTID] = { .name = "CONTID", .title = " CNTID ", .description = "Contract ID", .flags = 0, },
[LWPID] = { .name = "LWPID", .title = " LWPID ", .description = "LWP ID", .flags = 0, },
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
};
ProcessPidColumn Process_pidColumns[] = {
{ .id = ZONEID, .label = "ZONEID" },
{ .id = TASKID, .label = "TSKID" },
{ .id = PROJID, .label = "PRJID" },
{ .id = POOLID, .label = "POLID" },
{ .id = CONTID, .label = "CNTID" },
{ .id = PID, .label = "PID" },
{ .id = PPID, .label = "PPID" },
{ .id = LWPID, .label = "LWPID" },
{ .id = TPGID, .label = "TPGID" },
{ .id = TGID, .label = "TGID" },
{ .id = PGRP, .label = "PGRP" },
{ .id = SESSION, .label = "SID" },
{ .id = 0, .label = NULL },
};
SolarisProcess* SolarisProcess_new(Settings* settings) {
SolarisProcess* this = xCalloc(1, sizeof(SolarisProcess));
Object_setClass(this, Class(SolarisProcess));
Process_init(&this->super, settings);
return this;
}
void Process_delete(Object* cast) {
SolarisProcess* sp = (SolarisProcess*) cast;
Process_done((Process*)cast);
free(sp->zname);
free(sp);
}
void SolarisProcess_writeField(Process* this, RichString* str, ProcessField field) {
SolarisProcess* sp = (SolarisProcess*) this;
char buffer[256]; buffer[255] = '\0';
int attr = CRT_colors[DEFAULT_COLOR];
int n = sizeof(buffer) - 1;
switch ((int) field) {
// add Solaris-specific fields here
case ZONEID: xSnprintf(buffer, n, Process_pidFormat, sp->zoneid); break;
case PROJID: xSnprintf(buffer, n, Process_pidFormat, sp->projid); break;
case TASKID: xSnprintf(buffer, n, Process_pidFormat, sp->taskid); break;
case POOLID: xSnprintf(buffer, n, Process_pidFormat, sp->poolid); break;
case CONTID: xSnprintf(buffer, n, Process_pidFormat, sp->contid); break;
case ZONE:{
xSnprintf(buffer, n, "%-*s ", ZONENAME_MAX/4, sp->zname); break;
if (buffer[ZONENAME_MAX/4] != '\0') {
buffer[ZONENAME_MAX/4] = ' ';
buffer[(ZONENAME_MAX/4)+1] = '\0';
}
break;
}
case PID: xSnprintf(buffer, n, Process_pidFormat, sp->realpid); break;
case PPID: xSnprintf(buffer, n, Process_pidFormat, sp->realppid); break;
case LWPID: xSnprintf(buffer, n, Process_pidFormat, sp->lwpid); break;
default:
Process_writeField(this, str, field);
return;
}
RichString_append(str, attr, buffer);
}
long SolarisProcess_compare(const void* v1, const void* v2) {
SolarisProcess *p1, *p2;
Settings* settings = ((Process*)v1)->settings;
if (settings->direction == 1) {
p1 = (SolarisProcess*)v1;
p2 = (SolarisProcess*)v2;
} else {
p2 = (SolarisProcess*)v1;
p1 = (SolarisProcess*)v2;
}
switch ((int) settings->sortKey) {
case ZONEID:
return (p1->zoneid - p2->zoneid);
case PROJID:
return (p1->projid - p2->projid);
case TASKID:
return (p1->taskid - p2->taskid);
case POOLID:
return (p1->poolid - p2->poolid);
case CONTID:
return (p1->contid - p2->contid);
case ZONE:
return strcmp(p1->zname ? p1->zname : "global", p2->zname ? p2->zname : "global");
case PID:
return (p1->realpid - p2->realpid);
case PPID:
return (p1->realppid - p2->realppid);
case LWPID:
return (p1->lwpid - p2->lwpid);
default:
return Process_compare(v1, v2);
}
}
bool Process_isThread(Process* this) {
SolarisProcess* fp = (SolarisProcess*) this;
if (fp->kernel == 1 ) {
return 1;
} else if (fp->is_lwp) {
return 1;
} else {
return 0;
}
}

72
solaris/SolarisProcess.h Normal file
View File

@ -0,0 +1,72 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_SolarisProcess
#define HEADER_SolarisProcess
/*
htop - SolarisProcess.h
(C) 2015 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "Settings.h"
#include <zone.h>
#include <sys/proc.h>
#include <libproc.h>
typedef enum SolarisProcessFields {
// Add platform-specific fields here, with ids >= 100
ZONEID = 100,
ZONE = 101,
PROJID = 102,
TASKID = 103,
POOLID = 104,
CONTID = 105,
LWPID = 106,
LAST_PROCESSFIELD = 107,
} SolarisProcessField;
typedef struct SolarisProcess_ {
Process super;
int kernel;
zoneid_t zoneid;
char* zname;
taskid_t taskid;
projid_t projid;
poolid_t poolid;
ctid_t contid;
bool is_lwp;
pid_t realpid;
pid_t realppid;
pid_t lwpid;
} SolarisProcess;
#ifndef Process_isKernelThread
#define Process_isKernelThread(_process) (_process->kernel == 1)
#endif
#ifndef Process_isUserlandThread
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
#endif
extern ProcessClass SolarisProcess_class;
extern ProcessFieldData Process_fields[];
extern ProcessPidColumn Process_pidColumns[];
SolarisProcess* SolarisProcess_new(Settings* settings);
void Process_delete(Object* cast);
void SolarisProcess_writeField(Process* this, RichString* str, ProcessField field);
long SolarisProcess_compare(const void* v1, const void* v2);
bool Process_isThread(Process* this);
#endif

View File

@ -0,0 +1,373 @@
/*
htop - SolarisProcessList.c
(C) 2014 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#include "ProcessList.h"
#include "SolarisProcess.h"
#include "SolarisProcessList.h"
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/user.h>
#include <err.h>
#include <limits.h>
#include <string.h>
#include <procfs.h>
#include <errno.h>
#include <pwd.h>
#include <math.h>
#include <time.h>
#define MAXCMDLINE 255
/*{
#include <kstat.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/resource.h>
#include <sys/sysconf.h>
#include <sys/sysinfo.h>
#include <sys/swap.h>
#define ZONE_ERRMSGLEN 1024
char zone_errmsg[ZONE_ERRMSGLEN];
typedef struct CPUData_ {
double userPercent;
double nicePercent;
double systemPercent;
double irqPercent;
double idlePercent;
double systemAllPercent;
uint64_t luser;
uint64_t lkrnl;
uint64_t lintr;
uint64_t lidle;
} CPUData;
typedef struct SolarisProcessList_ {
ProcessList super;
kstat_ctl_t* kd;
CPUData* cpus;
} SolarisProcessList;
}*/
char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc) {
char* zname;
if ( sproc->zoneid == 0 ) {
zname = xStrdup("global ");
} else if ( kd == NULL ) {
zname = xStrdup("unknown ");
} else {
kstat_t* ks = kstat_lookup( kd, "zones", sproc->zoneid, NULL );
zname = xStrdup(ks->ks_name);
}
return zname;
}
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId) {
SolarisProcessList* spl = xCalloc(1, sizeof(SolarisProcessList));
ProcessList* pl = (ProcessList*) spl;
ProcessList_init(pl, Class(SolarisProcess), usersTable, pidWhiteList, userId);
spl->kd = kstat_open();
pl->cpuCount = sysconf(_SC_NPROCESSORS_ONLN);
if (pl->cpuCount == 1 ) {
spl->cpus = xRealloc(spl->cpus, sizeof(CPUData));
} else {
spl->cpus = xRealloc(spl->cpus, (pl->cpuCount + 1) * sizeof(CPUData));
}
return pl;
}
static inline void SolarisProcessList_scanCPUTime(ProcessList* pl) {
const SolarisProcessList* spl = (SolarisProcessList*) pl;
int cpus = pl->cpuCount;
kstat_t *cpuinfo = NULL;
int kchain = 0;
kstat_named_t *idletime = NULL;
kstat_named_t *intrtime = NULL;
kstat_named_t *krnltime = NULL;
kstat_named_t *usertime = NULL;
double idlebuf = 0;
double intrbuf = 0;
double krnlbuf = 0;
double userbuf = 0;
uint64_t totaltime = 0;
int arrskip = 0;
assert(cpus > 0);
if (cpus > 1) {
// Store values for the stats loop one extra element up in the array
// to leave room for the average to be calculated afterwards
arrskip++;
}
// Calculate per-CPU statistics first
for (int i = 0; i < cpus; i++) {
if (spl->kd != NULL) { cpuinfo = kstat_lookup(spl->kd,"cpu",i,"sys"); }
if (cpuinfo != NULL) { kchain = kstat_read(spl->kd,cpuinfo,NULL); }
if (kchain != -1 ) {
idletime = kstat_data_lookup(cpuinfo,"cpu_nsec_idle");
intrtime = kstat_data_lookup(cpuinfo,"cpu_nsec_intr");
krnltime = kstat_data_lookup(cpuinfo,"cpu_nsec_kernel");
usertime = kstat_data_lookup(cpuinfo,"cpu_nsec_user");
}
assert( (idletime != NULL) && (intrtime != NULL)
&& (krnltime != NULL) && (usertime != NULL) );
CPUData* cpuData = &(spl->cpus[i+arrskip]);
totaltime = (idletime->value.ui64 - cpuData->lidle)
+ (intrtime->value.ui64 - cpuData->lintr)
+ (krnltime->value.ui64 - cpuData->lkrnl)
+ (usertime->value.ui64 - cpuData->luser);
// Calculate percentages of deltas since last reading
cpuData->userPercent = ((usertime->value.ui64 - cpuData->luser) / (double)totaltime) * 100.0;
cpuData->nicePercent = (double)0.0; // Not implemented on Solaris
cpuData->systemPercent = ((krnltime->value.ui64 - cpuData->lkrnl) / (double)totaltime) * 100.0;
cpuData->irqPercent = ((intrtime->value.ui64 - cpuData->lintr) / (double)totaltime) * 100.0;
cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent;
cpuData->idlePercent = ((idletime->value.ui64 - cpuData->lidle) / (double)totaltime) * 100.0;
// Store current values to use for the next round of deltas
cpuData->luser = usertime->value.ui64;
cpuData->lkrnl = krnltime->value.ui64;
cpuData->lintr = intrtime->value.ui64;
cpuData->lidle = idletime->value.ui64;
// Accumulate the current percentages into buffers for later average calculation
if (cpus > 1) {
userbuf += cpuData->userPercent;
krnlbuf += cpuData->systemPercent;
intrbuf += cpuData->irqPercent;
idlebuf += cpuData->idlePercent;
}
}
if (cpus > 1) {
CPUData* cpuData = &(spl->cpus[0]);
cpuData->userPercent = userbuf / cpus;
cpuData->nicePercent = (double)0.0; // Not implemented on Solaris
cpuData->systemPercent = krnlbuf / cpus;
cpuData->irqPercent = intrbuf / cpus;
cpuData->systemAllPercent = cpuData->systemPercent + cpuData->irqPercent;
cpuData->idlePercent = idlebuf / cpus;
}
}
static inline void SolarisProcessList_scanMemoryInfo(ProcessList* pl) {
SolarisProcessList* spl = (SolarisProcessList*) pl;
kstat_t *meminfo = NULL;
int ksrphyserr = -1;
kstat_named_t *totalmem_pgs = NULL;
kstat_named_t *lockedmem_pgs = NULL;
kstat_named_t *pages = NULL;
struct swaptable *sl = NULL;
struct swapent *swapdev = NULL;
uint64_t totalswap = 0;
uint64_t totalfree = 0;
int nswap = 0;
char *spath = NULL;
char *spathbase = NULL;
// Part 1 - physical memory
if (spl->kd != NULL) { meminfo = kstat_lookup(spl->kd,"unix",0,"system_pages"); }
if (meminfo != NULL) { ksrphyserr = kstat_read(spl->kd,meminfo,NULL); }
if (ksrphyserr != -1) {
totalmem_pgs = kstat_data_lookup( meminfo, "physmem" );
lockedmem_pgs = kstat_data_lookup( meminfo, "pageslocked" );
pages = kstat_data_lookup( meminfo, "pagestotal" );
pl->totalMem = totalmem_pgs->value.ui64 * PAGE_SIZE_KB;
pl->usedMem = lockedmem_pgs->value.ui64 * PAGE_SIZE_KB;
// Not sure how to implement this on Solaris - suggestions welcome!
pl->cachedMem = 0;
// Not really "buffers" but the best Solaris analogue that I can find to
// "memory in use but not by programs or the kernel itself"
pl->buffersMem = (totalmem_pgs->value.ui64 - pages->value.ui64) * PAGE_SIZE_KB;
} else {
// Fall back to basic sysconf if kstat isn't working
pl->totalMem = sysconf(_SC_PHYS_PAGES) * PAGE_SIZE;
pl->buffersMem = 0;
pl->cachedMem = 0;
pl->usedMem = pl->totalMem - (sysconf(_SC_AVPHYS_PAGES) * PAGE_SIZE);
}
// Part 2 - swap
nswap = swapctl(SC_GETNSWP, NULL);
if (nswap > 0) { sl = xMalloc((nswap * sizeof(swapent_t)) + sizeof(int)); }
if (sl != NULL) { spathbase = xMalloc( nswap * MAXPATHLEN ); }
if (spathbase != NULL) {
spath = spathbase;
swapdev = sl->swt_ent;
for (int i = 0; i < nswap; i++, swapdev++) {
swapdev->ste_path = spath;
spath += MAXPATHLEN;
}
sl->swt_n = nswap;
}
nswap = swapctl(SC_LIST, sl);
if (nswap > 0) {
swapdev = sl->swt_ent;
for (int i = 0; i < nswap; i++, swapdev++) {
totalswap += swapdev->ste_pages;
totalfree += swapdev->ste_free;
}
}
free(spathbase);
free(sl);
pl->totalSwap = totalswap * PAGE_SIZE_KB;
pl->usedSwap = pl->totalSwap - (totalfree * PAGE_SIZE_KB);
}
void ProcessList_delete(ProcessList* pl) {
SolarisProcessList* spl = (SolarisProcessList*) pl;
ProcessList_done(pl);
free(spl->cpus);
if (spl->kd) kstat_close(spl->kd);
free(spl);
}
/* NOTE: the following is a callback function of type proc_walk_f
* and MUST conform to the appropriate definition in order
* to work. See libproc(3LIB) on a Solaris or Illumos
* system for more info.
*/
int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *listptr) {
struct timeval tv;
struct tm date;
bool preExisting;
pid_t getpid;
// Setup process list
ProcessList *pl = (ProcessList*) listptr;
SolarisProcessList *spl = (SolarisProcessList*) listptr;
id_t lwpid_real = _lwpsinfo->pr_lwpid;
if (lwpid_real > 1023) return 0;
pid_t lwpid = (_psinfo->pr_pid * 1024) + lwpid_real;
bool onMasterLWP = (_lwpsinfo->pr_lwpid == _psinfo->pr_lwp.pr_lwpid);
if (onMasterLWP) {
getpid = _psinfo->pr_pid * 1024;
} else {
getpid = lwpid;
}
Process *proc = ProcessList_getProcess(pl, getpid, &preExisting, (Process_New) SolarisProcess_new);
SolarisProcess *sproc = (SolarisProcess*) proc;
gettimeofday(&tv, NULL);
// Common code pass 1
proc->show = false;
sproc->taskid = _psinfo->pr_taskid;
sproc->projid = _psinfo->pr_projid;
sproc->poolid = _psinfo->pr_poolid;
sproc->contid = _psinfo->pr_contract;
proc->priority = _lwpsinfo->pr_pri;
proc->nice = _lwpsinfo->pr_nice;
proc->processor = _lwpsinfo->pr_onpro;
proc->state = _lwpsinfo->pr_sname;
// NOTE: This 'percentage' is a 16-bit BINARY FRACTIONS where 1.0 = 0x8000
// Source: https://docs.oracle.com/cd/E19253-01/816-5174/proc-4/index.html
// (accessed on 18 November 2017)
proc->percent_mem = ((uint16_t)_psinfo->pr_pctmem/(double)32768)*(double)100.0;
proc->st_uid = _psinfo->pr_euid;
proc->pgrp = _psinfo->pr_pgid;
proc->nlwp = _psinfo->pr_nlwp;
proc->tty_nr = _psinfo->pr_ttydev;
proc->m_resident = _psinfo->pr_rssize/PAGE_SIZE_KB;
proc->m_size = _psinfo->pr_size/PAGE_SIZE_KB;
if (!preExisting) {
sproc->realpid = _psinfo->pr_pid;
sproc->lwpid = lwpid_real;
sproc->zoneid = _psinfo->pr_zoneid;
sproc->zname = SolarisProcessList_readZoneName(spl->kd,sproc);
proc->user = UsersTable_getRef(pl->usersTable, proc->st_uid);
proc->comm = xStrdup(_psinfo->pr_fname);
proc->commLen = strnlen(_psinfo->pr_fname,PRFNSZ);
}
// End common code pass 1
if (onMasterLWP) { // Are we on the representative LWP?
proc->ppid = (_psinfo->pr_ppid * 1024);
proc->tgid = (_psinfo->pr_ppid * 1024);
sproc->realppid = _psinfo->pr_ppid;
// See note above (in common section) about this BINARY FRACTION
proc->percent_cpu = ((uint16_t)_psinfo->pr_pctcpu/(double)32768)*(double)100.0;
proc->time = _psinfo->pr_time.tv_sec;
if(!preExisting) { // Tasks done only for NEW processes
sproc->is_lwp = false;
proc->starttime_ctime = _psinfo->pr_start.tv_sec;
}
// Update proc and thread counts based on settings
if (sproc->kernel && !pl->settings->hideKernelThreads) {
pl->kernelThreads += proc->nlwp;
pl->totalTasks += proc->nlwp+1;
if (proc->state == 'O') pl->runningTasks++;
} else if (!sproc->kernel) {
if (proc->state == 'O') pl->runningTasks++;
if (pl->settings->hideUserlandThreads) {
pl->totalTasks++;
} else {
pl->userlandThreads += proc->nlwp;
pl->totalTasks += proc->nlwp+1;
}
}
proc->show = !(pl->settings->hideKernelThreads && sproc->kernel);
} else { // We are not in the master LWP, so jump to the LWP handling code
proc->percent_cpu = ((uint16_t)_lwpsinfo->pr_pctcpu/(double)32768)*(double)100.0;
proc->time = _lwpsinfo->pr_time.tv_sec;
if (!preExisting) { // Tasks done only for NEW LWPs
sproc->is_lwp = true;
proc->basenameOffset = -1;
proc->ppid = _psinfo->pr_pid * 1024;
proc->tgid = _psinfo->pr_pid * 1024;
sproc->realppid = _psinfo->pr_pid;
proc->starttime_ctime = _lwpsinfo->pr_start.tv_sec;
}
// Top-level process only gets this for the representative LWP
if (sproc->kernel && !pl->settings->hideKernelThreads) proc->show = true;
if (!sproc->kernel && !pl->settings->hideUserlandThreads) proc->show = true;
} // Top-level LWP or subordinate LWP
// Common code pass 2
if (!preExisting) {
if ((sproc->realppid <= 0) && !(sproc->realpid <= 1)) {
sproc->kernel = true;
} else {
sproc->kernel = false;
}
(void) localtime_r((time_t*) &proc->starttime_ctime, &date);
strftime(proc->starttime_show, 7, ((proc->starttime_ctime > tv.tv_sec - 86400) ? "%R " : "%b%d "), &date);
ProcessList_add(pl, proc);
}
proc->updated = true;
// End common code pass 2
return 0;
}
void ProcessList_goThroughEntries(ProcessList* this) {
SolarisProcessList_scanCPUTime(this);
SolarisProcessList_scanMemoryInfo(this);
this->kernelThreads = 1;
proc_walk(&SolarisProcessList_walkproc, this, PR_WALK_LWP);
}

View File

@ -0,0 +1,64 @@
/* Do not edit this file. It was automatically generated. */
#ifndef HEADER_SolarisProcessList
#define HEADER_SolarisProcessList
/*
htop - SolarisProcessList.h
(C) 2014 Hisham H. Muhammad
(C) 2017,2018 Guy M. Broome
Released under the GNU GPL, see the COPYING file
in the source distribution for its full text.
*/
#define MAXCMDLINE 255
#include <kstat.h>
#include <sys/param.h>
#include <sys/uio.h>
#include <sys/resource.h>
#include <sys/sysconf.h>
#include <sys/sysinfo.h>
#include <sys/swap.h>
#define ZONE_ERRMSGLEN 1024
char zone_errmsg[ZONE_ERRMSGLEN];
typedef struct CPUData_ {
double userPercent;
double nicePercent;
double systemPercent;
double irqPercent;
double idlePercent;
double systemAllPercent;
uint64_t luser;
uint64_t lkrnl;
uint64_t lintr;
uint64_t lidle;
} CPUData;
typedef struct SolarisProcessList_ {
ProcessList super;
kstat_ctl_t* kd;
CPUData* cpus;
} SolarisProcessList;
char* SolarisProcessList_readZoneName(kstat_ctl_t* kd, SolarisProcess* sproc);
ProcessList* ProcessList_new(UsersTable* usersTable, Hashtable* pidWhiteList, uid_t userId);
void ProcessList_delete(ProcessList* pl);
/* NOTE: the following is a callback function of type proc_walk_f
* and MUST conform to the appropriate definition in order
* to work. See libproc(3LIB) on a Solaris or Illumos
* system for more info.
*/
int SolarisProcessList_walkproc(psinfo_t *_psinfo, lwpsinfo_t *_lwpsinfo, void *listptr);
void ProcessList_goThroughEntries(ProcessList* this);
#endif

View File

@ -29,7 +29,15 @@ const SignalItem Platform_signals[] = {
const unsigned int Platform_numberOfSignals = sizeof(Platform_signals)/sizeof(SignalItem);
ProcessField Platform_defaultFields[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
ScreenDefaults Platform_defaultScreens[] = {
{
.name = "Default",
.columns = "PID LWPID USER PRIORITY NICE M_SIZE M_RESIDENT STATE PERCENT_CPU PERCENT_MEM TIME Command",
.sortKey = "PERCENT_CPU",
},
};
const unsigned int Platform_numberOfDefaultScreens = sizeof(Platform_defaultScreens)/sizeof(ScreenDefaults);
ProcessFieldData Process_fields[] = {
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },

View File

@ -19,7 +19,9 @@ extern const SignalItem Platform_signals[];
extern const unsigned int Platform_numberOfSignals;
extern ProcessField Platform_defaultFields[];
extern ScreenDefaults Platform_defaultScreens[];
extern const unsigned int Platform_numberOfDefaultScreens;
extern ProcessFieldData Process_fields[];