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:
parent
71f6880b01
commit
c0d1c313e9
@ -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
|
||||
|
@ -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>
|
||||
|
61
src/conky.c
61
src/conky.c
@ -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");
|
||||
|
@ -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;
|
||||
|
@ -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();
|
||||
}
|
||||
|
||||
|
@ -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
145
src/top.c
@ -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
|
||||
}
|
||||
|
21
src/top.h
21
src/top.h
@ -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_ */
|
||||
|
Loading…
Reference in New Issue
Block a user