diff options
Diffstat (limited to 'libps/spec.c')
-rw-r--r-- | libps/spec.c | 756 |
1 files changed, 756 insertions, 0 deletions
diff --git a/libps/spec.c b/libps/spec.c new file mode 100644 index 00000000..c3e68f95 --- /dev/null +++ b/libps/spec.c @@ -0,0 +1,756 @@ +/* Access, formatting, & comparison routines for printing process info. + + Copyright (C) 1995 Free Software Foundation, Inc. + + Written by Miles Bader <miles@gnu.ai.mit.edu> + + This program is free software; you can redistribute it and/or + modify it under the terms of the GNU General Public License as + published by the Free Software Foundation; either version 2, or (at + your option) any later version. + + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ + +#include <hurd.h> +#include <stdio.h> +#include <stdlib.h> +#include <assert.h> +#include <pwd.h> + +#include "ps.h" +#include "pshost.h" +#include "common.h" + +/* ---------------------------------------------------------------- */ +/* Getter definitions */ + +typedef void (*vf)(); + +static int +ps_get_pid(proc_stat_t ps) +{ + return proc_stat_pid(ps); +} +struct ps_getter ps_pid_getter = +{"pid", PSTAT_PID, (vf) ps_get_pid}; + +static int +ps_get_thread_index(proc_stat_t ps) +{ + return proc_stat_thread_index(ps); +} +struct ps_getter ps_thread_index_getter = +{"thread_index", PSTAT_THREAD, (vf) ps_get_thread_index}; + +static int +ps_get_owner(proc_stat_t ps) +{ + return proc_stat_info(ps)->owner; +} +struct ps_getter ps_owner_getter = +{"owner", PSTAT_INFO, (vf) ps_get_owner}; + +static int +ps_get_ppid(proc_stat_t ps) +{ + return proc_stat_info(ps)->ppid; +} +struct ps_getter ps_ppid_getter = +{"ppid", PSTAT_INFO, (vf) ps_get_ppid}; + +static int +ps_get_pgrp(proc_stat_t ps) +{ + return proc_stat_info(ps)->pgrp; +} +struct ps_getter ps_pgrp_getter = +{"pgrp", PSTAT_INFO, (vf) ps_get_pgrp}; + +static int +ps_get_session(proc_stat_t ps) +{ + return proc_stat_info(ps)->session; +} +struct ps_getter ps_session_getter = +{"session", PSTAT_INFO, (vf) ps_get_session}; + +static int +ps_get_login_col(proc_stat_t ps) +{ + return proc_stat_info(ps)->logincollection; +} +struct ps_getter ps_login_col_getter = +{"login_col", PSTAT_INFO, (vf) ps_get_login_col}; + +static int +ps_get_num_threads(proc_stat_t ps) +{ + return proc_stat_num_threads(ps); +} +struct ps_getter ps_num_threads_getter = +{"num_threads", PSTAT_NUM_THREADS, (vf)ps_get_num_threads}; + +static void +ps_get_args(proc_stat_t ps, char **args_p, int *args_len_p) +{ + *args_p = proc_stat_args(ps); + *args_len_p = proc_stat_args_len(ps); +} +struct ps_getter ps_args_getter = +{"args", PSTAT_ARGS, ps_get_args}; + +static int +ps_get_state(proc_stat_t ps) +{ + return proc_stat_state(ps); +} +struct ps_getter ps_state_getter = +{"state", PSTAT_STATE, (vf) ps_get_state}; + +static int +ps_get_vsize(proc_stat_t ps) +{ + return proc_stat_info(ps)->taskinfo.virtual_size; +} +struct ps_getter ps_vsize_getter = +{"vsize", PSTAT_INFO, (vf) ps_get_vsize}; + +static int +ps_get_rsize(proc_stat_t ps) +{ + return proc_stat_info(ps)->taskinfo.resident_size; +} +struct ps_getter ps_rsize_getter = +{"rsize", PSTAT_INFO, (vf) ps_get_rsize}; + +static int +ps_get_cur_priority(proc_stat_t ps) +{ + return proc_stat_thread_sched_info(ps)->cur_priority; +} +struct ps_getter ps_cur_priority_getter = +{"cur_priority", PSTAT_THREAD_INFO, (vf) ps_get_cur_priority}; + +static int +ps_get_base_priority(proc_stat_t ps) +{ + return proc_stat_thread_sched_info(ps)->base_priority; +} +struct ps_getter ps_base_priority_getter = +{"base_priority", PSTAT_THREAD_INFO, (vf) ps_get_base_priority}; + +static int +ps_get_max_priority(proc_stat_t ps) +{ + return proc_stat_thread_sched_info(ps)->max_priority; +} +struct ps_getter ps_max_priority_getter = +{"max_priority", PSTAT_THREAD_INFO, (vf) ps_get_max_priority}; + +static void +ps_get_usr_time(proc_stat_t ps, time_value_t * tv_out) +{ + *tv_out = proc_stat_thread_basic_info(ps)->user_time; +} +struct ps_getter ps_usr_time_getter = +{"usr_time", PSTAT_THREAD_INFO, ps_get_usr_time}; + +static void +ps_get_sys_time(proc_stat_t ps, time_value_t * tv_out) +{ + *tv_out = proc_stat_thread_basic_info(ps)->system_time; +} +struct ps_getter ps_sys_time_getter = +{"sys_time", PSTAT_THREAD_INFO, ps_get_sys_time}; + +static void +ps_get_tot_time(proc_stat_t ps, time_value_t * tv_out) +{ + *tv_out = proc_stat_thread_basic_info(ps)->user_time; + time_value_add(tv_out, &proc_stat_thread_basic_info(ps)->system_time); +} +struct ps_getter ps_tot_time_getter = +{"tot_time", PSTAT_THREAD_INFO, ps_get_tot_time}; + +static float +ps_get_rmem_frac(proc_stat_t ps) +{ + static int mem_size = 0; + + if (mem_size == 0) + { + host_basic_info_t info; + error_t err = ps_host_basic_info(&info); + if (err == 0) + mem_size = info->memory_size; + } + + if (mem_size > 0) + return (float)proc_stat_info(ps)->taskinfo.resident_size / (float)mem_size; + else + return 0.0; +} +struct ps_getter ps_rmem_frac_getter = +{"rmem_frac", PSTAT_INFO, (vf) ps_get_rmem_frac}; + +static float +ps_get_cpu_frac(proc_stat_t ps) +{ + return (float) proc_stat_thread_basic_info(ps)->cpu_usage + / (float) TH_USAGE_SCALE; +} +struct ps_getter ps_cpu_frac_getter = +{"cpu_frac", PSTAT_THREAD_INFO, (vf) ps_get_cpu_frac}; + +static int +ps_get_sleep(proc_stat_t ps) +{ + return proc_stat_thread_basic_info(ps)->sleep_time; +} +struct ps_getter ps_sleep_getter = +{"sleep", PSTAT_THREAD_INFO, (vf) ps_get_sleep}; + +static void +ps_get_tty_name(proc_stat_t ps, char **str_p, int *len_p) +{ + *str_p = proc_stat_tty_name(ps); + if (*str_p != NULL) + *len_p = strlen(*str_p) + 1; +} +struct ps_getter ps_tty_name_getter = +{"tty_name", PSTAT_TTY_NAME, ps_get_tty_name}; + +static int +ps_get_page_faults(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->faults; +} +struct ps_getter ps_page_faults_getter = +{"page_faults", PSTAT_TASK_EVENTS_INFO, (vf) ps_get_page_faults}; + +static int +ps_get_cow_faults(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->cow_faults; +} +struct ps_getter ps_cow_faults_getter = +{"cow_faults", PSTAT_TASK_EVENTS_INFO, (vf) ps_get_cow_faults}; + +static int +ps_get_pageins(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->pageins; +} +struct ps_getter ps_pageins_getter = +{"pageins", PSTAT_TASK_EVENTS_INFO, (vf) ps_get_pageins}; + +static int +ps_get_msgs_sent(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->messages_sent; +} +struct ps_getter ps_msgs_sent_getter = +{"msgs_sent", PSTAT_TASK_EVENTS_INFO, (vf) ps_get_msgs_sent}; + +static int +ps_get_msgs_rcvd(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->messages_received; +} +struct ps_getter ps_msgs_rcvd_getter = +{"msgs_rcvd", PSTAT_TASK_EVENTS_INFO, (vf) ps_get_msgs_rcvd}; + +static int +ps_get_zero_fills(proc_stat_t ps) +{ + return proc_stat_task_events_info(ps)->zero_fills; +} +struct ps_getter ps_zero_fills_getter = +{"zero_fills", PSTAT_TASK_EVENTS_INFO, (vf) ps_get_zero_fills}; + +/* ---------------------------------------------------------------- */ +/* some printing functions */ + +/* G() is a helpful macro that just returns the getter G's access function + cast into a function pointer returning TYPE, as how the function should be + called varies depending on the getter */ +#define G(g,type)((type (*)())ps_getter_function(g)) + +error_t +ps_emit_int(proc_stat_t ps, ps_getter_t getter, int width, FILE *stream, int *count) +{ + return ps_write_int_field(G(getter, int)(ps), width, stream, count); +} + +error_t +ps_emit_num_blocks(proc_stat_t ps, ps_getter_t getter, int width, FILE + *stream, int *count) +{ + char buf[20]; + sprintf(buf, "%ld", G(getter, int)(ps) / 1024); + return ps_write_field(buf, width, stream, count); +} + +int +sprint_frac_value(char *buf, + int value, int min_value_len, + int frac, int frac_scale, + int width) +{ + int value_len; + int frac_len; + + if (value >= 100) /* the integer part */ + value_len = 3; + else if (value >= 10) + value_len = 2; + else + value_len = 1; + + while (value_len < min_value_len--) + *buf++ = '0'; + + for (frac_len = frac_scale + ; frac_len > 0 && (width < value_len + 1 + frac_len || frac % 10 == 0) + ; frac_len--) + frac /= 10; + + if (frac_len > 0) + sprintf(buf, "%ld.%0*ld", value, frac_len, frac); + else + sprintf(buf, "%ld", value); + + return strlen(buf); +} + +error_t +ps_emit_percent(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char buf[20]; + float perc = G(getter, float)(ps) * 100; + + if (width == 0) + sprintf(buf, "%g", perc); + else if (ABS(width) > 3) + sprintf(buf, "%.*f", ABS(width) - 3, perc); + else + sprintf(buf, "%d", (int) perc); + + return ps_write_field(buf, width, stream, count); +} + +/* prints its value nicely */ +error_t +ps_emit_nice_int(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char buf[20]; + int value = G(getter, int)(ps); + char *sfx = " KMG"; + int frac = 0; + + while (value >= 1024) + { + frac = ((value & 0x3FF) * 1000) >> 10; + value >>= 10; + sfx++; + } + + sprintf(buf + sprint_frac_value(buf, value, 1, frac, 3, ABS(width) - 1), + "%c", *sfx); + + return ps_write_field(buf, width, stream, count); +} + +#define MINUTE 60 +#define HOUR (60*MINUTE) +#define DAY (24*HOUR) +#define WEEK (7*DAY) + +static int +sprint_long_time(char *buf, int seconds, int width) +{ + char *p = buf; + struct tscale + { + int length; + char *sfx; + char *short_sfx; + } + time_scales[] = + { + { WEEK, " week", "wk"} , + { DAY, " day", "dy"} , + { HOUR, " hour", "hr"} , + { MINUTE," min", "m"} , + { 0} + }; + struct tscale *ts = time_scales; + + while (ts->length > 0 && width > 0) + { + if (ts->length < seconds) + { + int len; + int num = seconds / ts->length; + seconds %= ts->length; + sprintf(p, "%d%s", num, ts->sfx); + len = strlen(p); + width -= len; + if (width < 0 && p > buf) + break; + p += len; + } + ts++; + } + + *p = '\0'; + + return p - buf; +} + +error_t +ps_emit_nice_seconds(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char buf[20]; + time_value_t tv; + + G(getter, int)(ps, &tv); + + if (tv.seconds == 0) + { + if (tv.microseconds < 500) + sprintf(buf, "%dus", tv.microseconds); + else + strcpy(buf + + sprint_frac_value(buf, + tv.microseconds / 1000, 1, + tv.microseconds % 1000, 3, + ABS(width) - 2), + "ms"); + } + else if (tv.seconds < MINUTE) + sprint_frac_value(buf, tv.seconds, 1, tv.microseconds, 6, ABS(width)); + else if (tv.seconds < HOUR) + { + /* 0:00.00... */ + int min_len; + sprintf(buf, "%d:", tv.seconds / 60); + min_len = strlen(buf); + sprint_frac_value(buf + min_len, + tv.seconds % 60, 2, + tv.microseconds, 6, + ABS(width) - min_len); + } + else + sprint_long_time(buf, tv.seconds, width); + + return ps_write_field(buf, width, stream, count); +} + +static int +append_fraction(char *buf, int frac, int digits, int width) +{ + int slen = strlen(buf); + int left = width - strlen(buf); + if (left > 1) + { + buf[slen] = '.'; + left--; + while (digits > left) + frac /= 10, digits--; + sprintf(buf + slen + 1, "%0*d", digits, frac); + return slen + 1 + digits; + } + else + return slen; +} + +error_t +ps_emit_seconds(proc_stat_t ps, ps_getter_t getter, int width, FILE *stream, + int *count) +{ + int max = (width == 0 ? 999 : ABS(width)); + char buf[20]; + time_value_t tv; + + G(getter, void)(ps, &tv); + + if (tv.seconds > DAY) + sprint_long_time(buf, tv.seconds, max); + else if (tv.seconds > HOUR) + if (max >= 8) + { + /* 0:00:00.00... */ + sprintf(buf, "%2d:%02d:%02d", + tv.seconds / HOUR, + (tv.seconds % HOUR) / MINUTE, (tv.seconds % MINUTE)); + append_fraction(buf, tv.microseconds, 6, max); + } + else + sprint_long_time(buf, tv.seconds, max); + else if (max >= 5 || tv.seconds > MINUTE) + { + /* 0:00.00... */ + sprintf(buf, "%2d:%02d", tv.seconds / MINUTE, tv.seconds % MINUTE); + append_fraction(buf, tv.microseconds, 6, max); + } + else + sprint_frac_value(buf, tv.seconds, 1, tv.microseconds, 6, max); + + return ps_write_field(buf, width, stream, count); +} + +error_t +ps_emit_user(proc_stat_t ps, ps_getter_t getter, int width, FILE *stream, int *count) +{ + int uid = G(getter, int)(ps); + if (uid < 0) + return ps_write_padding(0, width, stream, count); + else + { + struct passwd *pw = getpwuid(uid); + if (pw == NULL) + return ps_write_int_field(uid, width, stream, count); + else + return ps_write_field(pw->pw_name, width, stream, count); + } +} + +/* prints a string with embedded nuls as spaces */ +error_t +ps_emit_string0(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char *s0, *p, *q; + int s0len; + int fwidth = ABS(width); + char static_buf[200]; + char *buf = static_buf; + + G(getter, void)(ps, &s0, &s0len); + + if (s0 == NULL) + *buf = '\0'; + else + { + if (s0len > sizeof static_buf) + { + buf = malloc(s0len + 1); + if (buf == NULL) + return ENOMEM; + } + + if (fwidth == 0 || fwidth > s0len) + fwidth = s0len; + + for (p = buf, q = s0; fwidth-- > 0; p++, q++) + { + int ch = *q; + *p = (ch == '\0' ? ' ' : ch); + } + if (q > s0 && *(q - 1) == '\0') + *--p = '\0'; + else + *p = '\0'; + } + + { + error_t err = ps_write_field(buf, width, stream, count); + if (buf != static_buf) + free(buf); + return err; + } +} + +error_t +ps_emit_string(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char *str; + int len; + + G(getter, void)(ps, &str, &len); + + if (str == NULL) + str = ""; + else if (width != 0 && len > ABS(width)) + str[ABS(width)] = '\0'; + + return ps_write_field(str, width, stream, count); +} + +error_t +ps_emit_tty_name(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + struct tty_abbrev + { + char *pfx, *subst; + } + abbrevs[] = + { + { "/tmp/console", "oc" }, /* temp hack */ + { "/dev/console", "co"}, + { "/dev/tty", ""}, + { "/dev/pty", ""}, + { "/dev/", ""}, + { 0 } + }; + char buf[20]; + char *str; + int len; + + G(getter, void)(ps, &str, &len); + + if (str == NULL || len == 0) + str = "-"; + else + { + struct tty_abbrev *abbrev = abbrevs; + while (abbrev->pfx != NULL) + { + int pfx_len = strlen(abbrev->pfx); + if (strncmp(str, abbrev->pfx, pfx_len) == 0) + { + strcpy(buf, abbrev->subst); + strcat(buf, str + pfx_len); + str = buf; + break; + } + else + abbrev++; + } + } + + return ps_write_field(str, width, stream, count); +} + +error_t +ps_emit_state(proc_stat_t ps, ps_getter_t getter, + int width, FILE *stream, int *count) +{ + char *tags; + int state = G(getter, int)(ps); + char buf[20], *p = buf; + + /* turn off seemingly bogus or annoying flags */ + state &= ~PSTAT_STATE_SWAPPED; + + /* If any thread is running, don't mention sleeping or idle threads -- + presumably *some* work is getting done... */ + if (state & PSTAT_STATE_RUNNING) + state &= ~(PSTAT_STATE_SLEEPING | PSTAT_STATE_IDLE); + if (state & PSTAT_STATE_IDLE) + state &= ~PSTAT_STATE_SLEEPING; + + for (tags = proc_stat_state_tags + ; state != 0 && *tags != '\0' + ; state >>= 1, tags++) + if (state & 1) + *p++ = *tags; + + *p = '\0'; + + return ps_write_field(buf, width, stream, count); +} + +/* ---------------------------------------------------------------- */ +/* comparison functions */ + +int +ps_cmp_ints(proc_stat_t ps1, proc_stat_t ps2, ps_getter_t getter) +{ + int (*gf)() = G(getter, int); + int v1 = gf(ps1), v2 = gf(ps2); + return v1 == v2 ? 0 : v1 < v2 ? -1 : 1; +} + +int +ps_cmp_floats(proc_stat_t ps1, proc_stat_t ps2, ps_getter_t getter) +{ + float (*gf)() = G(getter, float); + float v1 = gf(ps1), v2 = gf(ps2); + return v1 == v2 ? 0 : v1 < v2 ? -1 : 1; +} + +int +ps_cmp_strings(proc_stat_t ps1, proc_stat_t ps2, ps_getter_t getter) +{ + void (*gf)() = G(getter, void); + char *s1, *s2; + int s1len, s2len; + + /* Get both strings */ + gf(ps1, &s1, &s1len); + gf(ps2, &s2, &s2len); + + if (s1 == NULL) + if (s2 == NULL) + return 0; + else + return -1; + else + if (s2 == NULL) + return 1; + else + return strncmp(s1, s2, MIN(s1len, s2len)); +} + +/* ---------------------------------------------------------------- */ + +ps_fmt_spec_t +find_ps_fmt_spec(char *name, ps_fmt_spec_t specs) +{ + while (!ps_fmt_spec_is_end(specs)) + if (strcasecmp(ps_fmt_spec_name(specs), name) == 0) + return specs; + else + specs++; + return NULL; +} + +/* ---------------------------------------------------------------- */ + +struct ps_fmt_spec ps_std_fmt_specs[] = +{ + {"PID", &ps_pid_getter, ps_emit_int, ps_cmp_ints, -5}, + {"TH#", &ps_thread_index_getter,ps_emit_int, ps_cmp_ints, -2}, + {"PPID", &ps_ppid_getter, ps_emit_int, ps_cmp_ints, -5}, + {"UID", &ps_owner_getter, ps_emit_int, ps_cmp_ints, -5}, + {"NTh", &ps_num_threads_getter, ps_emit_int, ps_cmp_ints, -2}, + {"PGrp", &ps_pgrp_getter, ps_emit_int, ps_cmp_ints, -5}, + {"User", &ps_owner_getter, ps_emit_user, ps_cmp_ints, 6}, + {"Sess", &ps_session_getter, ps_emit_int, ps_cmp_ints, -5}, + {"LColl", &ps_login_col_getter, ps_emit_int, ps_cmp_ints, -5}, + {"Args", &ps_args_getter, ps_emit_string0, ps_cmp_strings, 0}, + {"Time", &ps_tot_time_getter, ps_emit_seconds, ps_cmp_ints, -8}, + {"UTime", &ps_usr_time_getter, ps_emit_seconds, ps_cmp_ints, -8}, + {"STime", &ps_sys_time_getter, ps_emit_seconds, ps_cmp_ints, -8}, + {"VSize", &ps_vsize_getter, ps_emit_nice_int, ps_cmp_ints, -5}, + {"RSize", &ps_rsize_getter, ps_emit_nice_int, ps_cmp_ints, -5}, + {"Pri", &ps_cur_priority_getter,ps_emit_int, ps_cmp_ints, -2}, + {"BPri", &ps_base_priority_getter,ps_emit_int, ps_cmp_ints, -2}, + {"MPri", &ps_max_priority_getter,ps_emit_int, ps_cmp_ints, -2}, + {"%Mem", &ps_rmem_frac_getter, ps_emit_percent, ps_cmp_floats, -4}, + {"%CPU", &ps_cpu_frac_getter, ps_emit_percent, ps_cmp_floats, -4}, + {"State", &ps_state_getter, ps_emit_state, NULL, 4}, + {"Sleep", &ps_sleep_getter, ps_emit_int, ps_cmp_ints, -2}, + {"TTY", &ps_tty_name_getter, ps_emit_tty_name, ps_cmp_strings, 2}, + {"PgFlts", &ps_page_faults_getter, ps_emit_int, ps_cmp_ints, -5}, + {"COWFlts",&ps_cow_faults_getter, ps_emit_int, ps_cmp_ints, -5}, + {"PgIns", &ps_pageins_getter, ps_emit_int, ps_cmp_ints, -5}, + {"MsgsIn", &ps_msgs_rcvd_getter, ps_emit_int, ps_cmp_ints, -5}, + {"MsgsOut",&ps_msgs_sent_getter, ps_emit_int, ps_cmp_ints, -5}, + {"ZFills", &ps_zero_fills_getter, ps_emit_int, ps_cmp_ints, -5}, + {0} +}; |