mirror of
https://github.com/xzeldon/htop.git
synced 2024-12-23 22:55:46 +00:00
Remove arbitrary limit from rich strings
Fix subtree hiding Fix reading of CPU values in hidden threads Fix hiding of zombie processes as kernel threads Remove "debug proc" code Code cleanup in processElements
This commit is contained in:
parent
25551d44c1
commit
d8e1480a27
@ -75,7 +75,7 @@ static void CPUMeter_setValues(Meter* this, char* buffer, int size) {
|
||||
static void CPUMeter_display(Object* cast, RichString* out) {
|
||||
char buffer[50];
|
||||
Meter* this = (Meter*)cast;
|
||||
RichString_init(out);
|
||||
RichString_prune(out);
|
||||
if (this->param > this->pl->cpuCount) {
|
||||
RichString_append(out, CRT_colors[METER_TEXT], "absent");
|
||||
return;
|
||||
|
@ -36,9 +36,8 @@ static void LoadAverageMeter_setValues(Meter* this, char* buffer, int size) {
|
||||
static void LoadAverageMeter_display(Object* cast, RichString* out) {
|
||||
Meter* this = (Meter*)cast;
|
||||
char buffer[20];
|
||||
RichString_init(out);
|
||||
sprintf(buffer, "%.2f ", this->values[2]);
|
||||
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
|
||||
RichString_write(out, CRT_colors[LOAD_AVERAGE_FIFTEEN], buffer);
|
||||
sprintf(buffer, "%.2f ", this->values[1]);
|
||||
RichString_append(out, CRT_colors[LOAD_AVERAGE_FIVE], buffer);
|
||||
sprintf(buffer, "%.2f ", this->values[0]);
|
||||
@ -57,9 +56,8 @@ static void LoadMeter_setValues(Meter* this, char* buffer, int size) {
|
||||
static void LoadMeter_display(Object* cast, RichString* out) {
|
||||
Meter* this = (Meter*)cast;
|
||||
char buffer[20];
|
||||
RichString_init(out);
|
||||
sprintf(buffer, "%.2f ", ((Meter*)this)->values[0]);
|
||||
RichString_append(out, CRT_colors[LOAD], buffer);
|
||||
RichString_write(out, CRT_colors[LOAD], buffer);
|
||||
}
|
||||
|
||||
MeterType LoadAverageMeter = {
|
||||
|
@ -43,8 +43,7 @@ static void MemoryMeter_display(Object* cast, RichString* out) {
|
||||
long int usedMem = this->values[0] / k;
|
||||
long int buffersMem = this->values[1] / k;
|
||||
long int cachedMem = this->values[2] / k;
|
||||
RichString_init(out);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], ":");
|
||||
RichString_write(out, CRT_colors[METER_TEXT], ":");
|
||||
sprintf(buffer, format, totalMem);
|
||||
RichString_append(out, CRT_colors[METER_VALUE], buffer);
|
||||
sprintf(buffer, format, usedMem);
|
||||
|
26
Meter.c
26
Meter.c
@ -128,8 +128,6 @@ MeterType* Meter_types[] = {
|
||||
NULL
|
||||
};
|
||||
|
||||
static RichString Meter_stringBuffer;
|
||||
|
||||
Meter* Meter_new(ProcessList* pl, int param, MeterType* type) {
|
||||
Meter* this = calloc(sizeof(Meter), 1);
|
||||
Object_setClass(this, METER_CLASS);
|
||||
@ -166,14 +164,13 @@ void Meter_setCaption(Meter* this, const char* caption) {
|
||||
this->caption = strdup(caption);
|
||||
}
|
||||
|
||||
static inline void Meter_displayToStringBuffer(Meter* this, char* buffer) {
|
||||
static inline void Meter_displayBuffer(Meter* this, char* buffer, RichString* out) {
|
||||
MeterType* type = this->type;
|
||||
Object_Display display = ((Object*)this)->display;
|
||||
if (display) {
|
||||
display((Object*)this, &Meter_stringBuffer);
|
||||
display((Object*)this, out);
|
||||
} else {
|
||||
RichString_initVal(Meter_stringBuffer);
|
||||
RichString_append(&Meter_stringBuffer, CRT_colors[type->attributes[0]], buffer);
|
||||
RichString_write(out, CRT_colors[type->attributes[0]], buffer);
|
||||
}
|
||||
}
|
||||
|
||||
@ -229,10 +226,12 @@ static void TextMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
int captionLen = strlen(this->caption);
|
||||
w -= captionLen;
|
||||
x += captionLen;
|
||||
Meter_displayToStringBuffer(this, buffer);
|
||||
mvhline(y, x, ' ', CRT_colors[DEFAULT_COLOR]);
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
RichString_printVal(Meter_stringBuffer, y, x);
|
||||
RichString_begin(out);
|
||||
Meter_displayBuffer(this, buffer, &out);
|
||||
RichString_printVal(out, y, x);
|
||||
RichString_end(out);
|
||||
}
|
||||
|
||||
/* ---------- BarMeterMode ---------- */
|
||||
@ -378,14 +377,16 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
MeterType* type = this->type;
|
||||
char buffer[METER_BUFFER_LEN];
|
||||
type->setValues(this, buffer, METER_BUFFER_LEN - 1);
|
||||
|
||||
Meter_displayToStringBuffer(this, buffer);
|
||||
|
||||
RichString_begin(out);
|
||||
Meter_displayBuffer(this, buffer, &out);
|
||||
|
||||
attrset(CRT_colors[LED_COLOR]);
|
||||
mvaddstr(y+2, x, this->caption);
|
||||
int xx = x + strlen(this->caption);
|
||||
for (int i = 0; i < Meter_stringBuffer.len; i++) {
|
||||
char c = RichString_getCharVal(Meter_stringBuffer, i);
|
||||
int len = RichString_sizeVal(out);
|
||||
for (int i = 0; i < len; i++) {
|
||||
char c = RichString_getCharVal(out, i);
|
||||
if (c >= '0' && c <= '9') {
|
||||
LEDMeterMode_drawDigit(xx, y, c-48);
|
||||
xx += 4;
|
||||
@ -395,6 +396,7 @@ static void LEDMeterMode_draw(Meter* this, int x, int y, int w) {
|
||||
}
|
||||
}
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
RichString_end(out);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
62
Panel.c
62
Panel.c
@ -98,7 +98,7 @@ void Panel_init(Panel* this, int x, int y, int w, int h, char* type, bool owner)
|
||||
this->selected = 0;
|
||||
this->oldSelected = 0;
|
||||
this->needsRedraw = true;
|
||||
RichString_prune(&(this->header));
|
||||
RichString_beginAllocated(this->header);
|
||||
if (String_eq(CRT_termType, "linux"))
|
||||
this->scrollHAmount = 20;
|
||||
else
|
||||
@ -108,17 +108,19 @@ void Panel_init(Panel* this, int x, int y, int w, int h, char* type, bool owner)
|
||||
void Panel_done(Panel* this) {
|
||||
assert (this != NULL);
|
||||
Vector_delete(this->items);
|
||||
RichString_end(this->header);
|
||||
}
|
||||
|
||||
inline void Panel_setRichHeader(Panel* this, RichString header) {
|
||||
RichString* Panel_getHeader(Panel* this) {
|
||||
assert (this != NULL);
|
||||
|
||||
this->header = header;
|
||||
this->needsRedraw = true;
|
||||
return &(this->header);
|
||||
}
|
||||
|
||||
inline void Panel_setHeader(Panel* this, const char* header) {
|
||||
Panel_setRichHeader(this, RichString_quickString(CRT_colors[PANEL_HEADER_FOCUS], header));
|
||||
RichString_write(&(this->header), CRT_colors[PANEL_HEADER_FOCUS], header);
|
||||
this->needsRedraw = true;
|
||||
}
|
||||
|
||||
void Panel_setEventHandler(Panel* this, Panel_EventHandler eh) {
|
||||
@ -136,7 +138,7 @@ void Panel_move(Panel* this, int x, int y) {
|
||||
void Panel_resize(Panel* this, int w, int h) {
|
||||
assert (this != NULL);
|
||||
|
||||
if (this->header.len > 0)
|
||||
if (RichString_sizeVal(this->header) > 0)
|
||||
h--;
|
||||
this->w = w;
|
||||
this->h = h;
|
||||
@ -262,15 +264,16 @@ void Panel_draw(Panel* this, bool focus) {
|
||||
assert(first >= 0);
|
||||
assert(last <= itemCount);
|
||||
|
||||
if (this->header.len > 0) {
|
||||
int headerLen = RichString_sizeVal(this->header);
|
||||
if (headerLen > 0) {
|
||||
int attr = focus
|
||||
? CRT_colors[PANEL_HEADER_FOCUS]
|
||||
: CRT_colors[PANEL_HEADER_UNFOCUS];
|
||||
attrset(attr);
|
||||
mvhline(y, x, ' ', this->w);
|
||||
if (scrollH < this->header.len) {
|
||||
if (scrollH < headerLen) {
|
||||
RichString_printoffnVal(this->header, y, x, scrollH,
|
||||
MIN(this->header.len - scrollH, this->w));
|
||||
MIN(headerLen - scrollH, this->w));
|
||||
}
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
y++;
|
||||
@ -284,22 +287,23 @@ void Panel_draw(Panel* this, bool focus) {
|
||||
|
||||
for(int i = first, j = 0; j < this->h && i < last; i++, j++) {
|
||||
Object* itemObj = Vector_get(this->items, i);
|
||||
RichString itemRef;
|
||||
RichString_initVal(itemRef);
|
||||
itemObj->display(itemObj, &itemRef);
|
||||
int amt = MIN(itemRef.len - scrollH, this->w);
|
||||
RichString_begin(item);
|
||||
itemObj->display(itemObj, &item);
|
||||
int itemLen = RichString_sizeVal(item);
|
||||
int amt = MIN(itemLen - scrollH, this->w);
|
||||
if (i == this->selected) {
|
||||
attrset(highlight);
|
||||
RichString_setAttr(&itemRef, highlight);
|
||||
RichString_setAttr(&item, highlight);
|
||||
mvhline(y + j, x+0, ' ', this->w);
|
||||
if (amt > 0)
|
||||
RichString_printoffnVal(itemRef, y+j, x+0, scrollH, amt);
|
||||
RichString_printoffnVal(item, y+j, x+0, scrollH, amt);
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
} else {
|
||||
mvhline(y+j, x+0, ' ', this->w);
|
||||
if (amt > 0)
|
||||
RichString_printoffnVal(itemRef, y+j, x+0, scrollH, amt);
|
||||
RichString_printoffnVal(item, y+j, x+0, scrollH, amt);
|
||||
}
|
||||
RichString_end(item);
|
||||
}
|
||||
for (int i = y + (last - first); i < y + this->h; i++)
|
||||
mvhline(i, x+0, ' ', this->w);
|
||||
@ -307,24 +311,26 @@ void Panel_draw(Panel* this, bool focus) {
|
||||
|
||||
} else {
|
||||
Object* oldObj = Vector_get(this->items, this->oldSelected);
|
||||
RichString oldRef;
|
||||
RichString_initVal(oldRef);
|
||||
oldObj->display(oldObj, &oldRef);
|
||||
RichString_begin(old);
|
||||
oldObj->display(oldObj, &old);
|
||||
int oldLen = RichString_sizeVal(old);
|
||||
Object* newObj = Vector_get(this->items, this->selected);
|
||||
RichString newRef;
|
||||
RichString_initVal(newRef);
|
||||
newObj->display(newObj, &newRef);
|
||||
RichString_begin(new);
|
||||
newObj->display(newObj, &new);
|
||||
int newLen = RichString_sizeVal(new);
|
||||
mvhline(y+ this->oldSelected - this->scrollV, x+0, ' ', this->w);
|
||||
if (scrollH < oldRef.len)
|
||||
RichString_printoffnVal(oldRef, y+this->oldSelected - this->scrollV, x,
|
||||
this->scrollH, MIN(oldRef.len - scrollH, this->w));
|
||||
if (scrollH < oldLen)
|
||||
RichString_printoffnVal(old, y+this->oldSelected - this->scrollV, x,
|
||||
this->scrollH, MIN(oldLen - scrollH, this->w));
|
||||
attrset(highlight);
|
||||
mvhline(y+this->selected - this->scrollV, x+0, ' ', this->w);
|
||||
RichString_setAttr(&newRef, highlight);
|
||||
if (scrollH < newRef.len)
|
||||
RichString_printoffnVal(newRef, y+this->selected - this->scrollV, x,
|
||||
this->scrollH, MIN(newRef.len - scrollH, this->w));
|
||||
RichString_setAttr(&new, highlight);
|
||||
if (scrollH < newLen)
|
||||
RichString_printoffnVal(new, y+this->selected - this->scrollV, x,
|
||||
this->scrollH, MIN(newLen - scrollH, this->w));
|
||||
attrset(CRT_colors[RESET_COLOR]);
|
||||
RichString_end(new);
|
||||
RichString_end(old);
|
||||
}
|
||||
this->oldSelected = this->selected;
|
||||
move(0, 0);
|
||||
|
2
Panel.h
2
Panel.h
@ -78,7 +78,7 @@ void Panel_init(Panel* this, int x, int y, int w, int h, char* type, bool owner)
|
||||
|
||||
void Panel_done(Panel* this);
|
||||
|
||||
extern void Panel_setRichHeader(Panel* this, RichString header);
|
||||
RichString* Panel_getHeader(Panel* this);
|
||||
|
||||
extern void Panel_setHeader(Panel* this, const char* header);
|
||||
|
||||
|
26
Process.c
26
Process.c
@ -40,12 +40,18 @@ in the source distribution for its full text.
|
||||
#endif
|
||||
#define PAGE_SIZE_KB ( PAGE_SIZE / ONE_K )
|
||||
|
||||
#define PROCESS_COMM_LEN 300
|
||||
|
||||
/*{
|
||||
|
||||
#ifndef Process_isKernelThread
|
||||
#define Process_isKernelThread(_process) (_process->pgrp == 0)
|
||||
#endif
|
||||
|
||||
#ifndef Process_isUserlandThread
|
||||
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
|
||||
#endif
|
||||
|
||||
#ifndef Process_isThread
|
||||
#define Process_isThread(_process) (_process->pid != _process->tgid || _process->m_size == 0)
|
||||
#define Process_isThread(_process) (Process_isUserlandThread(_process) || Process_isKernelThread(_process))
|
||||
#endif
|
||||
|
||||
typedef enum ProcessField_ {
|
||||
@ -83,6 +89,7 @@ typedef struct Process_ {
|
||||
char state;
|
||||
bool tag;
|
||||
bool showChildren;
|
||||
bool show;
|
||||
pid_t ppid;
|
||||
unsigned int pgrp;
|
||||
unsigned int session;
|
||||
@ -225,7 +232,7 @@ static int Process_getuid = -1;
|
||||
#define ONE_M (ONE_K * ONE_K)
|
||||
#define ONE_G (ONE_M * ONE_K)
|
||||
|
||||
static void Process_printLargeNumber(Process* this, RichString *str, unsigned long number) {
|
||||
static void Process_printLargeNumber(Process* this, RichString* str, unsigned long number) {
|
||||
char buffer[11];
|
||||
int len;
|
||||
if(number >= (10 * ONE_M)) {
|
||||
@ -279,10 +286,10 @@ static void Process_printTime(RichString* str, unsigned long t) {
|
||||
}
|
||||
|
||||
static inline void Process_writeCommand(Process* this, int attr, int baseattr, RichString* str) {
|
||||
int start = str->len;
|
||||
int start = RichString_size(str);
|
||||
RichString_append(str, attr, this->comm);
|
||||
if (this->pl->highlightBaseName) {
|
||||
int finish = str->len - 1;
|
||||
int finish = RichString_size(str) - 1;
|
||||
int space = RichString_findChar(str, ' ', start);
|
||||
if (space != -1)
|
||||
finish = space - 1;
|
||||
@ -312,10 +319,10 @@ static inline void Process_outputRate(Process* this, RichString* str, int attr,
|
||||
}
|
||||
|
||||
static void Process_writeField(Process* this, RichString* str, ProcessField field) {
|
||||
char buffer[PROCESS_COMM_LEN];
|
||||
char buffer[128]; buffer[127] = '\0';
|
||||
int attr = CRT_colors[DEFAULT_COLOR];
|
||||
int baseattr = CRT_colors[PROCESS_BASENAME];
|
||||
int n = PROCESS_COMM_LEN;
|
||||
int n = sizeof(buffer) - 1;
|
||||
|
||||
switch (field) {
|
||||
case PID: snprintf(buffer, n, "%5u ", this->pid); break;
|
||||
@ -457,7 +464,7 @@ static void Process_writeField(Process* this, RichString* str, ProcessField fiel
|
||||
static void Process_display(Object* cast, RichString* out) {
|
||||
Process* this = (Process*) cast;
|
||||
ProcessField* fields = this->pl->fields;
|
||||
RichString_init(out);
|
||||
RichString_prune(out);
|
||||
for (int i = 0; fields[i]; i++)
|
||||
Process_writeField(this, out, fields[i]);
|
||||
if (this->pl->shadowOtherUsers && (int)this->st_uid != Process_getuid)
|
||||
@ -486,6 +493,7 @@ Process* Process_new(struct ProcessList_ *pl) {
|
||||
this->pl = pl;
|
||||
this->tag = false;
|
||||
this->showChildren = true;
|
||||
this->show = true;
|
||||
this->updated = false;
|
||||
this->utime = 0;
|
||||
this->stime = 0;
|
||||
|
11
Process.h
11
Process.h
@ -43,11 +43,17 @@ in the source distribution for its full text.
|
||||
#endif
|
||||
#define PAGE_SIZE_KB ( PAGE_SIZE / ONE_K )
|
||||
|
||||
#define PROCESS_COMM_LEN 300
|
||||
|
||||
#ifndef Process_isKernelThread
|
||||
#define Process_isKernelThread(_process) (_process->pgrp == 0)
|
||||
#endif
|
||||
|
||||
#ifndef Process_isUserlandThread
|
||||
#define Process_isUserlandThread(_process) (_process->pid != _process->tgid)
|
||||
#endif
|
||||
|
||||
#ifndef Process_isThread
|
||||
#define Process_isThread(_process) (_process->pid != _process->tgid || _process->m_size == 0)
|
||||
#define Process_isThread(_process) (Process_isUserlandThread(_process) || Process_isKernelThread(_process))
|
||||
#endif
|
||||
|
||||
typedef enum ProcessField_ {
|
||||
@ -85,6 +91,7 @@ typedef struct Process_ {
|
||||
char state;
|
||||
bool tag;
|
||||
bool showChildren;
|
||||
bool show;
|
||||
pid_t ppid;
|
||||
unsigned int pgrp;
|
||||
unsigned int session;
|
||||
|
757
ProcessList.c
757
ProcessList.c
@ -58,10 +58,6 @@ in the source distribution for its full text.
|
||||
|
||||
/*{
|
||||
|
||||
#ifdef DEBUG_PROC
|
||||
typedef int(*vxscanf)(void*, const char*, va_list);
|
||||
#endif
|
||||
|
||||
typedef struct CPUData_ {
|
||||
unsigned long long int totalTime;
|
||||
unsigned long long int userTime;
|
||||
@ -126,77 +122,12 @@ typedef struct ProcessList_ {
|
||||
bool highlightMegabytes;
|
||||
bool highlightThreads;
|
||||
bool detailedCPUTime;
|
||||
#ifdef DEBUG_PROC
|
||||
FILE* traceFile;
|
||||
#endif
|
||||
|
||||
} ProcessList;
|
||||
}*/
|
||||
|
||||
static ProcessField defaultHeaders[] = { PID, USER, PRIORITY, NICE, M_SIZE, M_RESIDENT, M_SHARE, STATE, PERCENT_CPU, PERCENT_MEM, TIME, COMM, 0 };
|
||||
|
||||
#ifdef DEBUG_PROC
|
||||
|
||||
#define ProcessList_read(this, buffer, format, ...) ProcessList_xread(this, (vxscanf) vsscanf, buffer, format, ## __VA_ARGS__ )
|
||||
#define ProcessList_fread(this, file, format, ...) ProcessList_xread(this, (vxscanf) vfscanf, file, format, ## __VA_ARGS__ )
|
||||
|
||||
static FILE* ProcessList_fopen(ProcessList* this, const char* path, const char* mode) {
|
||||
fprintf(this->traceFile, "[%s]\n", path);
|
||||
return fopen(path, mode);
|
||||
}
|
||||
|
||||
static inline int ProcessList_xread(ProcessList* this, vxscanf fn, void* buffer, char* format, ...) {
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
int num = fn(buffer, format, ap);
|
||||
va_end(format);
|
||||
va_start(ap, format);
|
||||
while (*format) {
|
||||
char ch = *format;
|
||||
char* c; int* d;
|
||||
long int* ld; unsigned long int* lu;
|
||||
long long int* lld; unsigned long long int* llu;
|
||||
char** s;
|
||||
if (ch != '%') {
|
||||
fprintf(this->traceFile, "%c", ch);
|
||||
format++;
|
||||
continue;
|
||||
}
|
||||
format++;
|
||||
switch(*format) {
|
||||
case 'c': c = va_arg(ap, char*); fprintf(this->traceFile, "%c", *c); break;
|
||||
case 'd': d = va_arg(ap, int*); fprintf(this->traceFile, "%d", *d); break;
|
||||
case 's': s = va_arg(ap, char**); fprintf(this->traceFile, "%s", *s); break;
|
||||
case 'l':
|
||||
format++;
|
||||
switch (*format) {
|
||||
case 'd': ld = va_arg(ap, long int*); fprintf(this->traceFile, "%ld", *ld); break;
|
||||
case 'u': lu = va_arg(ap, unsigned long int*); fprintf(this->traceFile, "%lu", *lu); break;
|
||||
case 'l':
|
||||
format++;
|
||||
switch (*format) {
|
||||
case 'd': lld = va_arg(ap, long long int*); fprintf(this->traceFile, "%lld", *lld); break;
|
||||
case 'u': llu = va_arg(ap, unsigned long long int*); fprintf(this->traceFile, "%llu", *llu); break;
|
||||
}
|
||||
}
|
||||
}
|
||||
format++;
|
||||
}
|
||||
fprintf(this->traceFile, "\n");
|
||||
va_end(format);
|
||||
return num;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
#ifndef ProcessList_read
|
||||
#define ProcessList_fopen(this, path, mode) fopen(path, mode)
|
||||
#define ProcessList_read(this, buffer, format, ...) sscanf(buffer, format, ## __VA_ARGS__ )
|
||||
#define ProcessList_fread(this, file, format, ...) fscanf(file, format, ## __VA_ARGS__ )
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
ProcessList* ProcessList_new(UsersTable* usersTable) {
|
||||
ProcessList* this;
|
||||
this = malloc(sizeof(ProcessList));
|
||||
@ -208,19 +139,15 @@ ProcessList* ProcessList_new(UsersTable* usersTable) {
|
||||
/* tree-view auxiliary buffers */
|
||||
this->processes2 = Vector_new(PROCESS_CLASS, true, DEFAULT_SIZE, Process_compare);
|
||||
|
||||
#ifdef DEBUG_PROC
|
||||
this->traceFile = fopen("/tmp/htop-proc-trace", "w");
|
||||
#endif
|
||||
|
||||
FILE* status = fopen(PROCSTATFILE, "r");
|
||||
assert(status != NULL);
|
||||
FILE* file = fopen(PROCSTATFILE, "r");
|
||||
assert(file != NULL);
|
||||
char buffer[256];
|
||||
int cpus = -1;
|
||||
do {
|
||||
cpus++;
|
||||
fgets(buffer, 255, status);
|
||||
fgets(buffer, 255, file);
|
||||
} while (String_startsWith(buffer, "cpu"));
|
||||
fclose(status);
|
||||
fclose(file);
|
||||
this->cpuCount = cpus - 1;
|
||||
|
||||
this->cpus = calloc(sizeof(CPUData), cpus);
|
||||
@ -240,8 +167,8 @@ ProcessList* ProcessList_new(UsersTable* usersTable) {
|
||||
this->direction = 1;
|
||||
this->hideThreads = false;
|
||||
this->shadowOtherUsers = false;
|
||||
this->showThreadNames = true;
|
||||
this->showingThreadNames = true;
|
||||
this->showThreadNames = false;
|
||||
this->showingThreadNames = false;
|
||||
this->hideKernelThreads = false;
|
||||
this->hideUserlandThreads = false;
|
||||
this->treeView = false;
|
||||
@ -256,13 +183,7 @@ void ProcessList_delete(ProcessList* this) {
|
||||
Hashtable_delete(this->processTable);
|
||||
Vector_delete(this->processes);
|
||||
Vector_delete(this->processes2);
|
||||
|
||||
free(this->cpus);
|
||||
|
||||
#ifdef DEBUG_PROC
|
||||
fclose(this->traceFile);
|
||||
#endif
|
||||
|
||||
free(this->fields);
|
||||
free(this);
|
||||
}
|
||||
@ -274,18 +195,16 @@ void ProcessList_invertSortOrder(ProcessList* this) {
|
||||
this->direction = 1;
|
||||
}
|
||||
|
||||
RichString ProcessList_printHeader(ProcessList* this) {
|
||||
RichString out;
|
||||
RichString_initVal(out);
|
||||
void ProcessList_printHeader(ProcessList* this, RichString* header) {
|
||||
RichString_prune(header);
|
||||
ProcessField* fields = this->fields;
|
||||
for (int i = 0; fields[i]; i++) {
|
||||
const char* field = Process_fieldTitles[fields[i]];
|
||||
if (this->sortKey == fields[i])
|
||||
RichString_append(&out, CRT_colors[PANEL_HIGHLIGHT_FOCUS], field);
|
||||
RichString_append(header, CRT_colors[PANEL_HIGHLIGHT_FOCUS], field);
|
||||
else
|
||||
RichString_append(&out, CRT_colors[PANEL_HEADER_FOCUS], field);
|
||||
RichString_append(header, CRT_colors[PANEL_HEADER_FOCUS], field);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
static void ProcessList_add(ProcessList* this, Process* p) {
|
||||
@ -334,21 +253,18 @@ static void ProcessList_buildTree(ProcessList* this, pid_t pid, int level, int i
|
||||
int size = Vector_size(children);
|
||||
for (int i = 0; i < size; i++) {
|
||||
Process* process = (Process*) (Vector_get(children, i));
|
||||
if (show) {
|
||||
int s = this->processes2->items;
|
||||
if (direction == 1)
|
||||
Vector_add(this->processes2, process);
|
||||
else
|
||||
Vector_insert(this->processes2, 0, process);
|
||||
assert(this->processes2->items == s+1); (void)s;
|
||||
int nextIndent = indent;
|
||||
if (i < size - 1)
|
||||
nextIndent = indent | (1 << level);
|
||||
ProcessList_buildTree(this, process->pid, level+1, nextIndent, direction, process->showChildren);
|
||||
process->indent = indent | (1 << level);
|
||||
} else {
|
||||
ProcessList_remove(this, process);
|
||||
}
|
||||
if (!show)
|
||||
process->show = false;
|
||||
show = show ? process->showChildren : false;
|
||||
int s = this->processes2->items;
|
||||
if (direction == 1)
|
||||
Vector_add(this->processes2, process);
|
||||
else
|
||||
Vector_insert(this->processes2, 0, process);
|
||||
assert(this->processes2->items == s+1); (void)s;
|
||||
int nextIndent = indent | (1 << level);
|
||||
ProcessList_buildTree(this, process->pid, level+1, (i < size - 1) ? nextIndent : indent, direction, show);
|
||||
process->indent = nextIndent;
|
||||
}
|
||||
Vector_delete(children);
|
||||
}
|
||||
@ -393,365 +309,352 @@ void ProcessList_sort(ProcessList* this) {
|
||||
}
|
||||
}
|
||||
|
||||
static int ProcessList_readStatFile(Process *proc, FILE *f, char *command) {
|
||||
static bool ProcessList_readStatFile(Process *process, const char* dirname, const char* name, char* command) {
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
static char buf[MAX_READ];
|
||||
unsigned long int zero;
|
||||
|
||||
int size = fread(buf, 1, MAX_READ, f);
|
||||
if(!size) return 0;
|
||||
int size = fread(buf, 1, MAX_READ, file);
|
||||
if (!size) { fclose(file); return false; }
|
||||
|
||||
assert(proc->pid == atoi(buf));
|
||||
assert(process->pid == atoi(buf));
|
||||
char *location = strchr(buf, ' ');
|
||||
if(!location) return 0;
|
||||
if (!location) { fclose(file); return false; }
|
||||
|
||||
location += 2;
|
||||
char *end = strrchr(location, ')');
|
||||
if(!end) return 0;
|
||||
if (!end) { fclose(file); return false; }
|
||||
|
||||
int commsize = end - location;
|
||||
memcpy(command, location, commsize);
|
||||
command[commsize] = '\0';
|
||||
location = end + 2;
|
||||
|
||||
#ifdef DEBUG_PROC
|
||||
int num = ProcessList_read(this, location,
|
||||
"%c %u %u %u %u %d %lu %lu %lu %lu "
|
||||
"%lu %lu %lu %ld %ld %ld %ld %ld %ld "
|
||||
"%lu %lu %ld %lu %lu %lu %lu %lu "
|
||||
"%lu %lu %lu %lu %lu %lu %lu %lu "
|
||||
|
||||
int num = sscanf(location,
|
||||
"%c %d %u %u %u "
|
||||
"%d %lu "
|
||||
"%*u %*u %*u %*u "
|
||||
"%lu %lu %ld %ld "
|
||||
"%ld %ld %ld "
|
||||
"%*d %*u %*u %*d %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u %*u "
|
||||
"%d %d",
|
||||
&proc->state, &proc->ppid, &proc->pgrp, &proc->session, &proc->tty_nr,
|
||||
&proc->tpgid, &proc->flags,
|
||||
&proc->minflt, &proc->cminflt, &proc->majflt, &proc->cmajflt,
|
||||
&proc->utime, &proc->stime, &proc->cutime, &proc->cstime,
|
||||
&proc->priority, &proc->nice, &proc->nlwp, &proc->itrealvalue,
|
||||
&zero, &proc->vsize, &proc->rss, &proc->rlim,
|
||||
&proc->startcode, &proc->endcode, &proc->startstack, &proc->kstkesp,
|
||||
&proc->kstkeip, &proc->signal, &proc->blocked, &proc->sigignore,
|
||||
&proc->sigcatch, &proc->wchan, &proc->nswap, &proc->cnswap,
|
||||
&proc->exit_signal, &proc->processor);
|
||||
#else
|
||||
long int uzero;
|
||||
int num = ProcessList_read(this, location,
|
||||
"%c %d %u %u %u %d %lu %lu %lu %lu "
|
||||
"%lu %lu %lu %ld %ld %ld %ld %ld %ld "
|
||||
"%lu %lu %ld %lu %lu %lu %lu %lu "
|
||||
"%lu %lu %lu %lu %lu %lu %lu %lu "
|
||||
"%d %d",
|
||||
&proc->state, &proc->ppid, &proc->pgrp, &proc->session, &proc->tty_nr,
|
||||
&proc->tpgid, &proc->flags,
|
||||
&zero, &zero, &zero, &zero,
|
||||
&proc->utime, &proc->stime, &proc->cutime, &proc->cstime,
|
||||
&proc->priority, &proc->nice, &proc->nlwp, &uzero,
|
||||
&zero, &zero, &uzero, &zero,
|
||||
&zero, &zero, &zero, &zero,
|
||||
&zero, &zero, &zero, &zero,
|
||||
&zero, &zero, &zero, &zero,
|
||||
&proc->exit_signal, &proc->processor);
|
||||
#endif
|
||||
|
||||
// This assert is always valid on 2.4, but reportedly not always valid on 2.6.
|
||||
// TODO: Check if the semantics of this field has changed.
|
||||
// assert(zero == 0);
|
||||
|
||||
if(num != 37) return 0;
|
||||
return 1;
|
||||
&process->state, &process->ppid, &process->pgrp, &process->session, &process->tty_nr,
|
||||
&process->tpgid, &process->flags,
|
||||
&process->utime, &process->stime, &process->cutime, &process->cstime,
|
||||
&process->priority, &process->nice, &process->nlwp,
|
||||
&process->exit_signal, &process->processor);
|
||||
fclose(file);
|
||||
return (num == 16);
|
||||
}
|
||||
|
||||
static bool ProcessList_readStatusFile(Process* proc, const char* dirname, char* name) {
|
||||
char statusfilename[MAX_NAME+1];
|
||||
statusfilename[MAX_NAME] = '\0';
|
||||
static bool ProcessList_statProcessDir(Process* process, const char* dirname, char* name) {
|
||||
char filename[MAX_NAME+1];
|
||||
filename[MAX_NAME] = '\0';
|
||||
|
||||
snprintf(statusfilename, MAX_NAME, "%s/%s", dirname, name);
|
||||
snprintf(filename, MAX_NAME, "%s/%s", dirname, name);
|
||||
struct stat sstat;
|
||||
int statok = stat(statusfilename, &sstat);
|
||||
int statok = stat(filename, &sstat);
|
||||
if (statok == -1)
|
||||
return false;
|
||||
proc->st_uid = sstat.st_uid;
|
||||
process->st_uid = sstat.st_uid;
|
||||
|
||||
struct tm date;
|
||||
time_t ctime = sstat.st_ctime;
|
||||
proc->starttime_ctime = ctime;
|
||||
process->starttime_ctime = ctime;
|
||||
(void) localtime_r((time_t*) &ctime, &date);
|
||||
strftime(proc->starttime_show, 7, ((ctime > time(NULL) - 86400) ? "%R " : "%b%d "), &date);
|
||||
strftime(process->starttime_show, 7, ((ctime > time(NULL) - 86400) ? "%R " : "%b%d "), &date);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef HAVE_TASKSTATS
|
||||
|
||||
static void ProcessList_readIoFile(Process* proc, const char* dirname, char* name) {
|
||||
char iofilename[MAX_NAME+1];
|
||||
iofilename[MAX_NAME] = '\0';
|
||||
static void ProcessList_readIoFile(Process* process, const char* dirname, char* name) {
|
||||
char filename[MAX_NAME+1];
|
||||
filename[MAX_NAME] = '\0';
|
||||
|
||||
snprintf(iofilename, MAX_NAME, "%s/%s/io", dirname, name);
|
||||
FILE* io = ProcessList_fopen(this, iofilename, "r");
|
||||
if (io) {
|
||||
char buffer[256];
|
||||
buffer[255] = '\0';
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
unsigned long long now = tv.tv_sec*1000+tv.tv_usec/1000;
|
||||
unsigned long long last_read = proc->io_read_bytes;
|
||||
unsigned long long last_write = proc->io_write_bytes;
|
||||
while (fgets(buffer, 255, io)) {
|
||||
if (ProcessList_read(this, buffer, "rchar: %llu", &proc->io_rchar)) continue;
|
||||
if (ProcessList_read(this, buffer, "wchar: %llu", &proc->io_wchar)) continue;
|
||||
if (ProcessList_read(this, buffer, "syscr: %llu", &proc->io_syscr)) continue;
|
||||
if (ProcessList_read(this, buffer, "syscw: %llu", &proc->io_syscw)) continue;
|
||||
if (ProcessList_read(this, buffer, "read_bytes: %llu", &proc->io_read_bytes)) {
|
||||
proc->io_rate_read_bps =
|
||||
((double)(proc->io_read_bytes - last_read))/(((double)(now - proc->io_rate_read_time))/1000);
|
||||
proc->io_rate_read_time = now;
|
||||
continue;
|
||||
}
|
||||
if (ProcessList_read(this, buffer, "write_bytes: %llu", &proc->io_write_bytes)) {
|
||||
proc->io_rate_write_bps =
|
||||
((double)(proc->io_write_bytes - last_write))/(((double)(now - proc->io_rate_write_time))/1000);
|
||||
proc->io_rate_write_time = now;
|
||||
continue;
|
||||
}
|
||||
ProcessList_read(this, buffer, "cancelled_write_bytes: %llu", &proc->io_cancelled_write_bytes);
|
||||
snprintf(filename, MAX_NAME, "%s/%s/io", dirname, name);
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return;
|
||||
|
||||
char buffer[256];
|
||||
buffer[255] = '\0';
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv,NULL);
|
||||
unsigned long long now = tv.tv_sec*1000+tv.tv_usec/1000;
|
||||
unsigned long long last_read = process->io_read_bytes;
|
||||
unsigned long long last_write = process->io_write_bytes;
|
||||
while (fgets(buffer, 255, file)) {
|
||||
if (sscanf(buffer, "rchar: %llu", &process->io_rchar)) continue;
|
||||
if (sscanf(buffer, "wchar: %llu", &process->io_wchar)) continue;
|
||||
if (sscanf(buffer, "syscr: %llu", &process->io_syscr)) continue;
|
||||
if (sscanf(buffer, "syscw: %llu", &process->io_syscw)) continue;
|
||||
if (sscanf(buffer, "read_bytes: %llu", &process->io_read_bytes)) {
|
||||
process->io_rate_read_bps =
|
||||
((double)(process->io_read_bytes - last_read))/(((double)(now - process->io_rate_read_time))/1000);
|
||||
process->io_rate_read_time = now;
|
||||
continue;
|
||||
}
|
||||
fclose(io);
|
||||
if (sscanf(buffer, "write_bytes: %llu", &process->io_write_bytes)) {
|
||||
process->io_rate_write_bps =
|
||||
((double)(process->io_write_bytes - last_write))/(((double)(now - process->io_rate_write_time))/1000);
|
||||
process->io_rate_write_time = now;
|
||||
continue;
|
||||
}
|
||||
sscanf(buffer, "cancelled_write_bytes: %llu", &process->io_cancelled_write_bytes);
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool ProcessList_processEntries(ProcessList* this, const char* dirname, Process* parent, pid_t parentPid, float period) {
|
||||
static bool ProcessList_readStatmFile(Process* process, const char* dirname, const char* name) {
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(filename, MAX_NAME, "%s/%s/statm", dirname, name);
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
int num = fscanf(file, "%d %d %d %d %d %d %d",
|
||||
&process->m_size, &process->m_resident, &process->m_share,
|
||||
&process->m_trs, &process->m_lrs, &process->m_drs,
|
||||
&process->m_dt);
|
||||
fclose(file);
|
||||
return (num == 7);
|
||||
}
|
||||
|
||||
#ifdef HAVE_OPENVZ
|
||||
|
||||
static void ProcessList_readOpenVZData(Process* process, const char* dirname, const char* name) {
|
||||
if (access("/proc/vz", R_OK) != 0) {
|
||||
process->vpid = process->pid;
|
||||
process->ctid = 0;
|
||||
return;
|
||||
}
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(filename, MAX_NAME, "%s/%s/stat", dirname, name);
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return;
|
||||
fscanf(file,
|
||||
"%*u %*s %*c %*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %u %u",
|
||||
&process->vpid, &process->ctid);
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CGROUP
|
||||
|
||||
static void ProcessList_readCGroupFile(Process* process, const char* dirname, const char* name) {
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(filename, MAX_NAME, "%s/%s/cgroup", dirname, name);
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file) {
|
||||
process->cgroup = strdup("");
|
||||
return;
|
||||
}
|
||||
char buffer[256];
|
||||
char *ok = fgets(buffer, 255, file);
|
||||
if (ok) {
|
||||
char* trimmed = String_trim(buffer);
|
||||
char** fields = String_split(trimmed, ':');
|
||||
free(trimmed);
|
||||
|
||||
process->cgroup = strndup(fields[2] + 1, 10);
|
||||
String_freeArray(fields);
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VSERVER
|
||||
|
||||
static void ProcessList_readVServerData(Process* process, const char* dirname, const char* name) {
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(filename, MAX_NAME, "%s/%s/status", dirname, name);
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return;
|
||||
char buffer[256];
|
||||
process->vxid = 0;
|
||||
while (fgets(buffer, 255, file)) {
|
||||
if (String_startsWith(buffer, "VxID:")) {
|
||||
int vxid;
|
||||
int ok = sscanf(buffer, "VxID:\t%d", &vxid);
|
||||
if (ok >= 1) {
|
||||
process->vxid = vxid;
|
||||
}
|
||||
}
|
||||
#if defined HAVE_ANCIENT_VSERVER
|
||||
else if (String_startsWith(buffer, "s_context:")) {
|
||||
int vxid;
|
||||
int ok = sscanf(buffer, "s_context:\t%d", &vxid);
|
||||
if (ok >= 1) {
|
||||
process->vxid = vxid;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
fclose(file);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static bool ProcessList_readCmdlineFile(Process* process, const char* dirname, const char* name) {
|
||||
if (Process_isKernelThread(process))
|
||||
return true;
|
||||
char filename[MAX_NAME+1];
|
||||
snprintf(filename, MAX_NAME, "%s/%s/cmdline", dirname, name);
|
||||
FILE* file = fopen(filename, "r");
|
||||
if (!file)
|
||||
return false;
|
||||
|
||||
char command[4096+1]; // max cmdline length on Linux
|
||||
int amtRead = fread(command, 1, sizeof(command) - 1, file);
|
||||
if (amtRead > 0) {
|
||||
for (int i = 0; i < amtRead; i++)
|
||||
if (command[i] == '\0' || command[i] == '\n') {
|
||||
command[i] = ' ';
|
||||
}
|
||||
}
|
||||
command[amtRead] = '\0';
|
||||
fclose(file);
|
||||
free(process->comm);
|
||||
process->comm = String_copy(command);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
static bool ProcessList_processEntries(ProcessList* this, const char* dirname, Process* parent, float period) {
|
||||
DIR* dir;
|
||||
struct dirent* entry;
|
||||
|
||||
dir = opendir(dirname);
|
||||
if (!dir) return false;
|
||||
int cpus = this->cpuCount;
|
||||
bool showUserlandThreads = !this->hideUserlandThreads;
|
||||
bool hideKernelThreads = this->hideKernelThreads;
|
||||
bool hideUserlandThreads = this->hideUserlandThreads;
|
||||
while ((entry = readdir(dir)) != NULL) {
|
||||
char* name = entry->d_name;
|
||||
int pid;
|
||||
// filename is a number: process directory
|
||||
pid = atoi(name);
|
||||
|
||||
if (pid == parentPid)
|
||||
int pid = atoi(name);
|
||||
|
||||
if (parent && pid == parent->pid)
|
||||
continue;
|
||||
|
||||
bool isThread = parent;
|
||||
// The RedHat kernel hides threads with a dot.
|
||||
// I believe this is non-standard.
|
||||
bool isThread = false;
|
||||
if ((!this->hideThreads) && pid == 0 && name[0] == '.') {
|
||||
char* tname = name + 1;
|
||||
pid = atoi(tname);
|
||||
if (pid > 0)
|
||||
isThread = true;
|
||||
isThread = true;
|
||||
}
|
||||
|
||||
if (pid > 0) {
|
||||
|
||||
FILE* status;
|
||||
char statusfilename[MAX_NAME+1];
|
||||
char command[PROCESS_COMM_LEN + 1];
|
||||
|
||||
Process* process = NULL;
|
||||
Process* existingProcess = (Process*) Hashtable_get(this->processTable, pid);
|
||||
|
||||
if (existingProcess) {
|
||||
assert(Vector_indexOf(this->processes, existingProcess, Process_pidCompare) != -1);
|
||||
process = existingProcess;
|
||||
assert(process->pid == pid);
|
||||
} else {
|
||||
if (parent && parent->pid == pid) {
|
||||
process = parent;
|
||||
} else {
|
||||
process = Process_new(this);
|
||||
assert(process->comm == NULL);
|
||||
process->pid = pid;
|
||||
}
|
||||
}
|
||||
process->tgid = parent ? parent->pid : pid;
|
||||
|
||||
if (showUserlandThreads && (!parent || pid != parent->pid)) {
|
||||
char subdirname[MAX_NAME+1];
|
||||
snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
|
||||
|
||||
ProcessList_processEntries(this, subdirname, process, pid, period);
|
||||
}
|
||||
|
||||
#ifdef HAVE_TASKSTATS
|
||||
ProcessList_readIoFile(process, dirname, name);
|
||||
#endif
|
||||
|
||||
process->updated = true;
|
||||
|
||||
if (!existingProcess)
|
||||
if (! ProcessList_readStatusFile(process, dirname, name))
|
||||
goto errorReadingProcess;
|
||||
|
||||
snprintf(statusfilename, MAX_NAME, "%s/%s/statm", dirname, name);
|
||||
status = ProcessList_fopen(this, statusfilename, "r");
|
||||
|
||||
if(!status) {
|
||||
goto errorReadingProcess;
|
||||
}
|
||||
int num = ProcessList_fread(this, status, "%d %d %d %d %d %d %d",
|
||||
&process->m_size, &process->m_resident, &process->m_share,
|
||||
&process->m_trs, &process->m_lrs, &process->m_drs,
|
||||
&process->m_dt);
|
||||
|
||||
fclose(status);
|
||||
if(num != 7)
|
||||
goto errorReadingProcess;
|
||||
|
||||
if (this->hideKernelThreads && process->m_size == 0)
|
||||
goto errorReadingProcess;
|
||||
|
||||
int lasttimes = (process->utime + process->stime);
|
||||
|
||||
snprintf(statusfilename, MAX_NAME, "%s/%s/stat", dirname, name);
|
||||
|
||||
status = ProcessList_fopen(this, statusfilename, "r");
|
||||
if (status == NULL)
|
||||
goto errorReadingProcess;
|
||||
|
||||
int success = ProcessList_readStatFile(process, status, command);
|
||||
fclose(status);
|
||||
if(!success) {
|
||||
goto errorReadingProcess;
|
||||
}
|
||||
|
||||
if(!existingProcess) {
|
||||
process->user = UsersTable_getRef(this->usersTable, process->st_uid);
|
||||
|
||||
#ifdef HAVE_OPENVZ
|
||||
if (access("/proc/vz", R_OK) != 0) {
|
||||
process->vpid = process->pid;
|
||||
process->ctid = 0;
|
||||
} else {
|
||||
snprintf(statusfilename, MAX_NAME, "%s/%s/stat", dirname, name);
|
||||
status = ProcessList_fopen(this, statusfilename, "r");
|
||||
if (status == NULL)
|
||||
goto errorReadingProcess;
|
||||
num = ProcessList_fread(this, status,
|
||||
"%*u %*s %*c %*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %*u %*u %*u %*u %*u "
|
||||
"%*u %*u %u %u",
|
||||
&process->vpid, &process->ctid);
|
||||
fclose(status);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CGROUP
|
||||
snprintf(statusfilename, MAX_NAME, "%s/%s/cgroup", dirname, name);
|
||||
status = ProcessList_fopen(this, statusfilename, "r");
|
||||
if (status) {
|
||||
char buffer[256];
|
||||
char *ok = fgets(buffer, 255, status);
|
||||
if (ok) {
|
||||
char* trimmed = String_trim(buffer);
|
||||
char** fields = String_split(trimmed, ':');
|
||||
free(trimmed);
|
||||
|
||||
process->cgroup = strndup(fields[2] + 1, 10);
|
||||
String_freeArray(fields);
|
||||
}
|
||||
fclose(status);
|
||||
} else {
|
||||
process->cgroup = strdup("");
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VSERVER
|
||||
snprintf(statusfilename, MAX_NAME, "%s/%s/status", dirname, name);
|
||||
status = ProcessList_fopen(this, statusfilename, "r");
|
||||
if (status == NULL)
|
||||
goto errorReadingProcess;
|
||||
else {
|
||||
char buffer[256];
|
||||
process->vxid = 0;
|
||||
while (fgets(buffer, 255, status)) {
|
||||
|
||||
if (String_startsWith(buffer, "VxID:")) {
|
||||
int vxid;
|
||||
int ok = ProcessList_read(this, buffer, "VxID:\t%d", &vxid);
|
||||
if (ok >= 1) {
|
||||
process->vxid = vxid;
|
||||
}
|
||||
}
|
||||
#if defined HAVE_ANCIENT_VSERVER
|
||||
else if (String_startsWith(buffer, "s_context:")) {
|
||||
int vxid;
|
||||
int ok = ProcessList_read(this, buffer, "s_context:\t%d", &vxid);
|
||||
if (ok >= 1) {
|
||||
process->vxid = vxid;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
fclose(status);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
if ( ((!existingProcess) && (!showUserlandThreads || pid == parentPid || !this->showThreadNames))
|
||||
|| (this->showingThreadNames && !this->showThreadNames) ) {
|
||||
|
||||
snprintf(statusfilename, MAX_NAME, "%s/%s/cmdline", dirname, name);
|
||||
status = ProcessList_fopen(this, statusfilename, "r");
|
||||
if (!status) {
|
||||
goto errorReadingProcess;
|
||||
}
|
||||
|
||||
int amtRead = fread(command, 1, PROCESS_COMM_LEN - 1, status);
|
||||
if (amtRead > 0) {
|
||||
for (int i = 0; i < amtRead; i++)
|
||||
if (command[i] == '\0' || command[i] == '\n')
|
||||
command[i] = ' ';
|
||||
command[amtRead] = '\0';
|
||||
}
|
||||
fclose(status);
|
||||
command[PROCESS_COMM_LEN] = '\0';
|
||||
free(process->comm);
|
||||
process->comm = String_copy(command);
|
||||
} else if (pid != parentPid && this->showThreadNames) {
|
||||
free(process->comm);
|
||||
process->comm = String_copy(command);
|
||||
}
|
||||
|
||||
int percent_cpu = (process->utime + process->stime - lasttimes) /
|
||||
period * 100.0;
|
||||
process->percent_cpu = MAX(MIN(percent_cpu, cpus*100.0), 0.0);
|
||||
if (isnan(process->percent_cpu)) process->percent_cpu = 0.0;
|
||||
|
||||
process->percent_mem = (process->m_resident * PAGE_SIZE_KB) /
|
||||
(float)(this->totalMem) *
|
||||
100.0;
|
||||
|
||||
this->totalTasks++;
|
||||
if (process->state == 'R') {
|
||||
this->runningTasks++;
|
||||
}
|
||||
|
||||
if (!existingProcess) {
|
||||
ProcessList_add(this, process);
|
||||
}
|
||||
if (pid <= 0)
|
||||
continue;
|
||||
|
||||
// Exception handler.
|
||||
errorReadingProcess: {
|
||||
if (process->comm) {
|
||||
free(process->comm);
|
||||
process->comm = NULL;
|
||||
}
|
||||
if (existingProcess)
|
||||
ProcessList_remove(this, process);
|
||||
else
|
||||
Process_delete((Object*)process);
|
||||
Process* process = NULL;
|
||||
Process* existingProcess = (Process*) Hashtable_get(this->processTable, pid);
|
||||
|
||||
if (existingProcess) {
|
||||
assert(Vector_indexOf(this->processes, existingProcess, Process_pidCompare) != -1);
|
||||
process = existingProcess;
|
||||
assert(process->pid == pid);
|
||||
} else {
|
||||
process = Process_new(this);
|
||||
assert(process->comm == NULL);
|
||||
process->pid = pid;
|
||||
process->tgid = parent ? parent->pid : pid;
|
||||
}
|
||||
|
||||
char subdirname[MAX_NAME+1];
|
||||
snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
|
||||
ProcessList_processEntries(this, subdirname, process, period);
|
||||
|
||||
#ifdef HAVE_TASKSTATS
|
||||
ProcessList_readIoFile(process, dirname, name);
|
||||
#endif
|
||||
|
||||
if (! ProcessList_readStatmFile(process, dirname, name))
|
||||
goto errorReadingProcess;
|
||||
|
||||
isThread = Process_isThread(process);
|
||||
process->show = ! ((hideKernelThreads && Process_isKernelThread(process)) || (hideUserlandThreads && Process_isUserlandThread(process)));
|
||||
|
||||
char command[MAX_NAME+1];
|
||||
int lasttimes = (process->utime + process->stime);
|
||||
if (! ProcessList_readStatFile(process, dirname, name, command))
|
||||
goto errorReadingProcess;
|
||||
int percent_cpu = (process->utime + process->stime - lasttimes) / period * 100.0;
|
||||
process->percent_cpu = MAX(MIN(percent_cpu, cpus*100.0), 0.0);
|
||||
if (isnan(process->percent_cpu)) process->percent_cpu = 0.0;
|
||||
process->percent_mem = (process->m_resident * PAGE_SIZE_KB) / (float)(this->totalMem) * 100.0;
|
||||
|
||||
if(!existingProcess) {
|
||||
process->user = UsersTable_getRef(this->usersTable, process->st_uid);
|
||||
|
||||
if (! ProcessList_statProcessDir(process, dirname, name))
|
||||
goto errorReadingProcess;
|
||||
|
||||
#ifdef HAVE_OPENVZ
|
||||
ProcessList_readOpenVZData(process, dirname, name);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CGROUP
|
||||
ProcessList_readCGroupFile(process, dirname, name);
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VSERVER
|
||||
ProcessList_readVServerData(process, dirname, name);
|
||||
#endif
|
||||
|
||||
if (! ProcessList_readCmdlineFile(process, dirname, name))
|
||||
goto errorReadingProcess;
|
||||
|
||||
ProcessList_add(this, process);
|
||||
}
|
||||
|
||||
if (isThread) {
|
||||
if (this->showThreadNames || Process_isKernelThread(process) || process->state == 'Z') {
|
||||
free(process->comm);
|
||||
process->comm = String_copy(command);
|
||||
} else if (this->showingThreadNames) {
|
||||
if (! ProcessList_readCmdlineFile(process, dirname, name))
|
||||
goto errorReadingProcess;
|
||||
}
|
||||
}
|
||||
|
||||
this->totalTasks++;
|
||||
if (process->state == 'R')
|
||||
this->runningTasks++;
|
||||
process->updated = true;
|
||||
|
||||
continue;
|
||||
|
||||
// Exception handler.
|
||||
errorReadingProcess: {
|
||||
if (process->comm) {
|
||||
free(process->comm);
|
||||
process->comm = NULL;
|
||||
}
|
||||
if (existingProcess)
|
||||
ProcessList_remove(this, process);
|
||||
else
|
||||
Process_delete((Object*)process);
|
||||
}
|
||||
}
|
||||
closedir(dir);
|
||||
return true;
|
||||
@ -761,36 +664,35 @@ void ProcessList_scan(ProcessList* this) {
|
||||
unsigned long long int usertime, nicetime, systemtime, systemalltime, idlealltime, idletime, totaltime, virtalltime;
|
||||
unsigned long long int swapFree = 0;
|
||||
|
||||
FILE* status;
|
||||
status = ProcessList_fopen(this, PROCMEMINFOFILE, "r");
|
||||
assert(status != NULL);
|
||||
FILE* file = fopen(PROCMEMINFOFILE, "r");
|
||||
assert(file != NULL);
|
||||
int cpus = this->cpuCount;
|
||||
{
|
||||
char buffer[128];
|
||||
while (fgets(buffer, 128, status)) {
|
||||
while (fgets(buffer, 128, file)) {
|
||||
|
||||
switch (buffer[0]) {
|
||||
case 'M':
|
||||
if (String_startsWith(buffer, "MemTotal:"))
|
||||
ProcessList_read(this, buffer, "MemTotal: %llu kB", &this->totalMem);
|
||||
sscanf(buffer, "MemTotal: %llu kB", &this->totalMem);
|
||||
else if (String_startsWith(buffer, "MemFree:"))
|
||||
ProcessList_read(this, buffer, "MemFree: %llu kB", &this->freeMem);
|
||||
sscanf(buffer, "MemFree: %llu kB", &this->freeMem);
|
||||
else if (String_startsWith(buffer, "MemShared:"))
|
||||
ProcessList_read(this, buffer, "MemShared: %llu kB", &this->sharedMem);
|
||||
sscanf(buffer, "MemShared: %llu kB", &this->sharedMem);
|
||||
break;
|
||||
case 'B':
|
||||
if (String_startsWith(buffer, "Buffers:"))
|
||||
ProcessList_read(this, buffer, "Buffers: %llu kB", &this->buffersMem);
|
||||
sscanf(buffer, "Buffers: %llu kB", &this->buffersMem);
|
||||
break;
|
||||
case 'C':
|
||||
if (String_startsWith(buffer, "Cached:"))
|
||||
ProcessList_read(this, buffer, "Cached: %llu kB", &this->cachedMem);
|
||||
sscanf(buffer, "Cached: %llu kB", &this->cachedMem);
|
||||
break;
|
||||
case 'S':
|
||||
if (String_startsWith(buffer, "SwapTotal:"))
|
||||
ProcessList_read(this, buffer, "SwapTotal: %llu kB", &this->totalSwap);
|
||||
sscanf(buffer, "SwapTotal: %llu kB", &this->totalSwap);
|
||||
if (String_startsWith(buffer, "SwapFree:"))
|
||||
ProcessList_read(this, buffer, "SwapFree: %llu kB", &swapFree);
|
||||
sscanf(buffer, "SwapFree: %llu kB", &swapFree);
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -798,11 +700,10 @@ void ProcessList_scan(ProcessList* this) {
|
||||
|
||||
this->usedMem = this->totalMem - this->freeMem;
|
||||
this->usedSwap = this->totalSwap - swapFree;
|
||||
fclose(status);
|
||||
fclose(file);
|
||||
|
||||
status = ProcessList_fopen(this, PROCSTATFILE, "r");
|
||||
|
||||
assert(status != NULL);
|
||||
file = fopen(PROCSTATFILE, "r");
|
||||
assert(file != NULL);
|
||||
for (int i = 0; i <= cpus; i++) {
|
||||
char buffer[256];
|
||||
int cpuid;
|
||||
@ -811,11 +712,11 @@ void ProcessList_scan(ProcessList* this) {
|
||||
// Dependending on your kernel version,
|
||||
// 5, 7 or 8 of these fields will be set.
|
||||
// The rest will remain at zero.
|
||||
fgets(buffer, 255, status);
|
||||
fgets(buffer, 255, file);
|
||||
if (i == 0)
|
||||
ProcessList_read(this, buffer, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest);
|
||||
sscanf(buffer, "cpu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest);
|
||||
else {
|
||||
ProcessList_read(this, buffer, "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu %llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest);
|
||||
sscanf(buffer, "cpu%d %llu %llu %llu %llu %llu %llu %llu %llu %llu", &cpuid, &usertime, &nicetime, &systemtime, &idletime, &ioWait, &irq, &softIrq, &steal, &guest);
|
||||
assert(cpuid == i - 1);
|
||||
}
|
||||
// Fields existing on kernels >= 2.6
|
||||
@ -862,7 +763,7 @@ void ProcessList_scan(ProcessList* this) {
|
||||
cpuData->guestTime = guest;
|
||||
cpuData->totalTime = totaltime;
|
||||
}
|
||||
float period = (float)this->cpus[0].totalPeriod / cpus; fclose(status);
|
||||
float period = (float)this->cpus[0].totalPeriod / cpus; fclose(file);
|
||||
|
||||
// mark all process as "dirty"
|
||||
for (int i = 0; i < Vector_size(this->processes); i++) {
|
||||
@ -873,7 +774,7 @@ void ProcessList_scan(ProcessList* this) {
|
||||
this->totalTasks = 0;
|
||||
this->runningTasks = 0;
|
||||
|
||||
ProcessList_processEntries(this, PROCDIR, NULL, 0, period);
|
||||
ProcessList_processEntries(this, PROCDIR, NULL, period);
|
||||
|
||||
this->showingThreadNames = this->showThreadNames;
|
||||
|
||||
|
@ -58,10 +58,6 @@ in the source distribution for its full text.
|
||||
|
||||
|
||||
|
||||
#ifdef DEBUG_PROC
|
||||
typedef int(*vxscanf)(void*, const char*, va_list);
|
||||
#endif
|
||||
|
||||
typedef struct CPUData_ {
|
||||
unsigned long long int totalTime;
|
||||
unsigned long long int userTime;
|
||||
@ -126,34 +122,16 @@ typedef struct ProcessList_ {
|
||||
bool highlightMegabytes;
|
||||
bool highlightThreads;
|
||||
bool detailedCPUTime;
|
||||
#ifdef DEBUG_PROC
|
||||
FILE* traceFile;
|
||||
#endif
|
||||
|
||||
} ProcessList;
|
||||
|
||||
#ifdef DEBUG_PROC
|
||||
|
||||
#define ProcessList_read(this, buffer, format, ...) ProcessList_xread(this, (vxscanf) vsscanf, buffer, format, ## __VA_ARGS__ )
|
||||
#define ProcessList_fread(this, file, format, ...) ProcessList_xread(this, (vxscanf) vfscanf, file, format, ## __VA_ARGS__ )
|
||||
|
||||
#else
|
||||
|
||||
#ifndef ProcessList_read
|
||||
#define ProcessList_fopen(this, path, mode) fopen(path, mode)
|
||||
#define ProcessList_read(this, buffer, format, ...) sscanf(buffer, format, ## __VA_ARGS__ )
|
||||
#define ProcessList_fread(this, file, format, ...) fscanf(file, format, ## __VA_ARGS__ )
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
ProcessList* ProcessList_new(UsersTable* usersTable);
|
||||
|
||||
void ProcessList_delete(ProcessList* this);
|
||||
|
||||
void ProcessList_invertSortOrder(ProcessList* this);
|
||||
|
||||
RichString ProcessList_printHeader(ProcessList* this);
|
||||
void ProcessList_printHeader(ProcessList* this, RichString* header);
|
||||
|
||||
Process* ProcessList_get(ProcessList* this, int idx);
|
||||
|
||||
@ -165,6 +143,19 @@ void ProcessList_sort(ProcessList* this);
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_OPENVZ
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_CGROUP
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_VSERVER
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
void ProcessList_scan(ProcessList* this);
|
||||
|
||||
ProcessField ProcessList_keyAt(ProcessList* this, int at);
|
||||
|
125
RichString.c
125
RichString.c
@ -22,26 +22,31 @@
|
||||
|
||||
/*{
|
||||
|
||||
#define RichString_init(this) (this)->len = 0
|
||||
#define RichString_initVal(this) (this).len = 0
|
||||
#define RichString_size(this) ((this)->chlen)
|
||||
#define RichString_sizeVal(this) ((this).chlen)
|
||||
|
||||
#define RichString_begin(this) RichString (this); (this).chlen = 0; (this).chptr = (this).chstr;
|
||||
#define RichString_beginAllocated(this) (this).chlen = 0; (this).chptr = (this).chstr;
|
||||
#define RichString_end(this) RichString_prune(&(this));
|
||||
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, this.chstr)
|
||||
#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, this.chstr + off, n)
|
||||
#define RichString_getCharVal(this, i) (this.chstr[i].chars[0] & 255)
|
||||
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr)
|
||||
#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + off, n)
|
||||
#define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255)
|
||||
#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)].chars[0] = ch; } while(0)
|
||||
#define CharType cchar_t
|
||||
#else
|
||||
#define RichString_printVal(this, y, x) mvaddchstr(y, x, this.chstr)
|
||||
#define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, this.chstr + off, n)
|
||||
#define RichString_getCharVal(this, i) (this.chstr[i])
|
||||
#define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr)
|
||||
#define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, (this).chptr + off, n)
|
||||
#define RichString_getCharVal(this, i) ((this).chptr[i])
|
||||
#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)] = ch; } while(0)
|
||||
#define CharType chtype
|
||||
#endif
|
||||
|
||||
typedef struct RichString_ {
|
||||
int len;
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
cchar_t chstr[RICHSTRING_MAXLEN+1];
|
||||
#else
|
||||
chtype chstr[RICHSTRING_MAXLEN+1];
|
||||
#endif
|
||||
int chlen;
|
||||
CharType chstr[RICHSTRING_MAXLEN+1];
|
||||
CharType* chptr;
|
||||
} RichString;
|
||||
|
||||
}*/
|
||||
@ -50,35 +55,57 @@ typedef struct RichString_ {
|
||||
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#define charBytes(n) (sizeof(CharType) * (n))
|
||||
|
||||
static inline void RichString_setLen(RichString* this, int len) {
|
||||
if (this->chlen <= RICHSTRING_MAXLEN) {
|
||||
if (len > RICHSTRING_MAXLEN) {
|
||||
this->chptr = malloc(charBytes(len+1));
|
||||
memcpy(this->chptr, this->chstr, charBytes(this->chlen+1));
|
||||
}
|
||||
} else {
|
||||
if (len <= RICHSTRING_MAXLEN) {
|
||||
memcpy(this->chstr, this->chptr, charBytes(this->chlen));
|
||||
free(this->chptr);
|
||||
this->chptr = this->chstr;
|
||||
} else {
|
||||
this->chptr = realloc(this->chptr, charBytes(len+1));
|
||||
}
|
||||
}
|
||||
RichString_setChar(this, len, 0);
|
||||
this->chlen = len;
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
|
||||
inline void RichString_appendn(RichString* this, int attrs, const char* data_c, int len) {
|
||||
wchar_t data[RICHSTRING_MAXLEN];
|
||||
len = mbstowcs(data, data_c, RICHSTRING_MAXLEN);
|
||||
wchar_t data[len+1];
|
||||
len = mbstowcs(data, data_c, len);
|
||||
if (len<0)
|
||||
return;
|
||||
int last = MIN(RICHSTRING_MAXLEN - 1, len + this->len);
|
||||
for (int i = this->len, j = 0; i < last; i++, j++) {
|
||||
memset(&this->chstr[i], 0, sizeof(this->chstr[i]));
|
||||
this->chstr[i].chars[0] = data[j];
|
||||
this->chstr[i].attr = attrs;
|
||||
int oldLen = this->chlen;
|
||||
int newLen = len + oldLen;
|
||||
RichString_setLen(this, newLen);
|
||||
for (int i = oldLen, j = 0; i < newLen; i++, j++) {
|
||||
memset(&this->chptr[i], 0, sizeof(this->chptr[i]));
|
||||
this->chptr[i].chars[0] = data[j];
|
||||
this->chptr[i].attr = attrs;
|
||||
}
|
||||
this->chstr[last].chars[0] = 0;
|
||||
this->len = last;
|
||||
this->chptr[newLen].chars[0] = 0;
|
||||
}
|
||||
|
||||
inline void RichString_setAttrn(RichString *this, int attrs, int start, int finish) {
|
||||
cchar_t* ch = this->chstr + start;
|
||||
inline void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
|
||||
cchar_t* ch = this->chptr + start;
|
||||
for (int i = start; i <= finish; i++) {
|
||||
ch->attr = attrs;
|
||||
ch++;
|
||||
}
|
||||
}
|
||||
|
||||
int RichString_findChar(RichString *this, char c, int start) {
|
||||
int RichString_findChar(RichString* this, char c, int start) {
|
||||
wchar_t wc = btowc(c);
|
||||
cchar_t* ch = this->chstr + start;
|
||||
for (int i = start; i < this->len; i++) {
|
||||
cchar_t* ch = this->chptr + start;
|
||||
for (int i = start; i < this->chlen; i++) {
|
||||
if (ch->chars[0] == wc)
|
||||
return i;
|
||||
ch++;
|
||||
@ -89,25 +116,25 @@ int RichString_findChar(RichString *this, char c, int start) {
|
||||
#else
|
||||
|
||||
inline void RichString_appendn(RichString* this, int attrs, const char* data_c, int len) {
|
||||
int last = MIN(RICHSTRING_MAXLEN - 1, len + this->len);
|
||||
for (int i = this->len, j = 0; i < last; i++, j++)
|
||||
this->chstr[i] = (isprint(data_c[j]) ? data_c[j] : '?') | attrs;
|
||||
|
||||
this->chstr[last] = 0;
|
||||
this->len = last;
|
||||
int oldLen = this->chlen;
|
||||
int newLen = len + oldLen;
|
||||
RichString_setLen(this, newLen);
|
||||
for (int i = oldLen, j = 0; i < newLen; i++, j++)
|
||||
this->chptr[i] = (isprint(data_c[j]) ? data_c[j] : '?') | attrs;
|
||||
this->chptr[newLen] = 0;
|
||||
}
|
||||
|
||||
void RichString_setAttrn(RichString *this, int attrs, int start, int finish) {
|
||||
chtype* ch = this->chstr + start;
|
||||
void RichString_setAttrn(RichString* this, int attrs, int start, int finish) {
|
||||
chtype* ch = this->chptr + start;
|
||||
for (int i = start; i <= finish; i++) {
|
||||
*ch = (*ch & 0xff) | attrs;
|
||||
ch++;
|
||||
}
|
||||
}
|
||||
|
||||
int RichString_findChar(RichString *this, char c, int start) {
|
||||
chtype* ch = this->chstr + start;
|
||||
for (int i = start; i < this->len; i++) {
|
||||
int RichString_findChar(RichString* this, char c, int start) {
|
||||
chtype* ch = this->chptr + start;
|
||||
for (int i = start; i < this->chlen; i++) {
|
||||
if ((*ch & 0xff) == (chtype) c)
|
||||
return i;
|
||||
ch++;
|
||||
@ -118,11 +145,14 @@ int RichString_findChar(RichString *this, char c, int start) {
|
||||
#endif
|
||||
|
||||
void RichString_prune(RichString* this) {
|
||||
this->len = 0;
|
||||
if (this->chlen > RICHSTRING_MAXLEN)
|
||||
free(this->chptr);
|
||||
this->chptr = this->chstr;
|
||||
this->chlen = 0;
|
||||
}
|
||||
|
||||
void RichString_setAttr(RichString *this, int attrs) {
|
||||
RichString_setAttrn(this, attrs, 0, this->len - 1);
|
||||
void RichString_setAttr(RichString* this, int attrs) {
|
||||
RichString_setAttrn(this, attrs, 0, this->chlen - 1);
|
||||
}
|
||||
|
||||
inline void RichString_append(RichString* this, int attrs, const char* data) {
|
||||
@ -130,13 +160,6 @@ inline void RichString_append(RichString* this, int attrs, const char* data) {
|
||||
}
|
||||
|
||||
void RichString_write(RichString* this, int attrs, const char* data) {
|
||||
RichString_init(this);
|
||||
RichString_append(this, attrs, data);
|
||||
}
|
||||
|
||||
RichString RichString_quickString(int attrs, const char* data) {
|
||||
RichString str;
|
||||
RichString_initVal(str);
|
||||
RichString_write(&str, attrs, data);
|
||||
return str;
|
||||
RichString_setLen(this, 0);
|
||||
RichString_appendn(this, attrs, data, strlen(data));
|
||||
}
|
||||
|
47
RichString.h
47
RichString.h
@ -24,26 +24,31 @@
|
||||
#define RICHSTRING_MAXLEN 300
|
||||
|
||||
|
||||
#define RichString_init(this) (this)->len = 0
|
||||
#define RichString_initVal(this) (this).len = 0
|
||||
#define RichString_size(this) ((this)->chlen)
|
||||
#define RichString_sizeVal(this) ((this).chlen)
|
||||
|
||||
#define RichString_begin(this) RichString (this); (this).chlen = 0; (this).chptr = (this).chstr;
|
||||
#define RichString_beginAllocated(this) (this).chlen = 0; (this).chptr = (this).chstr;
|
||||
#define RichString_end(this) RichString_prune(&(this));
|
||||
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, this.chstr)
|
||||
#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, this.chstr + off, n)
|
||||
#define RichString_getCharVal(this, i) (this.chstr[i].chars[0] & 255)
|
||||
#define RichString_printVal(this, y, x) mvadd_wchstr(y, x, (this).chptr)
|
||||
#define RichString_printoffnVal(this, y, x, off, n) mvadd_wchnstr(y, x, (this).chptr + off, n)
|
||||
#define RichString_getCharVal(this, i) ((this).chptr[i].chars[0] & 255)
|
||||
#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)].chars[0] = ch; } while(0)
|
||||
#define CharType cchar_t
|
||||
#else
|
||||
#define RichString_printVal(this, y, x) mvaddchstr(y, x, this.chstr)
|
||||
#define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, this.chstr + off, n)
|
||||
#define RichString_getCharVal(this, i) (this.chstr[i])
|
||||
#define RichString_printVal(this, y, x) mvaddchstr(y, x, (this).chptr)
|
||||
#define RichString_printoffnVal(this, y, x, off, n) mvaddchnstr(y, x, (this).chptr + off, n)
|
||||
#define RichString_getCharVal(this, i) ((this).chptr[i])
|
||||
#define RichString_setChar(this, at, ch) do{ (this)->chptr[(at)] = ch; } while(0)
|
||||
#define CharType chtype
|
||||
#endif
|
||||
|
||||
typedef struct RichString_ {
|
||||
int len;
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
cchar_t chstr[RICHSTRING_MAXLEN+1];
|
||||
#else
|
||||
chtype chstr[RICHSTRING_MAXLEN+1];
|
||||
#endif
|
||||
int chlen;
|
||||
CharType chstr[RICHSTRING_MAXLEN+1];
|
||||
CharType* chptr;
|
||||
} RichString;
|
||||
|
||||
|
||||
@ -51,32 +56,32 @@ typedef struct RichString_ {
|
||||
#define MIN(a,b) ((a)<(b)?(a):(b))
|
||||
#endif
|
||||
|
||||
#define charBytes(n) (sizeof(CharType) * (n))
|
||||
|
||||
#ifdef HAVE_LIBNCURSESW
|
||||
|
||||
extern void RichString_appendn(RichString* this, int attrs, const char* data_c, int len);
|
||||
|
||||
extern void RichString_setAttrn(RichString *this, int attrs, int start, int finish);
|
||||
extern void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
|
||||
|
||||
int RichString_findChar(RichString *this, char c, int start);
|
||||
int RichString_findChar(RichString* this, char c, int start);
|
||||
|
||||
#else
|
||||
|
||||
extern void RichString_appendn(RichString* this, int attrs, const char* data_c, int len);
|
||||
|
||||
void RichString_setAttrn(RichString *this, int attrs, int start, int finish);
|
||||
void RichString_setAttrn(RichString* this, int attrs, int start, int finish);
|
||||
|
||||
int RichString_findChar(RichString *this, char c, int start);
|
||||
int RichString_findChar(RichString* this, char c, int start);
|
||||
|
||||
#endif
|
||||
|
||||
void RichString_prune(RichString* this);
|
||||
|
||||
void RichString_setAttr(RichString *this, int attrs);
|
||||
void RichString_setAttr(RichString* this, int attrs);
|
||||
|
||||
extern void RichString_append(RichString* this, int attrs, const char* data);
|
||||
|
||||
void RichString_write(RichString* this, int attrs, const char* data);
|
||||
|
||||
RichString RichString_quickString(int attrs, const char* data);
|
||||
|
||||
#endif
|
||||
|
@ -34,8 +34,7 @@ static void SwapMeter_display(Object* cast, RichString* out) {
|
||||
char buffer[50];
|
||||
Meter* this = (Meter*)cast;
|
||||
long int swap = (long int) this->values[0];
|
||||
RichString_init(out);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], ":");
|
||||
RichString_write(out, CRT_colors[METER_TEXT], ":");
|
||||
sprintf(buffer, "%ldM ", (long int) this->total / 1024);
|
||||
RichString_append(out, CRT_colors[METER_VALUE], buffer);
|
||||
sprintf(buffer, "%ldk", swap);
|
||||
|
@ -26,10 +26,9 @@ static void TasksMeter_setValues(Meter* this, char* buffer, int len) {
|
||||
|
||||
static void TasksMeter_display(Object* cast, RichString* out) {
|
||||
Meter* this = (Meter*)cast;
|
||||
RichString_init(out);
|
||||
char buffer[20];
|
||||
sprintf(buffer, "%d", (int)this->total);
|
||||
RichString_append(out, CRT_colors[METER_VALUE], buffer);
|
||||
RichString_write(out, CRT_colors[METER_VALUE], buffer);
|
||||
RichString_append(out, CRT_colors[METER_TEXT], " total, ");
|
||||
sprintf(buffer, "%d", (int)this->values[0]);
|
||||
RichString_append(out, CRT_colors[TASKS_RUNNING], buffer);
|
||||
|
19
htop.c
19
htop.c
@ -241,7 +241,7 @@ static inline void setSortKey(ProcessList* pl, ProcessField sortKey, Panel* pane
|
||||
pl->direction = 1;
|
||||
pl->treeView = false;
|
||||
settings->changed = true;
|
||||
Panel_setRichHeader(panel, ProcessList_printHeader(pl));
|
||||
ProcessList_printHeader(pl, Panel_getHeader(panel));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
@ -352,7 +352,7 @@ int main(int argc, char** argv) {
|
||||
pl->treeView = false;
|
||||
pl->direction = 1;
|
||||
}
|
||||
Panel_setRichHeader(panel, ProcessList_printHeader(pl));
|
||||
ProcessList_printHeader(pl, Panel_getHeader(panel));
|
||||
|
||||
const char* searchFunctions[] = {"Next ", "Exit ", " Search: ", NULL};
|
||||
const char* searchKeys[] = {"F3", "Esc", " "};
|
||||
@ -395,7 +395,7 @@ int main(int argc, char** argv) {
|
||||
ProcessList_scan(pl);
|
||||
doRecalculate = false;
|
||||
}
|
||||
if (refreshTimeout == 0) {
|
||||
if (refreshTimeout == 0 || pl->treeView) {
|
||||
ProcessList_sort(pl);
|
||||
refreshTimeout = 1;
|
||||
}
|
||||
@ -404,7 +404,7 @@ int main(int argc, char** argv) {
|
||||
int idx = 0;
|
||||
for (int i = 0; i < size; i++) {
|
||||
Process* p = ProcessList_get(pl, i);
|
||||
if (!userOnly || (p->st_uid == userId)) {
|
||||
if (p->show && (!userOnly || (p->st_uid == userId))) {
|
||||
Panel_set(panel, idx, (Object*)p);
|
||||
if ((!follow && idx == currPos) || (follow && p->pid == currPid)) {
|
||||
Panel_setSelected(panel, idx);
|
||||
@ -608,7 +608,7 @@ int main(int argc, char** argv) {
|
||||
{
|
||||
Setup_run(settings, headerHeight);
|
||||
// TODO: shouldn't need this, colors should be dynamic
|
||||
Panel_setRichHeader(panel, ProcessList_printHeader(pl));
|
||||
ProcessList_printHeader(pl, Panel_getHeader(panel));
|
||||
headerHeight = Header_calculateHeight(header);
|
||||
Panel_move(panel, 0, headerHeight);
|
||||
Panel_resize(panel, COLS, LINES-headerHeight-1);
|
||||
@ -680,7 +680,7 @@ int main(int argc, char** argv) {
|
||||
napms(500);
|
||||
}
|
||||
}
|
||||
Panel_setRichHeader(panel, ProcessList_printHeader(pl));
|
||||
ProcessList_printHeader(pl, Panel_getHeader(panel));
|
||||
refreshTimeout = 0;
|
||||
break;
|
||||
}
|
||||
@ -715,7 +715,7 @@ int main(int argc, char** argv) {
|
||||
beep();
|
||||
}
|
||||
((Object*)affinityPanel)->delete((Object*)affinityPanel);
|
||||
Panel_setRichHeader(panel, ProcessList_printHeader(pl));
|
||||
ProcessList_printHeader(pl, Panel_getHeader(panel));
|
||||
refreshTimeout = 0;
|
||||
break;
|
||||
}
|
||||
@ -747,7 +747,7 @@ int main(int argc, char** argv) {
|
||||
settings->changed = true;
|
||||
setSortKey(pl, field->key, panel, settings);
|
||||
} else {
|
||||
Panel_setRichHeader(panel, ProcessList_printHeader(pl));
|
||||
ProcessList_printHeader(pl, Panel_getHeader(panel));
|
||||
}
|
||||
((Object*)sortPanel)->delete((Object*)sortPanel);
|
||||
refreshTimeout = 0;
|
||||
@ -825,6 +825,9 @@ int main(int argc, char** argv) {
|
||||
((Object*)killPanel)->delete((Object*)killPanel);
|
||||
UsersTable_delete(ut);
|
||||
Settings_delete(settings);
|
||||
#ifdef HAVE_PLPA
|
||||
plpa_finalize();
|
||||
#endif
|
||||
debug_done();
|
||||
return 0;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user