mirror of https://github.com/xzeldon/htop.git
Add support for Linux per-process IO statistics,
enabled with the --enable-taskstats flag, which requires a kernel compiled with taskstats support. Thanks to Tobias Oetiker!
This commit is contained in:
parent
460608d6e2
commit
12f4f09e6e
|
@ -1,6 +1,10 @@
|
||||||
|
|
||||||
What's new in version 0.7.1
|
What's new in version 0.7.1
|
||||||
|
|
||||||
|
* Add support for Linux per-process IO statistics,
|
||||||
|
enabled with the --enable-taskstats flag, which
|
||||||
|
requires a kernel compiled with taskstats support.
|
||||||
|
(thanks to Tobias Oetiker)
|
||||||
* Add Unicode support, enabled with the --enable-unicode
|
* Add Unicode support, enabled with the --enable-unicode
|
||||||
flag, which requires libncursesw.
|
flag, which requires libncursesw.
|
||||||
(thanks to Sergej Pupykin)
|
(thanks to Sergej Pupykin)
|
||||||
|
|
74
Process.c
74
Process.c
|
@ -49,6 +49,9 @@ typedef enum ProcessField_ {
|
||||||
#ifdef HAVE_OPENVZ
|
#ifdef HAVE_OPENVZ
|
||||||
VEID, VPID,
|
VEID, VPID,
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_TASKSTATS
|
||||||
|
RCHAR, WCHAR, SYSCR, SYSCW, RBYTES, WBYTES, CNCLWB, IO_READ_RATE, IO_WRITE_RATE,
|
||||||
|
#endif
|
||||||
LAST_PROCESSFIELD
|
LAST_PROCESSFIELD
|
||||||
} ProcessField;
|
} ProcessField;
|
||||||
|
|
||||||
|
@ -121,6 +124,19 @@ typedef struct Process_ {
|
||||||
unsigned int veid;
|
unsigned int veid;
|
||||||
unsigned int vpid;
|
unsigned int vpid;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_TASKSTATS
|
||||||
|
unsigned long long io_rchar;
|
||||||
|
unsigned long long io_wchar;
|
||||||
|
unsigned long long io_syscr;
|
||||||
|
unsigned long long io_syscw;
|
||||||
|
unsigned long long io_read_bytes;
|
||||||
|
unsigned long long io_write_bytes;
|
||||||
|
unsigned long long io_cancelled_write_bytes;
|
||||||
|
double io_rate_read_bps;
|
||||||
|
unsigned long long io_rate_read_time;
|
||||||
|
double io_rate_write_bps;
|
||||||
|
unsigned long long io_rate_write_time;
|
||||||
|
#endif
|
||||||
} Process;
|
} Process;
|
||||||
|
|
||||||
}*/
|
}*/
|
||||||
|
@ -136,13 +152,16 @@ char *Process_fieldNames[] = {
|
||||||
#ifdef HAVE_OPENVZ
|
#ifdef HAVE_OPENVZ
|
||||||
"VEID", "VPID",
|
"VEID", "VPID",
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_TASKSTATS
|
||||||
|
"RCHAR", "WCHAR", "SYSCR", "SYSCW", "RBYTES", "WBYTES", "CNCLWB", "IO_READ_RATE", "IO_WRITE_RATE",
|
||||||
|
#endif
|
||||||
"*** report bug! ***"
|
"*** report bug! ***"
|
||||||
};
|
};
|
||||||
|
|
||||||
static int Process_getuid = -1;
|
static int Process_getuid = -1;
|
||||||
|
|
||||||
Process* Process_new(struct ProcessList_ *pl) {
|
Process* Process_new(struct ProcessList_ *pl) {
|
||||||
Process* this = malloc(sizeof(Process));
|
Process* this = calloc(sizeof(Process), 1);
|
||||||
Object_setClass(this, PROCESS_CLASS);
|
Object_setClass(this, PROCESS_CLASS);
|
||||||
((Object*)this)->display = Process_display;
|
((Object*)this)->display = Process_display;
|
||||||
((Object*)this)->delete = Process_delete;
|
((Object*)this)->delete = Process_delete;
|
||||||
|
@ -160,6 +179,19 @@ Process* Process_new(struct ProcessList_ *pl) {
|
||||||
|
|
||||||
Process* Process_clone(Process* this) {
|
Process* Process_clone(Process* this) {
|
||||||
Process* clone = malloc(sizeof(Process));
|
Process* clone = malloc(sizeof(Process));
|
||||||
|
#if HAVE_TASKSTATS
|
||||||
|
this->io_rchar = 0;
|
||||||
|
this->io_wchar = 0;
|
||||||
|
this->io_syscr = 0;
|
||||||
|
this->io_syscw = 0;
|
||||||
|
this->io_read_bytes = 0;
|
||||||
|
this->io_rate_read_bps = 0;
|
||||||
|
this->io_rate_read_time = 0;
|
||||||
|
this->io_write_bytes = 0;
|
||||||
|
this->io_rate_write_bps = 0;
|
||||||
|
this->io_rate_write_time = 0;
|
||||||
|
this->io_cancelled_write_bytes = 0;
|
||||||
|
#endif
|
||||||
memcpy(clone, this, sizeof(Process));
|
memcpy(clone, this, sizeof(Process));
|
||||||
this->comm = NULL;
|
this->comm = NULL;
|
||||||
this->pid = 0;
|
this->pid = 0;
|
||||||
|
@ -400,11 +432,22 @@ void Process_writeField(Process* this, RichString* str, ProcessField field) {
|
||||||
case VEID: snprintf(buffer, n, "%5u ", this->veid); break;
|
case VEID: snprintf(buffer, n, "%5u ", this->veid); break;
|
||||||
case VPID: snprintf(buffer, n, "%5u ", this->vpid); break;
|
case VPID: snprintf(buffer, n, "%5u ", this->vpid); break;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_TASKSTATS
|
||||||
|
case RCHAR: snprintf(buffer, n, "%10llu ", this->io_rchar); break;
|
||||||
|
case WCHAR: snprintf(buffer, n, "%10llu ", this->io_wchar); break;
|
||||||
|
case SYSCR: snprintf(buffer, n, "%10llu ", this->io_syscr); break;
|
||||||
|
case SYSCW: snprintf(buffer, n, "%10llu ", this->io_syscw); break;
|
||||||
|
case RBYTES: snprintf(buffer, n, "%10llu ", this->io_read_bytes); break;
|
||||||
|
case WBYTES: snprintf(buffer, n, "%10llu ", this->io_write_bytes); break;
|
||||||
|
case CNCLWB: snprintf(buffer, n, "%10llu ", this->io_cancelled_write_bytes); break;
|
||||||
|
case IO_READ_RATE: Process_printLargeNumber(this, str, this->io_rate_read_bps / 1024); return;
|
||||||
|
case IO_WRITE_RATE: Process_printLargeNumber(this, str, this->io_rate_write_bps / 1024); return;
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
snprintf(buffer, n, "- ");
|
snprintf(buffer, n, "- ");
|
||||||
}
|
}
|
||||||
RichString_append(str, attr, buffer);
|
RichString_append(str, attr, buffer);
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
int Process_pidCompare(const void* v1, const void* v2) {
|
int Process_pidCompare(const void* v1, const void* v2) {
|
||||||
|
@ -423,6 +466,7 @@ int Process_compare(const void* v1, const void* v2) {
|
||||||
p2 = (Process*)v1;
|
p2 = (Process*)v1;
|
||||||
p1 = (Process*)v2;
|
p1 = (Process*)v2;
|
||||||
}
|
}
|
||||||
|
long long diff;
|
||||||
switch (pl->sortKey) {
|
switch (pl->sortKey) {
|
||||||
case PID:
|
case PID:
|
||||||
return (p1->pid - p2->pid);
|
return (p1->pid - p2->pid);
|
||||||
|
@ -470,10 +514,23 @@ int Process_compare(const void* v1, const void* v2) {
|
||||||
case VPID:
|
case VPID:
|
||||||
return (p1->vpid - p2->vpid);
|
return (p1->vpid - p2->vpid);
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_TASKSTATS
|
||||||
|
case RCHAR: diff = p2->io_rchar - p1->io_rchar; goto test_diff;
|
||||||
|
case WCHAR: diff = p2->io_wchar - p1->io_wchar; goto test_diff;
|
||||||
|
case SYSCR: diff = p2->io_syscr - p1->io_syscr; goto test_diff;
|
||||||
|
case SYSCW: diff = p2->io_syscw - p1->io_syscw; goto test_diff;
|
||||||
|
case RBYTES: diff = p2->io_read_bytes - p1->io_read_bytes; goto test_diff;
|
||||||
|
case WBYTES: diff = p2->io_write_bytes - p1->io_write_bytes; goto test_diff;
|
||||||
|
case CNCLWB: diff = p2->io_cancelled_write_bytes - p1->io_cancelled_write_bytes; goto test_diff;
|
||||||
|
case IO_READ_RATE: diff = p2->io_rate_read_bps - p1->io_rate_read_bps; goto test_diff;
|
||||||
|
case IO_WRITE_RATE: diff = p2->io_rate_write_bps - p1->io_rate_write_bps; goto test_diff;
|
||||||
|
#endif
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return (p1->pid - p2->pid);
|
return (p1->pid - p2->pid);
|
||||||
}
|
}
|
||||||
|
test_diff:
|
||||||
|
return (diff > 0) ? 1 : (diff < 0 ? -1 : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
char* Process_printField(ProcessField field) {
|
char* Process_printField(ProcessField field) {
|
||||||
|
@ -509,6 +566,17 @@ char* Process_printField(ProcessField field) {
|
||||||
case VEID: return " VEID ";
|
case VEID: return " VEID ";
|
||||||
case VPID: return " VPID ";
|
case VPID: return " VPID ";
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_TASKSTATS
|
||||||
|
case RCHAR: return " RD_CHAR ";
|
||||||
|
case WCHAR: return " WR_CHAR ";
|
||||||
|
case SYSCR: return " RD_SYSC ";
|
||||||
|
case SYSCW: return " WR_SYSC ";
|
||||||
|
case RBYTES: return " IO_RD ";
|
||||||
|
case WBYTES: return " IO_WR ";
|
||||||
|
case CNCLWB: return " IO_CANCEL ";
|
||||||
|
case IO_READ_RATE: return " IORR ";
|
||||||
|
case IO_WRITE_RATE: return " IOWR ";
|
||||||
|
#endif
|
||||||
default: return "- ";
|
default: return "- ";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
16
Process.h
16
Process.h
|
@ -51,6 +51,9 @@ typedef enum ProcessField_ {
|
||||||
#ifdef HAVE_OPENVZ
|
#ifdef HAVE_OPENVZ
|
||||||
VEID, VPID,
|
VEID, VPID,
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_TASKSTATS
|
||||||
|
RCHAR, WCHAR, SYSCR, SYSCW, RBYTES, WBYTES, CNCLWB, IO_READ_RATE, IO_WRITE_RATE,
|
||||||
|
#endif
|
||||||
LAST_PROCESSFIELD
|
LAST_PROCESSFIELD
|
||||||
} ProcessField;
|
} ProcessField;
|
||||||
|
|
||||||
|
@ -123,6 +126,19 @@ typedef struct Process_ {
|
||||||
unsigned int veid;
|
unsigned int veid;
|
||||||
unsigned int vpid;
|
unsigned int vpid;
|
||||||
#endif
|
#endif
|
||||||
|
#ifdef HAVE_TASKSTATS
|
||||||
|
unsigned long long io_rchar;
|
||||||
|
unsigned long long io_wchar;
|
||||||
|
unsigned long long io_syscr;
|
||||||
|
unsigned long long io_syscw;
|
||||||
|
unsigned long long io_read_bytes;
|
||||||
|
unsigned long long io_write_bytes;
|
||||||
|
unsigned long long io_cancelled_write_bytes;
|
||||||
|
double io_rate_read_bps;
|
||||||
|
unsigned long long io_rate_read_time;
|
||||||
|
double io_rate_write_bps;
|
||||||
|
unsigned long long io_rate_write_time;
|
||||||
|
#endif
|
||||||
} Process;
|
} Process;
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -499,6 +499,50 @@ bool ProcessList_readStatusFile(ProcessList* this, Process* proc, char* dirname,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_TASKSTATS
|
||||||
|
void ProcessList_readIoFile(ProcessList* this, Process* proc, char* dirname, char* name) {
|
||||||
|
char iofilename[MAX_NAME+1];
|
||||||
|
iofilename[MAX_NAME] = '\0';
|
||||||
|
|
||||||
|
char buffer[256];
|
||||||
|
buffer[255] = '\0';
|
||||||
|
|
||||||
|
snprintf(iofilename, MAX_NAME, "%s/%s/io", dirname, name);
|
||||||
|
FILE* io = ProcessList_fopen(this, iofilename, "r");
|
||||||
|
if (io) {
|
||||||
|
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 (!feof(io)) {
|
||||||
|
char* ok = fgets(buffer, 255, io);
|
||||||
|
if (!ok)
|
||||||
|
break;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
fclose(io);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
bool ProcessList_processEntries(ProcessList* this, char* dirname, Process* parent, float period) {
|
bool ProcessList_processEntries(ProcessList* this, char* dirname, Process* parent, float period) {
|
||||||
DIR* dir;
|
DIR* dir;
|
||||||
struct dirent* entry;
|
struct dirent* entry;
|
||||||
|
@ -547,6 +591,10 @@ bool ProcessList_processEntries(ProcessList* this, char* dirname, Process* paren
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifdef HAVE_TASKSTATS
|
||||||
|
ProcessList_readIoFile(this, process, dirname, name);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (showUserlandThreads && (!parent || pid != parent->pid)) {
|
if (showUserlandThreads && (!parent || pid != parent->pid)) {
|
||||||
char subdirname[MAX_NAME+1];
|
char subdirname[MAX_NAME+1];
|
||||||
snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
|
snprintf(subdirname, MAX_NAME, "%s/%s/task", dirname, name);
|
||||||
|
|
|
@ -165,6 +165,10 @@ void ProcessList_sort(ProcessList* this);
|
||||||
|
|
||||||
bool ProcessList_readStatusFile(ProcessList* this, Process* proc, char* dirname, char* name);
|
bool ProcessList_readStatusFile(ProcessList* this, Process* proc, char* dirname, char* name);
|
||||||
|
|
||||||
|
#ifdef HAVE_TASKSTATS
|
||||||
|
void ProcessList_readIoFile(ProcessList* this, Process* proc, char* dirname, char* name);
|
||||||
|
#endif
|
||||||
|
|
||||||
bool ProcessList_processEntries(ProcessList* this, char* dirname, Process* parent, float period);
|
bool ProcessList_processEntries(ProcessList* this, char* dirname, Process* parent, float period);
|
||||||
|
|
||||||
void ProcessList_scan(ProcessList* this);
|
void ProcessList_scan(ProcessList* this);
|
||||||
|
|
|
@ -69,6 +69,11 @@ if test "x$enable_openvz" = xyes; then
|
||||||
AC_DEFINE(HAVE_OPENVZ, 1, [Define if openvz support enabled.])
|
AC_DEFINE(HAVE_OPENVZ, 1, [Define if openvz support enabled.])
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
AC_ARG_ENABLE(taskstats, [AC_HELP_STRING([--enable-taskstats], [enable per-task IO Stats (taskstats kernel sup required)])], ,enable_taskstats="no")
|
||||||
|
if test "x$enable_taskstats" = xyes; then
|
||||||
|
AC_DEFINE(HAVE_TASKSTATS, 1, [Define if taskstats support enabled.])
|
||||||
|
fi
|
||||||
|
|
||||||
AC_ARG_ENABLE(unicode, [AC_HELP_STRING([--enable-unicode], [enable Unicode support])], ,enable_unicode="no")
|
AC_ARG_ENABLE(unicode, [AC_HELP_STRING([--enable-unicode], [enable Unicode support])], ,enable_unicode="no")
|
||||||
if test "x$enable_unicode" = xyes; then
|
if test "x$enable_unicode" = xyes; then
|
||||||
AC_CHECK_LIB([ncursesw], [refresh], [], [missing_libraries="$missing_libraries libncursesw"])
|
AC_CHECK_LIB([ncursesw], [refresh], [], [missing_libraries="$missing_libraries libncursesw"])
|
||||||
|
|
Loading…
Reference in New Issue