Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add grouped memory & cpu columns #1338

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
10 changes: 10 additions & 0 deletions Action.c
Original file line number Diff line number Diff line change
Expand Up @@ -219,10 +219,18 @@ static Htop_Reaction actionSortByMemory(State* st) {
return Action_setSortKey(st->host->settings, PERCENT_MEM);
}

static Htop_Reaction actionSortByMemoryGroup(State* st) {
return Action_setSortKey(st->host->settings, PERCENT_MEM_GROUP);
}

static Htop_Reaction actionSortByCPU(State* st) {
return Action_setSortKey(st->host->settings, PERCENT_CPU);
}

static Htop_Reaction actionSortByCPUGroup(State* st) {
return Action_setSortKey(st->host->settings, PERCENT_CPU_GROUP);
}

static Htop_Reaction actionSortByTime(State* st) {
return Action_setSortKey(st->host->settings, TIME);
}
Expand Down Expand Up @@ -897,9 +905,11 @@ void Action_setBindings(Htop_Action* keys) {
keys['I'] = actionInvertSortOrder;
keys['K'] = actionToggleKernelThreads;
keys['M'] = actionSortByMemory;
keys['X'] = actionSortByMemoryGroup;
keys['N'] = actionSortByPID;
keys['O'] = actionToggleRunningInContainer;
keys['P'] = actionSortByCPU;
keys['W'] = actionSortByCPUGroup;
keys['S'] = actionSetup;
keys['T'] = actionSortByTime;
keys['U'] = actionUntagAll;
Expand Down
51 changes: 51 additions & 0 deletions Process.c
Original file line number Diff line number Diff line change
Expand Up @@ -660,12 +660,14 @@ void Process_writeField(const Process* this, RichString* str, RowField field) {
xSnprintf(buffer, n, "%4ld ", this->nlwp);
break;
case PERCENT_CPU: Row_printPercentage(this->percent_cpu, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr); break;
case PERCENT_CPU_GROUP: Row_printPercentage(this->percent_cpu_group, buffer, n, Row_fieldWidths[PERCENT_CPU_GROUP], &attr); break;
case PERCENT_NORM_CPU: {
float cpuPercentage = this->percent_cpu / host->activeCPUs;
Row_printPercentage(cpuPercentage, buffer, n, Row_fieldWidths[PERCENT_CPU], &attr);
break;
}
case PERCENT_MEM: Row_printPercentage(this->percent_mem, buffer, n, 4, &attr); break;
case PERCENT_MEM_GROUP: Row_printPercentage(this->percent_mem_group, buffer, n, 4, &attr); break;
case PGRP: xSnprintf(buffer, n, "%*d ", Process_pidDigits, this->pgrp); break;
case PID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_getPid(this)); break;
case PPID: xSnprintf(buffer, n, "%*d ", Process_pidDigits, Process_getParent(this)); break;
Expand Down Expand Up @@ -915,8 +917,12 @@ int Process_compareByKey_Base(const Process* p1, const Process* p2, ProcessField
case PERCENT_CPU:
case PERCENT_NORM_CPU:
return compareRealNumbers(p1->percent_cpu, p2->percent_cpu);
case PERCENT_CPU_GROUP:
return compareRealNumbers(p1->percent_cpu_group, p2->percent_cpu_group);
case PERCENT_MEM:
return SPACESHIP_NUMBER(p1->m_resident, p2->m_resident);
case PERCENT_MEM_GROUP:
return SPACESHIP_NUMBER(p1->percent_mem_group, p2->percent_mem_group);
case COMM:
return SPACESHIP_NULLSTR(Process_getCommand(p1), Process_getCommand(p2));
case PROC_COMM: {
Expand Down Expand Up @@ -1073,6 +1079,51 @@ void Process_updateCPUFieldWidths(float percentage) {
Row_updateFieldWidth(PERCENT_NORM_CPU, width);
}

void Process_updateCPUGroupFieldWidth(float percentage) {
if (percentage < 99.9F) {
Row_updateFieldWidth(PERCENT_CPU_GROUP, 4);
return;
}

// Add additional two characters, one for "." and another for precision.
uint8_t width = ceil(log10(percentage + 0.1)) + 2;

Row_updateFieldWidth(PERCENT_CPU_GROUP, width);
}

void Process_resetGroupFields(pid_t _pid, Process* this, void* _data) {
this->percent_cpu_group = this->percent_cpu;
this->percent_mem_group = this->percent_mem;
}

void Process_updateGroupFields(pid_t _pid, Process* this, void* _data) {
const Machine* host = this->super.host;
const ProcessTable* pt = (const ProcessTable*) host->processTable;
Process* curProcess = this;

bool looping = curProcess->percent_cpu_group > 0 || curProcess->percent_mem_group > 0;

while (looping) {
const pid_t pid = Process_getPid(curProcess);
const pid_t parentPid = Process_getGroupOrParent(curProcess);
if (parentPid == pid) {
looping = false;

break;
}
Comment on lines +1109 to +1113
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to set logging=false; if it's not used beyond this loop anyway.

Also, have a look at Brent's algorithm for loop detection.

Process* parentProc = Hashtable_get(pt->super.table, parentPid);
if (parentProc == NULL) {
// TODO: or assert ?
looping = false;
break;
}
Comment on lines +1115 to +1119
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
if (parentProc == NULL) {
// TODO: or assert ?
looping = false;
break;
}
if (!parentProc) {
break;
}

An assert may trigger accidently here, if the parent of a process is not captured in the process scan (e.g. just started)

parentProc->percent_cpu_group += this->percent_cpu;
parentProc->percent_mem_group += this->percent_mem;
curProcess = parentProc;
}
Process_updateCPUGroupFieldWidth(curProcess->percent_cpu_group);
}

const ProcessClass Process_class = {
.super = {
.super = {
Expand Down
10 changes: 10 additions & 0 deletions Process.h
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,15 @@ typedef struct Process_ {
/* CPU usage during last cycle (in percent) */
float percent_cpu;

/* Sum of percent_mem for self and children */
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
/* Sum of percent_mem for self and children */
/* Sum of percent_cpu for self and children */

float percent_cpu_group;

/* Memory usage during last cycle (in percent) */
float percent_mem;

/* Sum of percent_mem for self and children */
float percent_mem_group;

/* Scheduling priority */
long int priority;

Expand Down Expand Up @@ -324,4 +330,8 @@ void Process_writeCommand(const Process* this, int attr, int baseAttr, RichStrin

void Process_updateCPUFieldWidths(float percentage);

void Process_resetGroupFields(pid_t _pid, Process* this, void* _data);

void Process_updateGroupFields(pid_t pid, Process* this, void* _data);

#endif
6 changes: 6 additions & 0 deletions ProcessTable.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,10 +55,16 @@ static void ProcessTable_prepareEntries(Table* super) {
Table_prepareEntries(super);
}

static void ProcessTable_aggregate(ProcessTable* this) {
Hashtable_foreach(this->super.table, Process_resetGroupFields, NULL);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Try to include this inside the call to ProcessTable_goThroughEntries, as this avoids one iteration over the hash map (which is not the fastest).

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This particular feedback might take time.

Specifically, I don't want to write anything in platform-specific files as the idea or the implementation isn't platform-specific at all. But I also agree with you on the unnecessary performance cost. In this dilemma, I want to explore my options and find something better, which may take time, maybe in weeks.

The rest of the feedback, including using PROCESS_FLAG_* like flags, is already done.

Hashtable_foreach(this->super.table, Process_updateGroupFields, NULL);
}

static void ProcessTable_iterateEntries(Table* super) {
ProcessTable* this = (ProcessTable*) super;
// calling into platform-specific code
ProcessTable_goThroughEntries(this);
ProcessTable_aggregate(this);
}

static void ProcessTable_cleanupEntries(Table* super) {
Expand Down
2 changes: 1 addition & 1 deletion Row.c
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ static const char* alignedTitleProcessField(ProcessField field, char* titleBuffe
}

if (Process_fields[field].autoWidth) {
if (field == PERCENT_CPU)
if (field == PERCENT_CPU || field == PERCENT_CPU_GROUP)
xSnprintf(titleBuffer, titleBufferSize, "%*s ", Row_fieldWidths[field], title);
else
xSnprintf(titleBuffer, titleBufferSize, "%-*.*s ", Row_fieldWidths[field], Row_fieldWidths[field], title);
Expand Down
2 changes: 2 additions & 0 deletions RowField.h
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ typedef enum ReservedFields_ {

/* Platform specific fields, defined in ${platform}/ProcessField.h */
PLATFORM_PROCESS_FIELDS
PERCENT_CPU_GROUP = 130,
PERCENT_MEM_GROUP = 131,
Comment on lines +47 to +48
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Global fields should be defined in the enum definition before platform-specific ones.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The only concern was not to affect existing enum values. Now, I've chosen to make these 127, 128 and had to do AUTOGROUP_ID = 129 , AUTOGROUP_NICE = 130 , CCGROUP = 131

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't see any place in htop codebase mentioning that global fields should be before platform specific ones. Personally I think htop has no specific rules for assigning field IDs and the current allocation is messy. Ideally the fields IDs should be stable across htop releases so the configuration files can refer the fields by numbers. In addition, we should reserve ranges of field IDs for specific purposes so that, e.g. dynamic fields would not use them causing collisions.

I would cite MediaWiki namespace IDs as a good example of how IDs can be allocated are how IDs are reserved for future uses.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not a matter of numeric values; just a code ordering. The macro including the platform flags should be the last line in that definition.

Regarding actual allocations: Old versions of htop used to write the field IDs into the config files. With more recent versions this has been changed to use name representations instead, thus the only thing that needs to be kept for BC is with the columns available with old htop versions that still stored the numeric IDs.

But yes, the current assignment is kinda messy, but nothing too easy to fix over night.


/* Do not add new fields after this entry (dynamic entries follow) */
LAST_RESERVED_FIELD
Expand Down
4 changes: 3 additions & 1 deletion darwin/DarwinProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, },
[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, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_CPU_GROUP] = { .name = "PERCENT_CPU_GROUP", .title = " GCPU%", .description = "Percentage of the CPU time the process and its children used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process and its children are using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why was this description updated?

[PERCENT_MEM_GROUP] = { .name = "PERCENT_MEM_GROUP", .title = "GMEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[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, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
Expand Down
2 changes: 2 additions & 0 deletions dragonflybsd/DragonFlyBSDProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, },
[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, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_CPU_GROUP] = { .name = "PERCENT_CPU_GROUP", .title = " GCPU%", .description = "Percentage of the CPU time the process and its children used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[PERCENT_MEM_GROUP] = { .name = "PERCENT_MEM_GROUP", .title = "GMEM% ", .description = "Percentage of the memory the process and its children are using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[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, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, },
Expand Down
2 changes: 2 additions & 0 deletions freebsd/FreeBSDProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_RESIDENT] = { .name = "M_RESIDENT", .title = " RES ", .description = "Resident set size, size of the text and data sections, plus stack usage", .flags = 0, .defaultSortDesc = true, },
[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, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_CPU_GROUP] = { .name = "PERCENT_CPU_GROUP", .title = " GCPU%", .description = "Percentage of the CPU time the process and its children used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[PERCENT_MEM_GROUP] = { .name = "PERCENT_MEM_GROUP", .title = "GMEM% ", .description = "Percentage of the memory the process and its children are using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[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, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, .defaultSortDesc = true, },
Expand Down
9 changes: 9 additions & 0 deletions htop.1.in
Original file line number Diff line number Diff line change
Expand Up @@ -440,6 +440,12 @@ This is the default way to represent CPU usage in Linux. Each process can
consume up to 100% which means the full capacity of the core it is running
on. This is sometimes called "Irix mode" e.g. in
.BR top (1).
.TP.B PERCENT_CPU_GROUP (GCPU%)
The percentage of the CPU time that the process and its children are currently using.
This is the default way to represent CPU usage in Linux. Each process can
consume up to 100% which means the full capacity of the core it is running
on. This is sometimes called "Irix mode" e.g. in
.BR top (1).
.TP
.B PERCENT_NORM_CPU (NCPU%)
The percentage of the CPU time that the process is currently using normalized
Expand All @@ -449,6 +455,9 @@ by CPU count. This is sometimes called "Solaris mode" e.g. in
.B PERCENT_MEM (MEM%)
The percentage of memory the process is currently using (based on the process's
resident memory size, see M_RESIDENT above).
.TP.B PERCENT_MEM_GROUP (GMEM%)
The percentage of memory the process and its children are currently using (based on the process's
resident memory size, see M_RESIDENT above).
.TP
.B USER
The username of the process owner, or the user ID if the name can't be
Expand Down
2 changes: 2 additions & 0 deletions linux/LinuxProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,10 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
[M_LRS] = { .name = "M_LRS", .title = " LIB ", .description = "The library size of the process (calculated from memory maps)", .flags = PROCESS_FLAG_LINUX_LRS_FIX, .defaultSortDesc = true, },
[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, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_CPU_GROUP] = { .name = "PERCENT_CPU_GROUP", .title = " GCPU%", .description = "Percentage of the CPU time the process and its children used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[PERCENT_MEM_GROUP] = { .name = "PERCENT_MEM_GROUP", .title = "GMEM% ", .description = "Percentage of the memory the process and its children are using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[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, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, .defaultSortDesc = true, },
Expand Down
15 changes: 15 additions & 0 deletions netbsd/NetBSDProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,14 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
.defaultSortDesc = true,
.autoWidth = true,
},
[PERCENT_CPU_GROUP] = {
.name = "PERCENT_CPU_GROUP",
.title = " GCPU%",
.description = "Percentage of the CPU time the process and its children used in the last sampling",
.flags = 0,
.defaultSortDesc = true,
.autoWidth = true,
},
[PERCENT_NORM_CPU] = {
.name = "PERCENT_NORM_CPU",
.title = "NCPU%",
Expand All @@ -165,6 +173,13 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
.flags = 0,
.defaultSortDesc = true,
},
[PERCENT_MEM_GROUP] = {
.name = "PERCENT_MEM_GROUP",
.title = "GMEM% ",
.description = "Percentage of the memory the process and its children are using, based on resident memory size",
.flags = 0,
.defaultSortDesc = true,
},
[USER] = {
.name = "USER",
.title = "USER ",
Expand Down
15 changes: 15 additions & 0 deletions openbsd/OpenBSDProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,14 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
.defaultSortDesc = true,
.autoWidth = true,
},
[PERCENT_CPU_GROUP] = {
.name = "PERCENT_CPU_GROUP",
.title = " GCPU%",
.description = "Percentage of the CPU time the process and its children used in the last sampling",
.flags = 0,
.defaultSortDesc = true,
.autoWidth = true,
},
[PERCENT_NORM_CPU] = {
.name = "PERCENT_NORM_CPU",
.title = "NCPU%",
Expand All @@ -156,6 +164,13 @@ const ProcessFieldData Process_fields[LAST_PROCESSFIELD] = {
.defaultSortDesc = true,
.autoWidth = true,
},
[PERCENT_MEM_GROUP] = {
.name = "PERCENT_MEM_GROUP",
.title = "GMEM% ",
.description = "Percentage of the memory the process and its children are using, based on resident memory size",
.flags = 0,
.defaultSortDesc = true,
},
[PERCENT_MEM] = {
.name = "PERCENT_MEM",
.title = "MEM% ",
Expand Down
2 changes: 2 additions & 0 deletions pcp/PCPProcess.c
Original file line number Diff line number Diff line change
Expand Up @@ -55,8 +55,10 @@ const ProcessFieldData Process_fields[] = {
[M_DT] = { .name = "M_DT", .title = " DIRTY ", .description = "Size of the dirty pages of the process (unused since Linux 2.6; always 0)", .flags = 0, .defaultSortDesc = true, },
[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, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_CPU_GROUP] = { .name = "PERCENT_CPU_GROUP", .title = " GCPU%", .description = "Percentage of the CPU time the process and its children used in the last sampling", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_NORM_CPU] = { .name = "PERCENT_NORM_CPU", .title = "NCPU%", .description = "Normalized percentage of the CPU time the process used in the last sampling (normalized by cpu count)", .flags = 0, .defaultSortDesc = true, .autoWidth = true, },
[PERCENT_MEM] = { .name = "PERCENT_MEM", .title = "MEM% ", .description = "Percentage of the memory the process is using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[PERCENT_MEM_GROUP] = { .name = "PERCENT_MEM_GROUP", .title = "GMEM% ", .description = "Percentage of the memory the process and its children are using, based on resident memory size", .flags = 0, .defaultSortDesc = true, },
[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, .defaultSortDesc = true, },
[NLWP] = { .name = "NLWP", .title = "NLWP ", .description = "Number of threads in the process", .flags = 0, .defaultSortDesc = true, },
Expand Down