1
0
mirror of https://github.com/Llewellynvdm/conky.git synced 2024-11-17 18:45:10 +00:00

Support for per-task I/O statistics - $top_io

Basically, I just added three new process properties (io_read, io_write, io_perc - representing
the amount of I/O done by the process during the update interval) and $top_io, that sorts
processes based on io_perc.

Atm, it's completely #ifdef'd, since it requires kernel support. But that creates some wierd
looking syntax at some places, so it may be better to remove some ifdefs.  It even may be
possible to completely remove the ifdefs (ie. convert them to #ifdef linux) since the code will
compile just fine even if the kernel doesn't support I/O accounting. I'll leave that for someone
else to decide.
This commit is contained in:
Pavel Labath 2009-06-12 19:08:44 +02:00
parent 71f6880b01
commit c0d1c313e9
8 changed files with 265 additions and 14 deletions

View File

@ -229,6 +229,25 @@ fi
AM_CONDITIONAL(BUILD_APCUPSD, test x$want_apcupsd = xyes)
dnl
dnl I/O stats
dnl
AC_ARG_ENABLE([iostats],
AC_HELP_STRING([--enable-iostats],
[enable if you want support for per-task I/O statistics @<:@default=no@:>@]),
[want_iostats="$enableval"], [want_iostats=no])
if test x$want_iostats = xyes; then
if test x"$uname" != xLinux; then
AC_MSG_NOTICE([iostats not supported on $uname... disabling])
want_apcupsd=no
else
AC_DEFINE(IOSTATS, 1, [Define if you want support for per-task I/O statistics])
fi
fi
dnl
dnl Math
dnl
@ -823,4 +842,5 @@ $PACKAGE $VERSION configured successfully:
Imlib2: $want_imlib2
ALSA mixer: $want_alsa
apcupsd: $want_apcupsd
I/O stats: $want_iostats
EOF

View File

@ -2899,8 +2899,8 @@
(number) Basically, processes are ranked from highest to
lowest in terms of cpu usage, which is what (num)
represents. The types are: "name", "pid", "cpu", "mem",
"mem_res", "mem_vsize", and "time". There can be a max of
10 processes listed.
"mem_res", "mem_vsize", "time", "io_perc", "io_read" and
"io_write". There can be a max of 10 processes listed.
<para /></listitem>
</varlistentry>
<varlistentry>
@ -2925,6 +2925,17 @@
instead of current CPU usage
<para /></listitem>
</varlistentry>
<varlistentry>
<term>
<command>
<option>top_io</option>
</command>
<option>type, num</option>
</term>
<listitem>Same as top, except sorted by the amount of I/O the
process has done during the update interval
<para /></listitem>
</varlistentry>
<varlistentry>
<term>
<command>

View File

@ -139,6 +139,9 @@ enum {
RIGHT_SPACER
} use_spacer;
int top_cpu, top_mem, top_time;
#ifdef IOSTATS
int top_io;
#endif
static unsigned int top_name_width = 15;
int output_methods;
enum x_initialiser_state x_initialised = NO;
@ -234,6 +237,9 @@ static void print_version(void)
#ifdef APCUPSD
" * apcupsd\n"
#endif /* APCUPSD */
#ifdef IOSTATS
" * iostats\n"
#endif /* IOSTATS */
);
exit(0);
@ -936,6 +942,9 @@ static void free_text_objects(struct text_object *root, int internal)
case OBJ_top:
case OBJ_top_mem:
case OBJ_top_time:
#ifdef IOSTATS
case OBJ_top_io:
#endif
if (info.first_process && !internal) {
free_all_processes();
info.first_process = NULL;
@ -1127,8 +1136,17 @@ static int parse_top_args(const char *s, const char *arg, struct text_object *ob
} else if (strcmp(&s[3], "_time") == EQUAL) {
obj->type = OBJ_top_time;
top_time = 1;
#ifdef IOSTATS
} else if (strcmp(&s[3], "_io") == EQUAL) {
obj->type = OBJ_top_io;
top_io = 1;
#endif
} else {
#ifdef IOSTATS
ERR("Must be top, top_mem, top_time or top_io");
#else
ERR("Must be top, top_mem or top_time");
#endif
return 0;
}
@ -1152,9 +1170,22 @@ static int parse_top_args(const char *s, const char *arg, struct text_object *ob
obj->data.top.type = TOP_MEM_RES;
} else if (strcmp(buf, "mem_vsize") == EQUAL) {
obj->data.top.type = TOP_MEM_VSIZE;
#ifdef IOSTATS
} else if (strcmp(buf, "io_read") == EQUAL) {
obj->data.top.type = TOP_READ_BYTES;
} else if (strcmp(buf, "io_write") == EQUAL) {
obj->data.top.type = TOP_WRITE_BYTES;
} else if (strcmp(buf, "io_perc") == EQUAL) {
obj->data.top.type = TOP_IO_PERC;
#endif
} else {
ERR("invalid type arg for top");
#ifdef IOSTATS
ERR("must be one of: name, cpu, pid, mem, time, mem_res, mem_vsize, "
"io_read, io_write, io_perc");
#else
ERR("must be one of: name, cpu, pid, mem, time, mem_res, mem_vsize");
#endif
return 0;
}
if (n < 1 || n > 10) {
@ -1952,8 +1983,8 @@ static struct text_object *construct_text_object(const char *s,
#endif /* !__OpenBSD__ */
END
/* we have three different types of top (top, top_mem and top_time). To
* avoid having almost-same code three times, we have this special
/* we have four different types of top (top, top_mem, top_time and top_io). To
* avoid having almost-same code four times, we have this special
* handler. */
if (strncmp(s, "top", 3) == EQUAL) {
if (!parse_top_args(s, arg, obj)) {
@ -5355,8 +5386,8 @@ static void generate_text_internal(char *p, int p_max_size,
snprintf(p, p_max_size, "%i", cur->bmpx.bitrate);
}
#endif /* BMPX */
/* we have three different types of top (top, top_mem
* and top_time). To avoid having almost-same code three
/* we have four different types of top (top, top_mem,
* top_time and top_io). To avoid having almost-same code four
* times, we have this special handler. */
break;
case OBJ_top:
@ -5368,6 +5399,11 @@ static void generate_text_internal(char *p, int p_max_size,
case OBJ_top_time:
parse_top_args("top_time", obj->data.top.s, obj);
if (!needed) needed = cur->time;
#ifdef IOSTATS
case OBJ_top_io:
parse_top_args("top_io", obj->data.top.s, obj);
if (!needed) needed = cur->io;
#endif
if (needed[obj->data.top.num]) {
char *timeval;
@ -5403,6 +5439,20 @@ static void generate_text_internal(char *p, int p_max_size,
human_readable(needed[obj->data.top.num]->vsize,
p, 255);
break;
#ifdef IOSTATS
case TOP_READ_BYTES:
human_readable(needed[obj->data.top.num]->read_bytes / update_interval,
p, 255);
break;
case TOP_WRITE_BYTES:
human_readable(needed[obj->data.top.num]->write_bytes / update_interval,
p, 255);
break;
case TOP_IO_PERC:
snprintf(p, 7, "%6.2f",
needed[obj->data.top.num]->io_perc);
break;
#endif
}
}
OBJ(tail)
@ -7420,6 +7470,9 @@ static void set_default_configurations(void)
format_human_readable = 1;
top_mem = 0;
top_time = 0;
#ifdef IOSTATS
top_io = 0;
#endif
#ifdef MPD
mpd_set_host("localhost");
mpd_set_port("6600");

View File

@ -265,6 +265,9 @@ struct information {
struct process *cpu[10];
struct process *memu[10];
struct process *time[10];
#ifdef IOSTATS
struct process *io[10];
#endif
struct process *first_process;
unsigned long looped;
struct entropy_s entropy;
@ -298,6 +301,9 @@ enum {
/* defined in conky.c, needed by top.c */
extern int top_cpu, top_mem, top_time;
#ifdef IOSTATS
extern int top_io;
#endif
/* defined in conky.c, needed by top.c */
extern int cpu_separate;

View File

@ -2107,7 +2107,11 @@ void get_powerbook_batt_info(char *buffer, size_t n, int i)
void update_top(void)
{
process_find_top(info.cpu, info.memu, info.time);
process_find_top(info.cpu, info.memu, info.time
#ifdef IOSTATS
, info.io
#endif
);
info.first_process = get_first_process();
}

View File

@ -191,6 +191,9 @@ enum text_object_type {
OBJ_top,
OBJ_top_mem,
OBJ_top_time,
#ifdef IOSTATS
OBJ_top_io,
#endif
OBJ_tail,
OBJ_head,
OBJ_lines,

145
src/top.c
View File

@ -87,6 +87,10 @@ static struct process *new_process(int p)
process->time_stamp = 0;
process->previous_user_time = ULONG_MAX;
process->previous_kernel_time = ULONG_MAX;
#ifdef IOSTATS
process->previous_read_bytes = ULLONG_MAX;
process->previous_write_bytes = ULLONG_MAX;
#endif
process->counted = 1;
/* process_find_name(process); */
@ -214,13 +218,85 @@ static int process_parse_stat(struct process *process)
return 0;
}
#ifdef IOSTATS
static int process_parse_io(struct process *process)
{
static const char *read_bytes_str="read_bytes:";
static const char *write_bytes_str="write_bytes:";
char line[BUFFER_LEN] = { 0 }, filename[BUFFER_LEN];
int ps;
int rc;
char *pos, *endpos;
unsigned long long read_bytes, write_bytes;
snprintf(filename, sizeof(filename), PROCFS_TEMPLATE_IO, process->pid);
ps = open(filename, O_RDONLY);
if (ps < 0) {
/* The process must have finished in the last few jiffies!
* Or, the kernel doesn't support I/O accounting.
*/
return 1;
}
rc = read(ps, line, sizeof(line));
close(ps);
if (rc < 0) {
return 1;
}
pos = strstr(line, read_bytes_str);
if (pos == NULL) {
/* these should not happen (unless the format of the file changes) */
return 1;
}
pos += strlen(read_bytes_str);
process->read_bytes = strtoull(pos, &endpos, 10);
if (endpos == pos) {
return 1;
}
pos = strstr(line, write_bytes_str);
if (pos == NULL) {
return 1;
}
pos += strlen(write_bytes_str);
process->write_bytes = strtoull(pos, &endpos, 10);
if (endpos == pos) {
return 1;
}
if (process->previous_read_bytes == ULLONG_MAX) {
process->previous_read_bytes = process->read_bytes;
}
if (process->previous_write_bytes == ULLONG_MAX) {
process->previous_write_bytes = process->write_bytes;
}
/* store the difference of the byte counts */
read_bytes = process->read_bytes - process->previous_read_bytes;
write_bytes = process->write_bytes - process->previous_write_bytes;
/* backup the counts for next time around */
process->previous_read_bytes = process->read_bytes;
process->previous_write_bytes = process->write_bytes;
/* store only the difference here... */
process->read_bytes = read_bytes;
process->write_bytes = write_bytes;
return 0;
}
#endif
/******************************************
* Get process structure for process pid *
******************************************/
/* This function seems to hog all of the CPU time.
* I can't figure out why - it doesn't do much. */
static int calculate_cpu(struct process *process)
static int calculate_stats(struct process *process)
{
int rc;
@ -230,6 +306,12 @@ static int calculate_cpu(struct process *process)
return 1;
/* rc = process_parse_statm(process); if (rc) return 1; */
#ifdef IOSTATS
rc = process_parse_io(process);
if (rc)
return 1;
#endif
/*
* Check name against the exclusion list
*/
@ -274,7 +356,7 @@ static int update_process_table(void)
}
/* compute each process cpu usage */
calculate_cpu(p);
calculate_stats(p);
}
}
@ -395,6 +477,22 @@ inline static void calc_cpu_each(unsigned long long total)
}
}
#ifdef IOSTATS
static void calc_io_each(void)
{
struct process *p;
unsigned long long sum = 0;
for (p = first_process; p; p = p->next)
sum += p->read_bytes + p->write_bytes;
if(sum == 0)
sum = 1; /* to avoid having NANs if no I/O occured */
for (p = first_process; p; p = p->next)
p->io_perc = 100.0 * (p->read_bytes + p->write_bytes) / (float) sum;
}
#endif
/******************************************
* Find the top processes *
******************************************/
@ -446,6 +544,20 @@ static int compare_time(struct process *a, struct process *b)
return b->total_cpu_time - a->total_cpu_time;
}
#ifdef IOSTATS
/* I/O comparision function for insert_sp_element */
static int compare_io(struct process *a, struct process *b)
{
if (a->io_perc < b->io_perc) {
return 1;
} else if (a->io_perc > b->io_perc) {
return -1;
} else {
return 0;
}
}
#endif
/* insert this process into the list in a sorted fashion,
* or destroy it if it doesn't fit on the list */
static int insert_sp_element(struct sorted_process *sp_cur,
@ -524,15 +636,26 @@ static void sp_acopy(struct sorted_process *sp_head, struct process **ar, int ma
* ****************************************************************** */
void process_find_top(struct process **cpu, struct process **mem,
struct process **ptime)
struct process **ptime
#ifdef IOSTATS
, struct process **io
#endif
)
{
struct sorted_process *spc_head = NULL, *spc_tail = NULL, *spc_cur = NULL;
struct sorted_process *spm_head = NULL, *spm_tail = NULL, *spm_cur = NULL;
struct sorted_process *spt_head = NULL, *spt_tail = NULL, *spt_cur = NULL;
#ifdef IOSTATS
struct sorted_process *spi_head = NULL, *spi_tail = NULL, *spi_cur = NULL;
#endif
struct process *cur_proc = NULL;
unsigned long long total = 0;
if (!top_cpu && !top_mem && !top_time) {
if (!top_cpu && !top_mem && !top_time
#ifdef IOSTATS
&& !top_io
#endif
) {
return;
}
@ -540,6 +663,9 @@ void process_find_top(struct process **cpu, struct process **mem,
update_process_table(); /* update the table with process list */
calc_cpu_each(total); /* and then the percentage for each task */
process_cleanup(); /* cleanup list from exited processes */
#ifdef IOSTATS
calc_io_each(); /* percentage of I/O for each task */
#endif
cur_proc = first_process;
@ -559,6 +685,13 @@ void process_find_top(struct process **cpu, struct process **mem,
insert_sp_element(spt_cur, &spt_head, &spt_tail, MAX_SP,
&compare_time);
}
#ifdef IOSTATS
if (top_io) {
spi_cur = malloc_sp(cur_proc);
insert_sp_element(spi_cur, &spi_head, &spi_tail, MAX_SP,
&compare_io);
}
#endif
cur_proc = cur_proc->next;
}
@ -568,4 +701,8 @@ void process_find_top(struct process **cpu, struct process **mem,
sp_acopy(spm_head, mem, MAX_SP);
if (top_time)
sp_acopy(spt_head, ptime, MAX_SP);
#ifdef IOSTATS
if (top_io)
sp_acopy(spi_head, io,MAX_SP);
#endif
}

View File

@ -70,6 +70,7 @@
#define PROCFS_TEMPLATE "/proc/%d/stat"
#define PROCFS_TEMPLATE_MEM "/proc/%d/statm"
#define PROCFS_TEMPLATE_IO "/proc/%d/io"
#define PROCFS_CMDLINE_TEMPLATE "/proc/%d/cmdline"
#define MAX_SP 10 // number of elements to sort
@ -80,7 +81,12 @@ enum top_field {
TOP_MEM,
TOP_TIME,
TOP_MEM_RES,
TOP_MEM_VSIZE
TOP_MEM_VSIZE,
#ifdef IOSTATS
TOP_READ_BYTES,
TOP_WRITE_BYTES,
TOP_IO_PERC
#endif
};
/******************************************
@ -103,6 +109,13 @@ struct process {
unsigned long total_cpu_time;
unsigned int vsize;
unsigned int rss;
#ifdef IOSTATS
unsigned long long read_bytes;
unsigned long long previous_read_bytes;
unsigned long long write_bytes;
unsigned long long previous_write_bytes;
float io_perc;
#endif
unsigned int time_stamp;
unsigned int counted;
unsigned int changed;
@ -116,6 +129,10 @@ struct sorted_process {
};
/* Pointer to head of process list */
void process_find_top(struct process **, struct process **, struct process **);
void process_find_top(struct process **, struct process **, struct process **
#ifdef IOSTATS
, struct process **
#endif
);
#endif /* _top_h_ */