mirror of https://github.com/xzeldon/htop.git
Improving Command display/sort
This commit is contained in:
parent
42c842c190
commit
09fe94da18
7
Action.c
7
Action.c
|
@ -224,6 +224,11 @@ static Htop_Reaction actionToggleProgramPath(State* st) {
|
||||||
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
|
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static Htop_Reaction actionToggleMergedCommand(State* st) {
|
||||||
|
st->settings->showMergedCommand = !st->settings->showMergedCommand;
|
||||||
|
return HTOP_REFRESH | HTOP_SAVE_SETTINGS;
|
||||||
|
}
|
||||||
|
|
||||||
static Htop_Reaction actionToggleTreeView(State* st) {
|
static Htop_Reaction actionToggleTreeView(State* st) {
|
||||||
st->settings->treeView = !st->settings->treeView;
|
st->settings->treeView = !st->settings->treeView;
|
||||||
if (st->settings->treeView) {
|
if (st->settings->treeView) {
|
||||||
|
@ -450,6 +455,7 @@ static const struct {
|
||||||
{ .key = " F4 \\: ",.info = "incremental name filtering" },
|
{ .key = " F4 \\: ",.info = "incremental name filtering" },
|
||||||
{ .key = " F5 t: ", .info = "tree view" },
|
{ .key = " F5 t: ", .info = "tree view" },
|
||||||
{ .key = " p: ", .info = "toggle program path" },
|
{ .key = " p: ", .info = "toggle program path" },
|
||||||
|
{ .key = " m: ", .info = "toggle merged command" },
|
||||||
{ .key = " Z: ", .info = "pause/resume process updates" },
|
{ .key = " Z: ", .info = "pause/resume process updates" },
|
||||||
{ .key = " u: ", .info = "show processes of a single user" },
|
{ .key = " u: ", .info = "show processes of a single user" },
|
||||||
{ .key = " H: ", .info = "hide/show user process threads" },
|
{ .key = " H: ", .info = "hide/show user process threads" },
|
||||||
|
@ -640,6 +646,7 @@ void Action_setBindings(Htop_Action* keys) {
|
||||||
keys['H'] = actionToggleUserlandThreads;
|
keys['H'] = actionToggleUserlandThreads;
|
||||||
keys['K'] = actionToggleKernelThreads;
|
keys['K'] = actionToggleKernelThreads;
|
||||||
keys['p'] = actionToggleProgramPath;
|
keys['p'] = actionToggleProgramPath;
|
||||||
|
keys['m'] = actionToggleMergedCommand;
|
||||||
keys['t'] = actionToggleTreeView;
|
keys['t'] = actionToggleTreeView;
|
||||||
keys[KEY_F(5)] = actionToggleTreeView;
|
keys[KEY_F(5)] = actionToggleTreeView;
|
||||||
keys[KEY_F(4)] = actionIncFilter;
|
keys[KEY_F(4)] = actionIncFilter;
|
||||||
|
|
18
CRT.c
18
CRT.c
|
@ -105,6 +105,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
|
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
|
||||||
[PANEL_SELECTION_UNFOCUS] = ColorPair(Black, White),
|
[PANEL_SELECTION_UNFOCUS] = ColorPair(Black, White),
|
||||||
[FAILED_SEARCH] = ColorPair(Red, Cyan),
|
[FAILED_SEARCH] = ColorPair(Red, Cyan),
|
||||||
|
[FAILED_READ] = A_BOLD | ColorPair(Red, Black),
|
||||||
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
||||||
[UPTIME] = A_BOLD | ColorPair(Cyan, Black),
|
[UPTIME] = A_BOLD | ColorPair(Cyan, Black),
|
||||||
[BATTERY] = A_BOLD | ColorPair(Cyan, Black),
|
[BATTERY] = A_BOLD | ColorPair(Cyan, Black),
|
||||||
|
@ -133,6 +134,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PROCESS_TOMB] = ColorPair(Black, Red),
|
[PROCESS_TOMB] = ColorPair(Black, Red),
|
||||||
[PROCESS_THREAD] = ColorPair(Green, Black),
|
[PROCESS_THREAD] = ColorPair(Green, Black),
|
||||||
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Black),
|
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Black),
|
||||||
|
[PROCESS_COMM] = ColorPair(Magenta, Black),
|
||||||
|
[PROCESS_THREAD_COMM] = ColorPair(Blue, Black),
|
||||||
[BAR_BORDER] = A_BOLD,
|
[BAR_BORDER] = A_BOLD,
|
||||||
[BAR_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
[BAR_SHADOW] = A_BOLD | ColorPairGrayBlack,
|
||||||
[SWAP] = ColorPair(Red, Black),
|
[SWAP] = ColorPair(Red, Black),
|
||||||
|
@ -186,6 +189,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PANEL_SELECTION_FOLLOW] = A_REVERSE,
|
[PANEL_SELECTION_FOLLOW] = A_REVERSE,
|
||||||
[PANEL_SELECTION_UNFOCUS] = A_BOLD,
|
[PANEL_SELECTION_UNFOCUS] = A_BOLD,
|
||||||
[FAILED_SEARCH] = A_REVERSE | A_BOLD,
|
[FAILED_SEARCH] = A_REVERSE | A_BOLD,
|
||||||
|
[FAILED_READ] = A_BOLD,
|
||||||
[PAUSED] = A_BOLD | A_REVERSE,
|
[PAUSED] = A_BOLD | A_REVERSE,
|
||||||
[UPTIME] = A_BOLD,
|
[UPTIME] = A_BOLD,
|
||||||
[BATTERY] = A_BOLD,
|
[BATTERY] = A_BOLD,
|
||||||
|
@ -214,6 +218,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PROCESS_TOMB] = A_DIM,
|
[PROCESS_TOMB] = A_DIM,
|
||||||
[PROCESS_THREAD] = A_BOLD,
|
[PROCESS_THREAD] = A_BOLD,
|
||||||
[PROCESS_THREAD_BASENAME] = A_REVERSE,
|
[PROCESS_THREAD_BASENAME] = A_REVERSE,
|
||||||
|
[PROCESS_COMM] = A_BOLD,
|
||||||
|
[PROCESS_THREAD_COMM] = A_REVERSE,
|
||||||
[BAR_BORDER] = A_BOLD,
|
[BAR_BORDER] = A_BOLD,
|
||||||
[BAR_SHADOW] = A_DIM,
|
[BAR_SHADOW] = A_DIM,
|
||||||
[SWAP] = A_BOLD,
|
[SWAP] = A_BOLD,
|
||||||
|
@ -267,6 +273,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
|
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
|
||||||
[PANEL_SELECTION_UNFOCUS] = ColorPair(Blue, White),
|
[PANEL_SELECTION_UNFOCUS] = ColorPair(Blue, White),
|
||||||
[FAILED_SEARCH] = ColorPair(Red, Cyan),
|
[FAILED_SEARCH] = ColorPair(Red, Cyan),
|
||||||
|
[FAILED_READ] = ColorPair(Red, White),
|
||||||
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
||||||
[UPTIME] = ColorPair(Yellow, White),
|
[UPTIME] = ColorPair(Yellow, White),
|
||||||
[BATTERY] = ColorPair(Yellow, White),
|
[BATTERY] = ColorPair(Yellow, White),
|
||||||
|
@ -295,6 +302,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PROCESS_TOMB] = ColorPair(White, Red),
|
[PROCESS_TOMB] = ColorPair(White, Red),
|
||||||
[PROCESS_THREAD] = ColorPair(Blue, White),
|
[PROCESS_THREAD] = ColorPair(Blue, White),
|
||||||
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, White),
|
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, White),
|
||||||
|
[PROCESS_COMM] = ColorPair(Magenta, White),
|
||||||
|
[PROCESS_THREAD_COMM] = ColorPair(Green, White),
|
||||||
[BAR_BORDER] = ColorPair(Blue, White),
|
[BAR_BORDER] = ColorPair(Blue, White),
|
||||||
[BAR_SHADOW] = ColorPair(Black, White),
|
[BAR_SHADOW] = ColorPair(Black, White),
|
||||||
[SWAP] = ColorPair(Red, White),
|
[SWAP] = ColorPair(Red, White),
|
||||||
|
@ -348,6 +357,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
|
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
|
||||||
[PANEL_SELECTION_UNFOCUS] = ColorPair(Blue, Black),
|
[PANEL_SELECTION_UNFOCUS] = ColorPair(Blue, Black),
|
||||||
[FAILED_SEARCH] = ColorPair(Red, Cyan),
|
[FAILED_SEARCH] = ColorPair(Red, Cyan),
|
||||||
|
[FAILED_READ] = ColorPair(Red, Black),
|
||||||
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
||||||
[UPTIME] = ColorPair(Yellow, Black),
|
[UPTIME] = ColorPair(Yellow, Black),
|
||||||
[BATTERY] = ColorPair(Yellow, Black),
|
[BATTERY] = ColorPair(Yellow, Black),
|
||||||
|
@ -376,6 +386,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PROCESS_TOMB] = ColorPair(Black, Red),
|
[PROCESS_TOMB] = ColorPair(Black, Red),
|
||||||
[PROCESS_THREAD] = ColorPair(Blue, Black),
|
[PROCESS_THREAD] = ColorPair(Blue, Black),
|
||||||
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black),
|
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black),
|
||||||
|
[PROCESS_COMM] = ColorPair(Magenta, Black),
|
||||||
|
[PROCESS_THREAD_COMM] = ColorPair(Yellow, Black),
|
||||||
[BAR_BORDER] = ColorPair(Blue, Black),
|
[BAR_BORDER] = ColorPair(Blue, Black),
|
||||||
[BAR_SHADOW] = ColorPairGrayBlack,
|
[BAR_SHADOW] = ColorPairGrayBlack,
|
||||||
[SWAP] = ColorPair(Red, Black),
|
[SWAP] = ColorPair(Red, Black),
|
||||||
|
@ -429,6 +441,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
|
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
|
||||||
[PANEL_SELECTION_UNFOCUS] = A_BOLD | ColorPair(Yellow, Blue),
|
[PANEL_SELECTION_UNFOCUS] = A_BOLD | ColorPair(Yellow, Blue),
|
||||||
[FAILED_SEARCH] = ColorPair(Red, Cyan),
|
[FAILED_SEARCH] = ColorPair(Red, Cyan),
|
||||||
|
[FAILED_READ] = A_BOLD | ColorPair(Red, Blue),
|
||||||
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
[PAUSED] = A_BOLD | ColorPair(Yellow, Cyan),
|
||||||
[UPTIME] = A_BOLD | ColorPair(Yellow, Blue),
|
[UPTIME] = A_BOLD | ColorPair(Yellow, Blue),
|
||||||
[BATTERY] = A_BOLD | ColorPair(Yellow, Blue),
|
[BATTERY] = A_BOLD | ColorPair(Yellow, Blue),
|
||||||
|
@ -457,6 +470,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PROCESS_TOMB] = ColorPair(Blue, Red),
|
[PROCESS_TOMB] = ColorPair(Blue, Red),
|
||||||
[PROCESS_THREAD] = ColorPair(Green, Blue),
|
[PROCESS_THREAD] = ColorPair(Green, Blue),
|
||||||
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Blue),
|
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Green, Blue),
|
||||||
|
[PROCESS_COMM] = ColorPair(Magenta, Blue),
|
||||||
|
[PROCESS_THREAD_COMM] = ColorPair(Black, Blue),
|
||||||
[BAR_BORDER] = A_BOLD | ColorPair(Yellow, Blue),
|
[BAR_BORDER] = A_BOLD | ColorPair(Yellow, Blue),
|
||||||
[BAR_SHADOW] = ColorPair(Cyan, Blue),
|
[BAR_SHADOW] = ColorPair(Cyan, Blue),
|
||||||
[SWAP] = ColorPair(Red, Blue),
|
[SWAP] = ColorPair(Red, Blue),
|
||||||
|
@ -510,6 +525,7 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
|
[PANEL_SELECTION_FOLLOW] = ColorPair(Black, Yellow),
|
||||||
[PANEL_SELECTION_UNFOCUS] = ColorPair(Black, White),
|
[PANEL_SELECTION_UNFOCUS] = ColorPair(Black, White),
|
||||||
[FAILED_SEARCH] = ColorPair(Red, Green),
|
[FAILED_SEARCH] = ColorPair(Red, Green),
|
||||||
|
[FAILED_READ] = A_BOLD | ColorPair(Red, Black),
|
||||||
[PAUSED] = A_BOLD | ColorPair(Yellow, Green),
|
[PAUSED] = A_BOLD | ColorPair(Yellow, Green),
|
||||||
[UPTIME] = ColorPair(Green, Black),
|
[UPTIME] = ColorPair(Green, Black),
|
||||||
[BATTERY] = ColorPair(Green, Black),
|
[BATTERY] = ColorPair(Green, Black),
|
||||||
|
@ -532,6 +548,8 @@ int CRT_colorSchemes[LAST_COLORSCHEME][LAST_COLORELEMENT] = {
|
||||||
[PROCESS_TREE] = ColorPair(Cyan, Black),
|
[PROCESS_TREE] = ColorPair(Cyan, Black),
|
||||||
[PROCESS_THREAD] = ColorPair(Green, Black),
|
[PROCESS_THREAD] = ColorPair(Green, Black),
|
||||||
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black),
|
[PROCESS_THREAD_BASENAME] = A_BOLD | ColorPair(Blue, Black),
|
||||||
|
[PROCESS_COMM] = ColorPair(Magenta, Black),
|
||||||
|
[PROCESS_THREAD_COMM] = ColorPair(Yellow, Black),
|
||||||
[PROCESS_R_STATE] = ColorPair(Green, Black),
|
[PROCESS_R_STATE] = ColorPair(Green, Black),
|
||||||
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
|
[PROCESS_D_STATE] = A_BOLD | ColorPair(Red, Black),
|
||||||
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
|
[PROCESS_HIGH_PRIORITY] = ColorPair(Red, Black),
|
||||||
|
|
3
CRT.h
3
CRT.h
|
@ -43,6 +43,7 @@ typedef enum ColorElements_ {
|
||||||
FUNCTION_BAR,
|
FUNCTION_BAR,
|
||||||
FUNCTION_KEY,
|
FUNCTION_KEY,
|
||||||
FAILED_SEARCH,
|
FAILED_SEARCH,
|
||||||
|
FAILED_READ,
|
||||||
PAUSED,
|
PAUSED,
|
||||||
PANEL_HEADER_FOCUS,
|
PANEL_HEADER_FOCUS,
|
||||||
PANEL_HEADER_UNFOCUS,
|
PANEL_HEADER_UNFOCUS,
|
||||||
|
@ -77,6 +78,8 @@ typedef enum ColorElements_ {
|
||||||
PROCESS_TOMB,
|
PROCESS_TOMB,
|
||||||
PROCESS_THREAD,
|
PROCESS_THREAD,
|
||||||
PROCESS_THREAD_BASENAME,
|
PROCESS_THREAD_BASENAME,
|
||||||
|
PROCESS_COMM,
|
||||||
|
PROCESS_THREAD_COMM,
|
||||||
BAR_BORDER,
|
BAR_BORDER,
|
||||||
BAR_SHADOW,
|
BAR_SHADOW,
|
||||||
GRAPH_1,
|
GRAPH_1,
|
||||||
|
|
|
@ -16,7 +16,7 @@ static void CommandScreen_scan(InfoScreen* this) {
|
||||||
int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
|
int idx = MAXIMUM(Panel_getSelectedIndex(panel), 0);
|
||||||
Panel_prune(panel);
|
Panel_prune(panel);
|
||||||
|
|
||||||
const char* p = this->process->comm;
|
const char* p = Process_getCommand(this->process);
|
||||||
char* line = xMalloc(COLS + 1);
|
char* line = xMalloc(COLS + 1);
|
||||||
int line_offset = 0, last_spc = -1, len;
|
int line_offset = 0, last_spc = -1, len;
|
||||||
for (; *p != '\0'; p++, line_offset++) {
|
for (; *p != '\0'; p++, line_offset++) {
|
||||||
|
@ -46,7 +46,7 @@ static void CommandScreen_scan(InfoScreen* this) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void CommandScreen_draw(InfoScreen* this) {
|
static void CommandScreen_draw(InfoScreen* this) {
|
||||||
InfoScreen_drawTitled(this, "Command of process %d - %s", this->process->pid, this->process->comm);
|
InfoScreen_drawTitled(this, "Command of process %d - %s", this->process->pid, Process_getCommand(this->process));
|
||||||
}
|
}
|
||||||
|
|
||||||
const InfoScreenClass CommandScreen_class = {
|
const InfoScreenClass CommandScreen_class = {
|
||||||
|
|
|
@ -84,6 +84,9 @@ DisplayOptionsPanel* DisplayOptionsPanel_new(Settings* settings, ScreenManager*
|
||||||
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show custom thread names"), &(settings->showThreadNames)));
|
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show custom thread names"), &(settings->showThreadNames)));
|
||||||
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show program path"), &(settings->showProgramPath)));
|
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Show program path"), &(settings->showProgramPath)));
|
||||||
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight program \"basename\""), &(settings->highlightBaseName)));
|
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight program \"basename\""), &(settings->highlightBaseName)));
|
||||||
|
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Try to find comm in cmdline, in merged Command"), &(settings->findCommInCmdline)));
|
||||||
|
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Try to strip exe from cmdline, in merged Command"), &(settings->stripExeFromCmdline)));
|
||||||
|
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Merge exe, comm and cmdline in Command"), &(settings->showMergedCommand)));
|
||||||
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight large numbers in memory counters"), &(settings->highlightMegabytes)));
|
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Highlight large numbers in memory counters"), &(settings->highlightMegabytes)));
|
||||||
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Leave a margin around header"), &(settings->headerMargin)));
|
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Leave a margin around header"), &(settings->headerMargin)));
|
||||||
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)"), &(settings->detailedCPUTime)));
|
Panel_add(super, (Object*) CheckItem_newByRef(xStrdup("Detailed CPU time (System/IO-Wait/Hard-IRQ/Soft-IRQ/Steal/Guest)"), &(settings->detailedCPUTime)));
|
||||||
|
|
|
@ -34,7 +34,7 @@ void EnvScreen_delete(Object* this) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnvScreen_draw(InfoScreen* this) {
|
void EnvScreen_draw(InfoScreen* this) {
|
||||||
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, this->process->comm);
|
InfoScreen_drawTitled(this, "Environment of process %d - %s", this->process->pid, Process_getCommand(this->process));
|
||||||
}
|
}
|
||||||
|
|
||||||
void EnvScreen_scan(InfoScreen* this) {
|
void EnvScreen_scan(InfoScreen* this) {
|
||||||
|
|
|
@ -136,7 +136,7 @@ int MainPanel_selectedPid(MainPanel* this) {
|
||||||
|
|
||||||
const char* MainPanel_getValue(MainPanel* this, int i) {
|
const char* MainPanel_getValue(MainPanel* this, int i) {
|
||||||
Process* p = (Process*) Panel_get((Panel*)this, i);
|
Process* p = (Process*) Panel_get((Panel*)this, i);
|
||||||
return p ? p->comm : "";
|
return Process_getCommand(p);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
|
bool MainPanel_foreachProcess(MainPanel* this, MainPanel_ForeachProcessFn fn, Arg arg, bool* wasAnyTagged) {
|
||||||
|
|
|
@ -82,7 +82,7 @@ void OpenFilesScreen_delete(Object* this) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static void OpenFilesScreen_draw(InfoScreen* this) {
|
static void OpenFilesScreen_draw(InfoScreen* this) {
|
||||||
InfoScreen_drawTitled(this, "Snapshot of files open in process %d - %s", ((OpenFilesScreen*)this)->pid, this->process->comm);
|
InfoScreen_drawTitled(this, "Snapshot of files open in process %d - %s", ((OpenFilesScreen*)this)->pid, Process_getCommand(this->process));
|
||||||
}
|
}
|
||||||
|
|
||||||
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
|
static OpenFiles_ProcessData* OpenFilesScreen_getProcessData(pid_t pid) {
|
||||||
|
|
|
@ -416,6 +416,10 @@ void Process_done(Process* this) {
|
||||||
free(this->comm);
|
free(this->comm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static const char* Process_getCommandStr(const Process* p) {
|
||||||
|
return p->comm ? p->comm : "";
|
||||||
|
}
|
||||||
|
|
||||||
const ProcessClass Process_class = {
|
const ProcessClass Process_class = {
|
||||||
.super = {
|
.super = {
|
||||||
.extends = Class(Object),
|
.extends = Class(Object),
|
||||||
|
@ -424,6 +428,7 @@ const ProcessClass Process_class = {
|
||||||
.compare = Process_compare
|
.compare = Process_compare
|
||||||
},
|
},
|
||||||
.writeField = Process_writeField,
|
.writeField = Process_writeField,
|
||||||
|
.getCommandStr = Process_getCommandStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
void Process_init(Process* this, const struct Settings_* settings) {
|
void Process_init(Process* this, const struct Settings_* settings) {
|
||||||
|
@ -503,7 +508,7 @@ long Process_compare(const void* v1, const void* v2) {
|
||||||
case PERCENT_MEM:
|
case PERCENT_MEM:
|
||||||
return SPACESHIP_NUMBER(p2->m_resident, p1->m_resident);
|
return SPACESHIP_NUMBER(p2->m_resident, p1->m_resident);
|
||||||
case COMM:
|
case COMM:
|
||||||
return SPACESHIP_NULLSTR(p1->comm, p2->comm);
|
return SPACESHIP_NULLSTR(Process_getCommand(p1), Process_getCommand(p2));
|
||||||
case MAJFLT:
|
case MAJFLT:
|
||||||
return SPACESHIP_NUMBER(p2->majflt, p1->majflt);
|
return SPACESHIP_NUMBER(p2->majflt, p1->majflt);
|
||||||
case MINFLT:
|
case MINFLT:
|
||||||
|
|
|
@ -67,7 +67,7 @@ typedef struct Process_ {
|
||||||
pid_t pid;
|
pid_t pid;
|
||||||
pid_t ppid;
|
pid_t ppid;
|
||||||
pid_t tgid;
|
pid_t tgid;
|
||||||
char* comm;
|
char* comm; /* use Process_getCommand() for a decorated command line */
|
||||||
int commLen;
|
int commLen;
|
||||||
int indent;
|
int indent;
|
||||||
|
|
||||||
|
@ -127,14 +127,18 @@ extern char Process_pidFormat[20];
|
||||||
|
|
||||||
typedef Process*(*Process_New)(const struct Settings_*);
|
typedef Process*(*Process_New)(const struct Settings_*);
|
||||||
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
|
typedef void (*Process_WriteField)(const Process*, RichString*, ProcessField);
|
||||||
|
typedef const char* (*Process_GetCommandStr)(const Process*);
|
||||||
|
|
||||||
typedef struct ProcessClass_ {
|
typedef struct ProcessClass_ {
|
||||||
const ObjectClass super;
|
const ObjectClass super;
|
||||||
const Process_WriteField writeField;
|
const Process_WriteField writeField;
|
||||||
|
const Process_GetCommandStr getCommandStr;
|
||||||
} ProcessClass;
|
} ProcessClass;
|
||||||
|
|
||||||
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
|
#define As_Process(this_) ((const ProcessClass*)((this_)->super.klass))
|
||||||
|
|
||||||
|
#define Process_getCommand(this_) As_Process(this_)->getCommandStr((const Process*)(this_))
|
||||||
|
|
||||||
static inline pid_t Process_getParentPid(const Process* this) {
|
static inline pid_t Process_getParentPid(const Process* this) {
|
||||||
return this->tgid == this->pid ? this->ppid : this->tgid;
|
return this->tgid == this->pid ? this->ppid : this->tgid;
|
||||||
}
|
}
|
||||||
|
|
|
@ -281,7 +281,7 @@ void ProcessList_rebuildPanel(ProcessList* this) {
|
||||||
|
|
||||||
if ( (!p->show)
|
if ( (!p->show)
|
||||||
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|
|| (this->userId != (uid_t) -1 && (p->st_uid != this->userId))
|
||||||
|| (incFilter && !(String_contains_i(p->comm, incFilter)))
|
|| (incFilter && !(String_contains_i(Process_getCommand(p), incFilter)))
|
||||||
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
|
|| (this->pidMatchList && !Hashtable_get(this->pidMatchList, p->tgid)) )
|
||||||
hidden = true;
|
hidden = true;
|
||||||
|
|
||||||
|
|
15
Settings.c
15
Settings.c
|
@ -161,6 +161,12 @@ static bool Settings_read(Settings* this, const char* fileName, int initialCpuCo
|
||||||
this->highlightChanges = atoi(option[1]);
|
this->highlightChanges = atoi(option[1]);
|
||||||
} else if (String_eq(option[0], "highlight_changes_delay_secs")) {
|
} else if (String_eq(option[0], "highlight_changes_delay_secs")) {
|
||||||
this->highlightDelaySecs = atoi(option[1]);
|
this->highlightDelaySecs = atoi(option[1]);
|
||||||
|
} else if (String_eq(option[0], "find_comm_in_cmdline")) {
|
||||||
|
this->findCommInCmdline = atoi(option[1]);
|
||||||
|
} else if (String_eq(option[0], "strip_exe_from_cmdline")) {
|
||||||
|
this->stripExeFromCmdline = atoi(option[1]);
|
||||||
|
} else if (String_eq(option[0], "show_merged_command")) {
|
||||||
|
this->showMergedCommand = atoi(option[1]);
|
||||||
} else if (String_eq(option[0], "header_margin")) {
|
} else if (String_eq(option[0], "header_margin")) {
|
||||||
this->headerMargin = atoi(option[1]);
|
this->headerMargin = atoi(option[1]);
|
||||||
} else if (String_eq(option[0], "expand_system_time")) {
|
} else if (String_eq(option[0], "expand_system_time")) {
|
||||||
|
@ -277,6 +283,9 @@ bool Settings_write(Settings* this) {
|
||||||
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
|
fprintf(fd, "highlight_threads=%d\n", (int) this->highlightThreads);
|
||||||
fprintf(fd, "highlight_changes=%d\n", (int) this->highlightChanges);
|
fprintf(fd, "highlight_changes=%d\n", (int) this->highlightChanges);
|
||||||
fprintf(fd, "highlight_changes_delay_secs=%d\n", (int) this->highlightDelaySecs);
|
fprintf(fd, "highlight_changes_delay_secs=%d\n", (int) this->highlightDelaySecs);
|
||||||
|
fprintf(fd, "find_comm_in_cmdline=%d\n", (int) this->findCommInCmdline);
|
||||||
|
fprintf(fd, "strip_exe_from_cmdline=%d\n", (int) this->stripExeFromCmdline);
|
||||||
|
fprintf(fd, "show_merged_command=%d\n", (int) this->showMergedCommand);
|
||||||
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
|
fprintf(fd, "tree_view=%d\n", (int) this->treeView);
|
||||||
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
|
fprintf(fd, "header_margin=%d\n", (int) this->headerMargin);
|
||||||
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
|
fprintf(fd, "detailed_cpu_time=%d\n", (int) this->detailedCPUTime);
|
||||||
|
@ -328,6 +337,9 @@ Settings* Settings_new(int initialCpuCount) {
|
||||||
this->highlightThreads = true;
|
this->highlightThreads = true;
|
||||||
this->highlightChanges = false;
|
this->highlightChanges = false;
|
||||||
this->highlightDelaySecs = DEFAULT_HIGHLIGHT_SECS;
|
this->highlightDelaySecs = DEFAULT_HIGHLIGHT_SECS;
|
||||||
|
this->findCommInCmdline = true;
|
||||||
|
this->stripExeFromCmdline = true;
|
||||||
|
this->showMergedCommand = false;
|
||||||
#ifdef HAVE_LIBHWLOC
|
#ifdef HAVE_LIBHWLOC
|
||||||
this->topologyAffinity = false;
|
this->topologyAffinity = false;
|
||||||
#endif
|
#endif
|
||||||
|
@ -406,6 +418,9 @@ Settings* Settings_new(int initialCpuCount) {
|
||||||
this->hideKernelThreads = true;
|
this->hideKernelThreads = true;
|
||||||
this->highlightMegabytes = true;
|
this->highlightMegabytes = true;
|
||||||
this->highlightThreads = true;
|
this->highlightThreads = true;
|
||||||
|
this->findCommInCmdline = true;
|
||||||
|
this->stripExeFromCmdline = true;
|
||||||
|
this->showMergedCommand = false;
|
||||||
this->headerMargin = true;
|
this->headerMargin = true;
|
||||||
}
|
}
|
||||||
return this;
|
return this;
|
||||||
|
|
|
@ -53,6 +53,9 @@ typedef struct Settings_ {
|
||||||
bool highlightThreads;
|
bool highlightThreads;
|
||||||
bool highlightChanges;
|
bool highlightChanges;
|
||||||
int highlightDelaySecs;
|
int highlightDelaySecs;
|
||||||
|
bool findCommInCmdline;
|
||||||
|
bool stripExeFromCmdline;
|
||||||
|
bool showMergedCommand;
|
||||||
bool updateProcessNames;
|
bool updateProcessNames;
|
||||||
bool accountGuestInCPUMeter;
|
bool accountGuestInCPUMeter;
|
||||||
bool headerMargin;
|
bool headerMargin;
|
||||||
|
|
|
@ -73,7 +73,7 @@ void TraceScreen_delete(Object* cast) {
|
||||||
void TraceScreen_draw(InfoScreen* this) {
|
void TraceScreen_draw(InfoScreen* this) {
|
||||||
attrset(CRT_colors[PANEL_HEADER_FOCUS]);
|
attrset(CRT_colors[PANEL_HEADER_FOCUS]);
|
||||||
mvhline(0, 0, ' ', COLS);
|
mvhline(0, 0, ' ', COLS);
|
||||||
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, this->process->comm);
|
mvprintw(0, 0, "Trace of process %d - %s", this->process->pid, Process_getCommand(this->process));
|
||||||
attrset(CRT_colors[DEFAULT_COLOR]);
|
attrset(CRT_colors[DEFAULT_COLOR]);
|
||||||
IncSet_drawBar(this->inc);
|
IncSet_drawBar(this->inc);
|
||||||
}
|
}
|
||||||
|
|
16
htop.1.in
16
htop.1.in
|
@ -218,6 +218,9 @@ Show full paths to running programs, where applicable. (This is a toggle key.)
|
||||||
.B Z
|
.B Z
|
||||||
Pause/resume process updates.
|
Pause/resume process updates.
|
||||||
.TP
|
.TP
|
||||||
|
.B m
|
||||||
|
Merge exe, comm and cmdline, where applicable. (This is a toggle key.)
|
||||||
|
.TP
|
||||||
.B Ctrl-L
|
.B Ctrl-L
|
||||||
Refresh: redraw screen and recalculate values.
|
Refresh: redraw screen and recalculate values.
|
||||||
.TP
|
.TP
|
||||||
|
@ -238,7 +241,18 @@ main screen, it is shown below in parenthesis.
|
||||||
.LP
|
.LP
|
||||||
.TP 5
|
.TP 5
|
||||||
.B Command
|
.B Command
|
||||||
The full command line of the process (i.e. program name and arguments).
|
The full command line of the process (i.e. program name and arguments). If the
|
||||||
|
option 'Merge exe, comm and cmdline in Command' (toggled by the 'm' key) is set,
|
||||||
|
and if readable, the executable path (/proc/[pid]/exe) and the command name
|
||||||
|
(/proc/[pid]/comm) are also shown merged with the command line.
|
||||||
|
.TP
|
||||||
|
.B Comm
|
||||||
|
The command name of the process obtained from /proc/[pid]/comm, if readable.
|
||||||
|
.TP
|
||||||
|
.B Exe
|
||||||
|
The abbreviated basename of the executable of the process, obtained from
|
||||||
|
/proc/[pid]/exe, if readable. htop is able to read this file on linux for ALL
|
||||||
|
the processes only if it has the capability CAP_SYS_PTRACE or root privileges.
|
||||||
.TP
|
.TP
|
||||||
.B PID
|
.B PID
|
||||||
The process ID.
|
The process ID.
|
||||||
|
|
|
@ -25,6 +25,9 @@ in the source distribution for its full text.
|
||||||
/* semi-global */
|
/* semi-global */
|
||||||
long long btime;
|
long long btime;
|
||||||
|
|
||||||
|
/* Used to identify kernel threads in Comm and Exe columns */
|
||||||
|
static const char *const kthreadID = "KTHREAD";
|
||||||
|
|
||||||
ProcessFieldData Process_fields[] = {
|
ProcessFieldData Process_fields[] = {
|
||||||
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
|
[0] = { .name = "", .title = NULL, .description = NULL, .flags = 0, },
|
||||||
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
|
[PID] = { .name = "PID", .title = " PID ", .description = "Process/thread ID", .flags = 0, },
|
||||||
|
@ -114,6 +117,8 @@ ProcessFieldData Process_fields[] = {
|
||||||
[M_PSSWP] = { .name = "M_PSSWP", .title = " PSSWP ", .description = "shows proportional swap share of this mapping, Unlike \"Swap\", this does not take into account swapped out page of underlying shmem objects.", .flags = PROCESS_FLAG_LINUX_SMAPS, },
|
[M_PSSWP] = { .name = "M_PSSWP", .title = " PSSWP ", .description = "shows proportional swap share of this mapping, Unlike \"Swap\", this does not take into account swapped out page of underlying shmem objects.", .flags = PROCESS_FLAG_LINUX_SMAPS, },
|
||||||
[CTXT] = { .name = "CTXT", .title = " CTXT ", .description = "Context switches (incremental sum of voluntary_ctxt_switches and nonvoluntary_ctxt_switches)", .flags = PROCESS_FLAG_LINUX_CTXT, },
|
[CTXT] = { .name = "CTXT", .title = " CTXT ", .description = "Context switches (incremental sum of voluntary_ctxt_switches and nonvoluntary_ctxt_switches)", .flags = PROCESS_FLAG_LINUX_CTXT, },
|
||||||
[SECATTR] = { .name = "SECATTR", .title = " Security Attribute ", .description = "Security attribute of the process (e.g. SELinux or AppArmor)", .flags = PROCESS_FLAG_LINUX_SECATTR, },
|
[SECATTR] = { .name = "SECATTR", .title = " Security Attribute ", .description = "Security attribute of the process (e.g. SELinux or AppArmor)", .flags = PROCESS_FLAG_LINUX_SECATTR, },
|
||||||
|
[PROC_COMM] = { .name = "COMM", .title = "COMM ", .description = "comm string of the process from /proc/[pid]/comm", .flags = 0, },
|
||||||
|
[PROC_EXE] = { .name = "EXE", .title = "EXE ", .description = "Basename of exe of the process from /proc/[pid]/exe", .flags = 0, },
|
||||||
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
|
[LAST_PROCESSFIELD] = { .name = "*** report bug! ***", .title = NULL, .description = NULL, .flags = 0, },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -130,6 +135,17 @@ ProcessPidColumn Process_pidColumns[] = {
|
||||||
{ .id = 0, .label = NULL },
|
{ .id = 0, .label = NULL },
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* This function returns the string displayed in Command column, so that sorting
|
||||||
|
* happens on what is displayed - whether comm, full path, basename, etc.. So
|
||||||
|
* this follows LinuxProcess_writeField(COMM) and LinuxProcess_writeCommand */
|
||||||
|
static const char* LinuxProcess_getCommandStr(const Process *this) {
|
||||||
|
const LinuxProcess *lp = (const LinuxProcess *)this;
|
||||||
|
if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !lp->mergedCommand.str) {
|
||||||
|
return this->comm;
|
||||||
|
}
|
||||||
|
return lp->mergedCommand.str;
|
||||||
|
}
|
||||||
|
|
||||||
Process* LinuxProcess_new(const Settings* settings) {
|
Process* LinuxProcess_new(const Settings* settings) {
|
||||||
LinuxProcess* this = xCalloc(1, sizeof(LinuxProcess));
|
LinuxProcess* this = xCalloc(1, sizeof(LinuxProcess));
|
||||||
Object_setClass(this, Class(LinuxProcess));
|
Object_setClass(this, Class(LinuxProcess));
|
||||||
|
@ -148,6 +164,9 @@ void Process_delete(Object* cast) {
|
||||||
#endif
|
#endif
|
||||||
free(this->secattr);
|
free(this->secattr);
|
||||||
free(this->ttyDevice);
|
free(this->ttyDevice);
|
||||||
|
free(this->procExe);
|
||||||
|
free(this->procComm);
|
||||||
|
free(this->mergedCommand.str);
|
||||||
free(this);
|
free(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -195,6 +214,348 @@ static void LinuxProcess_printDelay(float delay_percent, char* buffer, int n) {
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
/*
|
||||||
|
TASK_COMM_LEN is defined to be 16 for /proc/[pid]/comm in man proc(5), but it is
|
||||||
|
not available in an userspace header - so define it. Note: when colorizing a
|
||||||
|
basename with the comm prefix, the entire basename (not just the comm prefix) is
|
||||||
|
colorized for better readability, and it is implicit that only upto
|
||||||
|
(TASK_COMM_LEN - 1) could be comm
|
||||||
|
*/
|
||||||
|
#define TASK_COMM_LEN 16
|
||||||
|
|
||||||
|
static bool findCommInCmdline(const char *comm, const char *cmdline, int cmdlineBasenameOffset, int *pCommStart, int *pCommEnd) {
|
||||||
|
/* Try to find procComm in tokenized cmdline - this might in rare cases
|
||||||
|
* mis-identify a string or fail, if comm or cmdline had been unsuitably
|
||||||
|
* modified by the process */
|
||||||
|
const char *token;
|
||||||
|
const char *tokenBase;
|
||||||
|
size_t tokenLen;
|
||||||
|
const size_t commLen = strlen(comm);
|
||||||
|
|
||||||
|
for (token = cmdline + cmdlineBasenameOffset; *token; ) {
|
||||||
|
for (tokenBase = token; *token && *token != '\n'; ++token) {
|
||||||
|
if (*token == '/') {
|
||||||
|
tokenBase = token + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tokenLen = token - tokenBase;
|
||||||
|
|
||||||
|
if (((commLen < (TASK_COMM_LEN - 1) && tokenLen == commLen) ||
|
||||||
|
(commLen == (TASK_COMM_LEN - 1) && tokenLen >= commLen)) &&
|
||||||
|
strncmp(tokenBase, comm, commLen) == 0) {
|
||||||
|
*pCommStart = tokenBase - cmdline;
|
||||||
|
*pCommEnd = token - cmdline;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (*token) {
|
||||||
|
do {
|
||||||
|
++token;
|
||||||
|
} while ('\n' == *token);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int matchCmdlinePrefixWithExeSuffix(const char *cmdline, int cmdlineBaseOffset, const char *exe, int exeBaseOffset, int exeBaseLen) {
|
||||||
|
int matchLen; /* matching length to be returned */
|
||||||
|
char delim; /* delimiter following basename */
|
||||||
|
|
||||||
|
/* cmdline prefix is an absolute path: it must match whole exe. */
|
||||||
|
if (cmdline[0] == '/') {
|
||||||
|
matchLen = exeBaseLen + exeBaseOffset;
|
||||||
|
if (strncmp(cmdline, exe, matchLen) == 0) {
|
||||||
|
delim = cmdline[matchLen];
|
||||||
|
if (delim == 0 || delim == '\n' || delim == ' ') {
|
||||||
|
return matchLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* cmdline prefix is a relative path: We need to first match the basename at
|
||||||
|
* cmdlineBaseOffset and then reverse match the cmdline prefix with the exe
|
||||||
|
* suffix. But there is a catch: Some processes modify their cmdline in ways
|
||||||
|
* that make htop's identification of the basename in cmdline unreliable.
|
||||||
|
* For e.g. /usr/libexec/gdm-session-worker modifies its cmdline to
|
||||||
|
* "gdm-session-worker [pam/gdm-autologin]" and htop ends up with
|
||||||
|
* procCmdlineBasenameOffset at "gdm-autologin]". This issue could arise with
|
||||||
|
* chrome as well as it stores in cmdline its concatenated argument vector,
|
||||||
|
* without NUL delimiter between the arguments (which may contain a '/')
|
||||||
|
*
|
||||||
|
* So if needed, we adjust cmdlineBaseOffset to the previous (if any)
|
||||||
|
* component of the cmdline relative path, and retry the procedure. */
|
||||||
|
bool delimFound; /* if valid basename delimiter found */
|
||||||
|
do {
|
||||||
|
/* match basename */
|
||||||
|
matchLen = exeBaseLen + cmdlineBaseOffset;
|
||||||
|
if (cmdlineBaseOffset < exeBaseOffset &&
|
||||||
|
strncmp(cmdline + cmdlineBaseOffset, exe + exeBaseOffset, exeBaseLen) == 0) {
|
||||||
|
delim = cmdline[matchLen];
|
||||||
|
if (delim == 0 || delim == '\n' || delim == ' ') {
|
||||||
|
int i, j;
|
||||||
|
/* reverse match the cmdline prefix and exe suffix */
|
||||||
|
for (i = cmdlineBaseOffset - 1, j = exeBaseOffset - 1;
|
||||||
|
i >= 0 && cmdline[i] == exe[j]; --i, --j)
|
||||||
|
;
|
||||||
|
/* full match, with exe suffix being a valid relative path */
|
||||||
|
if (i < 0 && exe[j] == '/') {
|
||||||
|
return matchLen;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* Try to find the previous potential cmdlineBaseOffset - it would be
|
||||||
|
* preceded by '/' or nothing, and delimited by ' ' or '\n' */
|
||||||
|
for (delimFound = false, cmdlineBaseOffset -= 2; cmdlineBaseOffset > 0; --cmdlineBaseOffset) {
|
||||||
|
if (delimFound) {
|
||||||
|
if (cmdline[cmdlineBaseOffset - 1] == '/') {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} else if (cmdline[cmdlineBaseOffset] == ' ' || cmdline[cmdlineBaseOffset] == '\n') {
|
||||||
|
delimFound = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} while (delimFound);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* stpcpy, but also converts newlines to spaces */
|
||||||
|
static inline char *stpcpyWithNewlineConversion(char *dstStr, const char *srcStr) {
|
||||||
|
for (; *srcStr; ++srcStr) {
|
||||||
|
*dstStr++ = (*srcStr == '\n') ? ' ' : *srcStr;
|
||||||
|
}
|
||||||
|
*dstStr = 0;
|
||||||
|
return dstStr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
This function makes the merged Command string. It also stores the offsets of the
|
||||||
|
basename, comm w.r.t the merged Command string - these offsets will be used by
|
||||||
|
LinuxProcess_writeCommand() for coloring. The merged Command string is also
|
||||||
|
returned by LinuxProcess_getCommandStr() for searching, sorting and filtering.
|
||||||
|
*/
|
||||||
|
void LinuxProcess_makeCommandStr(Process* this) {
|
||||||
|
LinuxProcess *lp = (LinuxProcess *)this;
|
||||||
|
bool showMergedCommand = this->settings->showMergedCommand;
|
||||||
|
bool showProgramPath = this->settings->showProgramPath;
|
||||||
|
bool searchCommInCmdline = this->settings->findCommInCmdline;
|
||||||
|
bool stripExeFromCmdline = this->settings->stripExeFromCmdline;
|
||||||
|
|
||||||
|
/* lp->mergedCommand.str needs to be remade only if there is a change in its
|
||||||
|
* state consisting of the relevant settings and the three fields cmdline,
|
||||||
|
* comm and exe */
|
||||||
|
if (showMergedCommand == lp->mergedCommand.prevMergeSet && showProgramPath == lp->mergedCommand.prevPathSet &&
|
||||||
|
searchCommInCmdline == lp->mergedCommand.prevCommSet && stripExeFromCmdline == lp->mergedCommand.prevCmdlineSet &&
|
||||||
|
!lp->mergedCommand.cmdlineChanged && !lp->mergedCommand.commChanged && !lp->mergedCommand.exeChanged) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* The field separtor "│" has been chosen such that it will not match any
|
||||||
|
* valid search string used for sorting or filtering */
|
||||||
|
const char *SEPARATOR = CRT_treeStr[TREE_STR_VERT];
|
||||||
|
const int SEPARATOR_LEN = strlen(SEPARATOR);
|
||||||
|
|
||||||
|
if (lp->mergedCommand.cmdlineChanged || lp->mergedCommand.commChanged || lp->mergedCommand.exeChanged) {
|
||||||
|
free(lp->mergedCommand.str);
|
||||||
|
/* Also accomodate two field separators and a NUL */
|
||||||
|
lp->mergedCommand.str = xMalloc(lp->mergedCommand.maxLen + 2*SEPARATOR_LEN + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lp->mergedCommand.prevMergeSet = showMergedCommand;
|
||||||
|
lp->mergedCommand.prevPathSet = showProgramPath;
|
||||||
|
lp->mergedCommand.prevCommSet = searchCommInCmdline;
|
||||||
|
lp->mergedCommand.prevCmdlineSet = stripExeFromCmdline;
|
||||||
|
lp->mergedCommand.cmdlineChanged = false;
|
||||||
|
lp->mergedCommand.commChanged = false;
|
||||||
|
lp->mergedCommand.exeChanged = false;
|
||||||
|
|
||||||
|
char *str;
|
||||||
|
char *strStart = lp->mergedCommand.str;
|
||||||
|
const char *cmdline = this->comm;
|
||||||
|
const char *procExe = lp->procExe;
|
||||||
|
const char *procComm = lp->procComm;
|
||||||
|
int cmdlineBasenameOffset = lp->procCmdlineBasenameOffset;
|
||||||
|
|
||||||
|
if (!showMergedCommand || !procExe || !procComm) { /* fall back to cmdline */
|
||||||
|
if (showProgramPath) {
|
||||||
|
(void) stpcpyWithNewlineConversion(strStart, cmdline);
|
||||||
|
lp->mergedCommand.baseStart = cmdlineBasenameOffset;
|
||||||
|
lp->mergedCommand.baseEnd = lp->procCmdlineBasenameEnd;
|
||||||
|
} else {
|
||||||
|
(void) stpcpyWithNewlineConversion(strStart, cmdline + cmdlineBasenameOffset);
|
||||||
|
lp->mergedCommand.baseStart = 0;
|
||||||
|
lp->mergedCommand.baseEnd = lp->procCmdlineBasenameEnd - cmdlineBasenameOffset;
|
||||||
|
}
|
||||||
|
lp->mergedCommand.commEnd = 0;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int commStart = 0;
|
||||||
|
int commEnd = 0;
|
||||||
|
int exeBasenameOffset = lp->procExeBasenameOffset;
|
||||||
|
int exeLen = lp->procExeLen;
|
||||||
|
int exeBaseLen = exeLen - exeBasenameOffset;
|
||||||
|
bool commInCmdline = false;
|
||||||
|
|
||||||
|
/* Start with copying exe */
|
||||||
|
if (showProgramPath) {
|
||||||
|
str = stpcpy(strStart, procExe);
|
||||||
|
lp->mergedCommand.baseStart = exeBasenameOffset;
|
||||||
|
lp->mergedCommand.baseEnd = exeLen;
|
||||||
|
} else {
|
||||||
|
str = stpcpy(strStart, procExe + exeBasenameOffset);
|
||||||
|
lp->mergedCommand.baseStart = 0;
|
||||||
|
lp->mergedCommand.baseEnd = exeBaseLen;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Try to match procComm with procExe's basename: This is reliable (predictable) */
|
||||||
|
if (strncmp(procExe + exeBasenameOffset, procComm, TASK_COMM_LEN - 1) == 0) {
|
||||||
|
commStart = lp->mergedCommand.baseStart;
|
||||||
|
commEnd = lp->mergedCommand.baseEnd;
|
||||||
|
} else if (searchCommInCmdline) {
|
||||||
|
/* commStart/commEnd will be adjusted later along with cmdline */
|
||||||
|
commInCmdline = findCommInCmdline(procComm, cmdline, cmdlineBasenameOffset, &commStart, &commEnd);
|
||||||
|
}
|
||||||
|
|
||||||
|
int matchLen = matchCmdlinePrefixWithExeSuffix(cmdline, cmdlineBasenameOffset,
|
||||||
|
procExe, exeBasenameOffset, exeBaseLen);
|
||||||
|
/* Note: commStart, commEnd are offsets into RichString. But the multibyte
|
||||||
|
* separator (with size SEPARATOR_LEN) has size 1 in RichString. The offset
|
||||||
|
* adjustments below reflect this. */
|
||||||
|
if (commEnd) {
|
||||||
|
if (matchLen) { /* strip the matched exe prefix */
|
||||||
|
lp->mergedCommand.unmatchedExe = false;
|
||||||
|
cmdline += matchLen;
|
||||||
|
if (commInCmdline) {
|
||||||
|
commStart += str - strStart - matchLen;
|
||||||
|
commEnd += str - strStart - matchLen;
|
||||||
|
}
|
||||||
|
} else { /* cmdline will be a separate field */
|
||||||
|
lp->mergedCommand.unmatchedExe = true;
|
||||||
|
str = stpcpy(str, SEPARATOR);
|
||||||
|
if (commInCmdline) {
|
||||||
|
commStart += str - strStart - SEPARATOR_LEN + 1;
|
||||||
|
commEnd += str - strStart - SEPARATOR_LEN + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
lp->mergedCommand.separateComm = false; /* procComm merged */
|
||||||
|
} else {
|
||||||
|
str = stpcpy(str, SEPARATOR);
|
||||||
|
commStart = str - strStart - SEPARATOR_LEN + 1;
|
||||||
|
str = stpcpy(str, procComm);
|
||||||
|
commEnd = str - strStart - SEPARATOR_LEN + 1; /* or commStart + strlen(procComm) */
|
||||||
|
if (matchLen) {
|
||||||
|
lp->mergedCommand.unmatchedExe = false;
|
||||||
|
if (stripExeFromCmdline) {
|
||||||
|
cmdline += matchLen;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
lp->mergedCommand.unmatchedExe = true;
|
||||||
|
}
|
||||||
|
if (*cmdline) {
|
||||||
|
str = stpcpy(str, SEPARATOR);
|
||||||
|
}
|
||||||
|
lp->mergedCommand.separateComm = true; /* procComm a separate field */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Display cmdline if it hasn't been consumed by procExe */
|
||||||
|
if (*cmdline) {
|
||||||
|
(void) stpcpyWithNewlineConversion(str, cmdline);
|
||||||
|
}
|
||||||
|
|
||||||
|
lp->mergedCommand.commStart = commStart;
|
||||||
|
lp->mergedCommand.commEnd = commEnd;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LinuxProcess_writeCommand(const Process* this, int attr, int baseattr, RichString* str) {
|
||||||
|
const LinuxProcess *lp = (const LinuxProcess *)this;
|
||||||
|
int strStart = RichString_size(str);
|
||||||
|
int baseStart = strStart + lp->mergedCommand.baseStart;
|
||||||
|
int baseEnd = strStart + lp->mergedCommand.baseEnd;
|
||||||
|
bool highlightBaseName = this->settings->highlightBaseName;
|
||||||
|
|
||||||
|
RichString_append(str, attr, lp->mergedCommand.str);
|
||||||
|
|
||||||
|
if (lp->mergedCommand.commEnd) {
|
||||||
|
int commStart = strStart + lp->mergedCommand.commStart;
|
||||||
|
int commEnd = strStart + lp->mergedCommand.commEnd;
|
||||||
|
int commAttr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM];
|
||||||
|
if (lp->mergedCommand.separateComm) {
|
||||||
|
RichString_setAttrn(str, commAttr, commStart, commEnd - 1);
|
||||||
|
if (lp->mergedCommand.unmatchedExe) {
|
||||||
|
RichString_setAttrn(str, CRT_colors[FAILED_READ], commEnd, commEnd);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
/* If it was matched with procExe's basename, make it bold if needed */
|
||||||
|
if (commStart == baseStart && highlightBaseName) {
|
||||||
|
if (commEnd > baseEnd) {
|
||||||
|
RichString_setAttrn(str, A_BOLD | commAttr, commStart, baseEnd - 1);
|
||||||
|
baseStart = baseEnd;
|
||||||
|
RichString_setAttrn(str, commAttr, baseStart, commEnd - 1);
|
||||||
|
} else {
|
||||||
|
RichString_setAttrn(str, A_BOLD | commAttr, commStart, commEnd - 1);
|
||||||
|
baseStart = commEnd;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
RichString_setAttrn(str, commAttr, commStart, commEnd - 1);
|
||||||
|
}
|
||||||
|
if (lp->mergedCommand.unmatchedExe) {
|
||||||
|
RichString_setAttrn(str, CRT_colors[FAILED_READ], baseEnd, baseEnd);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (baseStart < baseEnd && highlightBaseName) {
|
||||||
|
RichString_setAttrn(str, baseattr, baseStart, baseEnd - 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void LinuxProcess_writeCommandField(const Process *this, RichString *str, char *buffer, int n, int attr) {
|
||||||
|
/* This code is from Process_writeField for COMM, but we invoke
|
||||||
|
* LinuxProcess_writeCommand to display
|
||||||
|
* /proc/pid/exe (or its basename)│/proc/pid/comm│/proc/pid/cmdline */
|
||||||
|
int baseattr = CRT_colors[PROCESS_BASENAME];
|
||||||
|
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) {
|
||||||
|
LinuxProcess_writeCommand(this, attr, baseattr, str);
|
||||||
|
} else {
|
||||||
|
char* buf = buffer;
|
||||||
|
int maxIndent = 0;
|
||||||
|
bool lastItem = (this->indent < 0);
|
||||||
|
int indent = (this->indent < 0 ? -this->indent : this->indent);
|
||||||
|
int vertLen = strlen(CRT_treeStr[TREE_STR_VERT]);
|
||||||
|
|
||||||
|
for (int i = 0; i < 32; i++) {
|
||||||
|
if (indent & (1U << i)) {
|
||||||
|
maxIndent = i+1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (int i = 0; i < maxIndent - 1; i++) {
|
||||||
|
if (indent & (1 << i)) {
|
||||||
|
if (buf - buffer + (vertLen + 3) > n) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf = stpcpy(buf, CRT_treeStr[TREE_STR_VERT]);
|
||||||
|
buf = stpcpy(buf, " ");
|
||||||
|
} else {
|
||||||
|
if (buf - buffer + 4 > n) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
buf = stpcpy(buf, " ");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
n -= (buf - buffer);
|
||||||
|
const char* draw = CRT_treeStr[lastItem ? (this->settings->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);
|
||||||
|
LinuxProcess_writeCommand(this, attr, baseattr, str);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
static void LinuxProcess_writeField(const Process* this, RichString* str, ProcessField field) {
|
static void LinuxProcess_writeField(const Process* this, RichString* str, ProcessField field) {
|
||||||
const LinuxProcess* lp = (const LinuxProcess*) this;
|
const LinuxProcess* lp = (const LinuxProcess*) this;
|
||||||
bool coloring = this->settings->highlightMegabytes;
|
bool coloring = this->settings->highlightMegabytes;
|
||||||
|
@ -289,6 +650,35 @@ static void LinuxProcess_writeField(const Process* this, RichString* str, Proces
|
||||||
xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff);
|
xSnprintf(buffer, n, "%5lu ", lp->ctxt_diff);
|
||||||
break;
|
break;
|
||||||
case SECATTR: snprintf(buffer, n, "%-30s ", lp->secattr ? lp->secattr : "?"); break;
|
case SECATTR: snprintf(buffer, n, "%-30s ", lp->secattr ? lp->secattr : "?"); break;
|
||||||
|
case COMM: {
|
||||||
|
if ((Process_isUserlandThread(this) && this->settings->showThreadNames) || !lp->mergedCommand.str) {
|
||||||
|
Process_writeField(this, str, field);
|
||||||
|
} else {
|
||||||
|
LinuxProcess_writeCommandField(this, str, buffer, n, attr);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case PROC_COMM: {
|
||||||
|
if (lp->procComm) {
|
||||||
|
attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_COMM : PROCESS_COMM];
|
||||||
|
/* 15 being (TASK_COMM_LEN - 1) */
|
||||||
|
xSnprintf(buffer, n, "%-15.15s ", lp->procComm);
|
||||||
|
} else {
|
||||||
|
attr = CRT_colors[FAILED_READ];
|
||||||
|
xSnprintf(buffer, n, "%-15.15s ", Process_isKernelThread(lp) ? kthreadID : "?");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case PROC_EXE: {
|
||||||
|
if (lp->procExe) {
|
||||||
|
attr = CRT_colors[Process_isUserlandThread(this) ? PROCESS_THREAD_BASENAME : PROCESS_BASENAME];
|
||||||
|
xSnprintf(buffer, n, "%-15.15s ", lp->procExe + lp->procExeBasenameOffset);
|
||||||
|
} else {
|
||||||
|
attr = CRT_colors[FAILED_READ];
|
||||||
|
xSnprintf(buffer, n, "%-15.15s ", Process_isKernelThread(lp) ? kthreadID : "?");
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
Process_writeField(this, str, field);
|
Process_writeField(this, str, field);
|
||||||
return;
|
return;
|
||||||
|
@ -385,6 +775,16 @@ static long LinuxProcess_compare(const void* v1, const void* v2) {
|
||||||
return SPACESHIP_NUMBER(p2->ctxt_diff, p1->ctxt_diff);
|
return SPACESHIP_NUMBER(p2->ctxt_diff, p1->ctxt_diff);
|
||||||
case SECATTR:
|
case SECATTR:
|
||||||
return SPACESHIP_NULLSTR(p1->secattr, p2->secattr);
|
return SPACESHIP_NULLSTR(p1->secattr, p2->secattr);
|
||||||
|
case PROC_COMM: {
|
||||||
|
const char *comm1 = p1->procComm ? p1->procComm : (Process_isKernelThread(p1) ? kthreadID : "");
|
||||||
|
const char *comm2 = p2->procComm ? p2->procComm : (Process_isKernelThread(p2) ? kthreadID : "");
|
||||||
|
return strcmp(comm1, comm2);
|
||||||
|
}
|
||||||
|
case PROC_EXE: {
|
||||||
|
const char *exe1 = p1->procExe ? (p1->procExe + p1->procExeBasenameOffset) : (Process_isKernelThread(p1) ? kthreadID : "");
|
||||||
|
const char *exe2 = p2->procExe ? (p2->procExe + p2->procExeBasenameOffset) : (Process_isKernelThread(p2) ? kthreadID : "");
|
||||||
|
return strcmp(exe1, exe2);
|
||||||
|
}
|
||||||
default:
|
default:
|
||||||
return Process_compare(v1, v2);
|
return Process_compare(v1, v2);
|
||||||
}
|
}
|
||||||
|
@ -401,5 +801,6 @@ const ProcessClass LinuxProcess_class = {
|
||||||
.delete = Process_delete,
|
.delete = Process_delete,
|
||||||
.compare = LinuxProcess_compare
|
.compare = LinuxProcess_compare
|
||||||
},
|
},
|
||||||
.writeField = LinuxProcess_writeField
|
.writeField = LinuxProcess_writeField,
|
||||||
|
.getCommandStr = LinuxProcess_getCommandStr
|
||||||
};
|
};
|
||||||
|
|
|
@ -95,11 +95,42 @@ typedef enum LinuxProcessFields {
|
||||||
M_PSSWP = 121,
|
M_PSSWP = 121,
|
||||||
CTXT = 122,
|
CTXT = 122,
|
||||||
SECATTR = 123,
|
SECATTR = 123,
|
||||||
LAST_PROCESSFIELD = 124,
|
PROC_COMM = 124,
|
||||||
|
PROC_EXE = 125,
|
||||||
|
LAST_PROCESSFIELD = 126,
|
||||||
} LinuxProcessField;
|
} LinuxProcessField;
|
||||||
|
|
||||||
|
/* LinuxProcessMergedCommand is populated by LinuxProcess_makeCommandStr: It
|
||||||
|
* contains the merged Command string, and the information needed by
|
||||||
|
* LinuxProcess_writeCommand to color the string. str will be NULL for kernel
|
||||||
|
* threads and zombies */
|
||||||
|
typedef struct LinuxProcessMergedCommand_ {
|
||||||
|
char *str; /* merged Command string */
|
||||||
|
int maxLen; /* maximum expected length of Command string */
|
||||||
|
int baseStart; /* basename's start offset */
|
||||||
|
int baseEnd; /* basename's end offset */
|
||||||
|
int commStart; /* comm's start offset */
|
||||||
|
int commEnd; /* comm's end offset */
|
||||||
|
bool separateComm; /* whether comm is a separate field */
|
||||||
|
bool unmatchedExe; /* whether exe matched with cmdline */
|
||||||
|
bool cmdlineChanged; /* whether cmdline changed */
|
||||||
|
bool exeChanged; /* whether exe changed */
|
||||||
|
bool commChanged; /* whether comm changed */
|
||||||
|
bool prevMergeSet; /* whether showMergedCommand was set */
|
||||||
|
bool prevPathSet; /* whether showProgramPath was set */
|
||||||
|
bool prevCommSet; /* whether findCommInCmdline was set */
|
||||||
|
bool prevCmdlineSet; /* whether findCommInCmdline was set */
|
||||||
|
} LinuxProcessMergedCommand;
|
||||||
|
|
||||||
typedef struct LinuxProcess_ {
|
typedef struct LinuxProcess_ {
|
||||||
Process super;
|
Process super;
|
||||||
|
char *procComm;
|
||||||
|
char *procExe;
|
||||||
|
int procExeLen;
|
||||||
|
int procExeBasenameOffset;
|
||||||
|
int procCmdlineBasenameOffset;
|
||||||
|
int procCmdlineBasenameEnd;
|
||||||
|
LinuxProcessMergedCommand mergedCommand;
|
||||||
bool isKernelThread;
|
bool isKernelThread;
|
||||||
IOPriority ioPriority;
|
IOPriority ioPriority;
|
||||||
unsigned long int cminflt;
|
unsigned long int cminflt;
|
||||||
|
@ -177,6 +208,10 @@ IOPriority LinuxProcess_updateIOPriority(LinuxProcess* this);
|
||||||
|
|
||||||
bool LinuxProcess_setIOPriority(Process* this, Arg ioprio);
|
bool LinuxProcess_setIOPriority(Process* this, Arg ioprio);
|
||||||
|
|
||||||
|
/* This function constructs the string that is displayed by
|
||||||
|
* LinuxProcess_writeCommand and also returned by LinuxProcess_getCommandStr */
|
||||||
|
void LinuxProcess_makeCommandStr(Process *this);
|
||||||
|
|
||||||
bool Process_isThread(const Process* this);
|
bool Process_isThread(const Process* this);
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -875,6 +875,7 @@ static void setCommand(Process* process, const char* command, int len) {
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
|
static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
|
||||||
|
LinuxProcess *lp = (LinuxProcess *)process;
|
||||||
char filename[MAX_NAME + 1];
|
char filename[MAX_NAME + 1];
|
||||||
xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
|
xSnprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
|
||||||
int fd = open(filename, O_RDONLY);
|
int fd = open(filename, O_RDONLY);
|
||||||
|
@ -885,6 +886,7 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
|
||||||
int amtRead = xread(fd, command, sizeof(command) - 1);
|
int amtRead = xread(fd, command, sizeof(command) - 1);
|
||||||
close(fd);
|
close(fd);
|
||||||
int tokenEnd = 0;
|
int tokenEnd = 0;
|
||||||
|
int tokenStart = 0;
|
||||||
int lastChar = 0;
|
int lastChar = 0;
|
||||||
if (amtRead == 0) {
|
if (amtRead == 0) {
|
||||||
if (process->state == 'Z') {
|
if (process->state == 'Z') {
|
||||||
|
@ -897,12 +899,23 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
for (int i = 0; i < amtRead; i++) {
|
for (int i = 0; i < amtRead; i++) {
|
||||||
if (command[i] == '\0' || command[i] == '\n') {
|
/* newline used as delimiter - when forming the mergedCommand, newline is
|
||||||
|
* converted to space by LinuxProcess_makeCommandStr */
|
||||||
|
if (command[i] == '\0') {
|
||||||
|
command[i] = '\n';
|
||||||
|
}
|
||||||
|
|
||||||
|
if (command[i] == '\n') {
|
||||||
if (tokenEnd == 0) {
|
if (tokenEnd == 0) {
|
||||||
tokenEnd = i;
|
tokenEnd = i;
|
||||||
}
|
}
|
||||||
command[i] = ' ';
|
|
||||||
} else {
|
} else {
|
||||||
|
/* htop considers the next character after the last / that is before
|
||||||
|
* basenameOffset, as the start of the basename in cmdline - see
|
||||||
|
* Process_writeCommand */
|
||||||
|
if (!tokenEnd && command[i] == '/') {
|
||||||
|
tokenStart = i + 1;
|
||||||
|
}
|
||||||
lastChar = i;
|
lastChar = i;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -910,8 +923,55 @@ static bool LinuxProcessList_readCmdlineFile(Process* process, const char* dirna
|
||||||
tokenEnd = amtRead;
|
tokenEnd = amtRead;
|
||||||
}
|
}
|
||||||
command[lastChar + 1] = '\0';
|
command[lastChar + 1] = '\0';
|
||||||
|
lp->mergedCommand.maxLen = lastChar + 1; /* accomodate cmdline */
|
||||||
|
if (!process->comm || strcmp(command, process->comm)) {
|
||||||
process->basenameOffset = tokenEnd;
|
process->basenameOffset = tokenEnd;
|
||||||
setCommand(process, command, lastChar + 1);
|
setCommand(process, command, lastChar + 1);
|
||||||
|
lp->procCmdlineBasenameOffset = tokenStart;
|
||||||
|
lp->procCmdlineBasenameEnd = tokenEnd;
|
||||||
|
lp->mergedCommand.cmdlineChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* /proc/[pid]/comm could change, so should be udpated */
|
||||||
|
xSnprintf(filename, MAX_NAME, "%s/%s/comm", dirname, name);
|
||||||
|
if ((fd = open(filename, O_RDONLY)) != -1 &&
|
||||||
|
(amtRead = xread(fd, command, sizeof(command) - 1)) > 0) {
|
||||||
|
close(fd);
|
||||||
|
command[amtRead - 1] = 0;
|
||||||
|
lp->mergedCommand.maxLen += amtRead - 1; /* accomodate comm */
|
||||||
|
if (!lp->procComm || strcmp(command, lp->procComm)) {
|
||||||
|
free(lp->procComm);
|
||||||
|
lp->procComm = xStrdup(command);
|
||||||
|
lp->mergedCommand.commChanged = true;
|
||||||
|
}
|
||||||
|
} else if (lp->procComm) {
|
||||||
|
free(lp->procComm);
|
||||||
|
lp->procComm = NULL;
|
||||||
|
lp->mergedCommand.commChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* execve could change /proc/[pid]/exe, so procExe should be udpated */
|
||||||
|
xSnprintf(command, sizeof(command), "%s/%s/exe", dirname, name);
|
||||||
|
if ((amtRead = readlink(command, filename, sizeof(filename) - 1)) > 0) {
|
||||||
|
filename[amtRead] = 0;
|
||||||
|
lp->mergedCommand.maxLen += amtRead; /* accomodate exe */
|
||||||
|
if (!lp->procExe || strcmp(filename, lp->procExe)) {
|
||||||
|
free(lp->procExe);
|
||||||
|
lp->procExe = xStrdup(filename);
|
||||||
|
lp->procExeLen = amtRead;
|
||||||
|
/* exe is guaranteed to contain at least one /, but validate anyway */
|
||||||
|
while (amtRead && filename[--amtRead] != '/')
|
||||||
|
;
|
||||||
|
lp->procExeBasenameOffset = amtRead + 1;
|
||||||
|
lp->mergedCommand.exeChanged = true;
|
||||||
|
}
|
||||||
|
} else if (lp->procExe) {
|
||||||
|
free(lp->procExe);
|
||||||
|
lp->procExe = NULL;
|
||||||
|
lp->procExeLen = 0;
|
||||||
|
lp->procExeBasenameOffset = 0;
|
||||||
|
lp->mergedCommand.exeChanged = true;
|
||||||
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1121,6 +1181,15 @@ static bool LinuxProcessList_recurseProcTree(LinuxProcessList* this, const char*
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/* (Re)Generate the Command string, but only if the process is:
|
||||||
|
* - not a kernel thread, and
|
||||||
|
* - not a zombie or it became zombie under htop's watch, and
|
||||||
|
* - not a user thread or if showThreadNames is not set */
|
||||||
|
if (!Process_isKernelThread(proc) &&
|
||||||
|
(proc->state != 'Z' || lp->mergedCommand.str) &&
|
||||||
|
(!Process_isUserlandThread(proc) || !settings->showThreadNames)) {
|
||||||
|
LinuxProcess_makeCommandStr(proc);
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef HAVE_DELAYACCT
|
#ifdef HAVE_DELAYACCT
|
||||||
LinuxProcessList_readDelayAcctData(this, lp);
|
LinuxProcessList_readDelayAcctData(this, lp);
|
||||||
|
|
Loading…
Reference in New Issue