1
0
Files
irix-657m-src/eoe/cmd/topwait/topwait.c
2022-09-29 17:59:04 +03:00

648 lines
13 KiB
C

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <signal.h>
#include <unistd.h>
#include <bstring.h>
#include <sys/time.h>
#include <sys/par.h>
#include <sys/utsname.h>
#include <sys/types.h>
#include <sys/syssgi.h>
#include "data.h"
#include "wait.h"
#include "term.h"
#include "freelist.h"
#include "top.h"
static const char *topwait_help_title[] = {
"Topwait version 1.0",
"",
"A top synchronization objects display for Unix",
NULL
};
static const char *topwait_help_text[] = {
"t\t- show only objects of specified type (all, sv, mutex, mrlock, sema)",
NULL
};
void *topwait_get_first(top_state *ts);
void *topwait_get_next(top_state *ts, void*);
static void topwait_zero_interval_counters(top_state *ts);
static int topwait_display_objs(top_state *ts, int lineno);
static int topwait_display_stats(top_state *ts, int lineno);
static int topwait_display_help_title(top_state *ts, int lineno);
static int topwait_display_help_text(top_state *ts, int lineno);
static int topwait_handle_event(top_state *ts, tstamp_event_entry_t*);
static int topwait_handle_keypress(top_state *ts, int ch);
static sort_order_proto(order_load);
static sort_order_proto(order_swtch);
static sort_order_t topwait_sort_orders[] = {
"load", order_load,
"switch", order_swtch,
NULL, NULL
};
static top_impl_t topwait_impl = {
NULL, /* topwait_destroy */
NULL, /* topwait_usage */
topwait_sort_orders,
"task",
NULL, /* topwait_getopts */
topwait_get_first,
topwait_get_next,
topwait_zero_interval_counters,
topwait_display_objs,
topwait_display_stats,
topwait_display_help_title,
topwait_display_help_text,
topwait_handle_event,
topwait_handle_keypress,
NULL, /* topwait_handle_option */
NULL /* topwait_init */
};
typedef struct wait_lock_type_s {
const char *wt_name;
int wt_type;
} wait_lock_type_t;
#define SHOW_ALL_TYPES 0
static wait_lock_type_t lock_types[] = {
"all", SHOW_ALL_TYPES,
"sv", SVWAIT,
"mutex", MUTEXWAIT,
"mrlock", MRWAIT,
"sema", SEMAWAIT,
NULL, NULL
};
#define UNPACK_WAIT_FLAGS(ev, f) { f = (int)((ev->qual[1])>>32); }
#define STAT_NLINES 3
static wait_state ws;
static freelist wp_freelist, wl_freelist;
static int show_type = SHOW_ALL_TYPES;
static char title[160];
static double cycle_rate;
static void pid_leaving_state(wait_pid_state *wp, tstamp_event_entry_t *ev);
static int set_show_type(const char *type);
FREELIST_IMPL_FUNCS(wait_pid_state, wp)
FREELIST_IMPL_FUNCS(wait_lock_state, wl)
static void
wl_up(wait_lock_state *wl, uint64_t tstamp)
{
wl->wl_load++;
wl->wl_nswtch++;
assert(wl->wl_load > 0);
if (wl->wl_load == 1)
wl->wl_wait_when = tstamp;
if (wl->wl_load > wl->wl_max_load)
wl->wl_max_load = wl->wl_load;
assert(wl->wl_max_load >= 1);
}
static void
wl_down(wait_lock_state *wl, uint64_t tstamp)
{
assert(wl->wl_load > 0);
wl->wl_load--;
if (wl->wl_load == 0)
wl->wl_wait_total += (tstamp - wl->wl_wait_when);
}
#define PID_ENTER_STATE(wpv, state, ev) \
{ pid_leaving_state(wpv, ev); \
(wpv) -> wp_tstamp = (ev) -> tstamp; \
ws.ws_ ## state ++; wpv -> wp_reason = wp_ ## state; }
static void wait_block(tstamp_event_entry_t *ev)
{
pid_t pid = (pid_t) ev->qual[0];
int flags;
hashable *hash;
wait_pid_state *wp;
ws.ws_nswtch.total++;
if ((hash = htab_find(&ws.ws_pids, HASH_PID(pid), pid)) != NULL)
wp = HASHABLE_TO_WP(hash);
else {
wp = wp_alloc(&wp_freelist);
wp->wp_pid = pid;
wp->wp_reason = wp_running;
wp->wp_tstamp = ev->tstamp;
wp->wp_locking = NULL;
ws.ws_running++;
htab_insert(&ws.ws_pids, HASH_PID(pid), pid,
WP_TO_HASHABLE(wp));
}
UNPACK_WAIT_FLAGS(ev, flags);
if (flags & RESCHED_LOCK) {
wait_lock_state *wl;
uint64_t lock = ev->qual[2];
/* swtch event logging race condition.
*/
if (lock == 0)
return;
ws.ws_nswtch.wait++;
if ((hash = htab_find(&ws.ws_locks, HASH_LOCK(lock),
lock)) != NULL)
wl = HASHABLE_TO_WL(hash);
else {
wl = wl_alloc(&wl_freelist);
wl->wl_addr = lock;
wl->wl_type = flags;
wl->wl_load = 0;
wl->wl_max_load = 0;
wl->wl_nswtch = 0;
wl->wl_wait_total = 0;
htab_insert(&ws.ws_locks, HASH_LOCK(lock), lock,
WL_TO_HASHABLE(wl));
wl->wl_name[0] = '\0';
if (ev->jumbocnt > 0) {
const tstamp_event_resched_data_t* rdp =
(const tstamp_event_resched_data_t*) ev->qual;
if (rdp->wchanname[0])
strncpy(wl->wl_name, rdp->wchanname,
16);
}
}
#if 0
if (wp->wp_tstamp > ev->tstamp) {
/* If this event becomes visible
after the process has already switched
states, we should remain in the old state,
but properly credit the blocking time to
the lock.
*/
wl_up(wl, ev->tstamp);
wl_down(wl, wp->wp_tstamp);
}
else {
#endif
wl_up(wl, ev->tstamp);
PID_ENTER_STATE(wp, waiting, ev);
wp->wp_locking = wl;
#if 0
}
#endif
}
else {
switch (flags) {
case MUSTRUNCPU: /* switching cpus */
case GANGWAIT: /* waiting for gang to run */
case RESCHED_P: /* preempted in user mode */
case RESCHED_KP: /* preempted in kernel mode */
ws.ws_nswtch.preempt++;
PID_ENTER_STATE(wp, preempted, ev);
break;
case RESCHED_S: /* stopped by signal */
PID_ENTER_STATE(wp, stopped, ev);
break;
case RESCHED_Y: /* voluntary yield */
ws.ws_nswtch.yield++;
PID_ENTER_STATE(wp, yielding, ev);
break;
case RESCHED_D: /* died */
PID_ENTER_STATE(wp, dead, ev);
break;
default:
PID_ENTER_STATE(wp, unknown, ev);
break;
}
}
}
static void
wp_unwait(wait_pid_state *wp, tstamp_event_entry_t *ev)
{
wait_lock_state *wl;
wl = wp->wp_locking;
wl_down(wl, ev->tstamp);
}
static void
pid_leaving_state(wait_pid_state *wp, tstamp_event_entry_t *ev)
{
#define LEAVE_STATE(s) case wp_ ## s : ws.ws_ ## s --; break
switch (wp->wp_reason) {
case wp_waiting:
wp_unwait(wp, ev);
break;
}
switch (wp->wp_reason) {
LEAVE_STATE(running);
LEAVE_STATE(preempted);
LEAVE_STATE(yielding);
LEAVE_STATE(waiting);
LEAVE_STATE(stopped);
LEAVE_STATE(dead);
LEAVE_STATE(unknown);
default:
#ifndef NDEBUG
abort();
#endif /* !NDEBUG */
break;
}
#undef LEAVE_STATE
}
static void wait_unblock(tstamp_event_entry_t *ev)
{
pid_t pid = (pid_t) ev->qual[0];
wait_pid_state *wp;
hashable *hash;
if ((hash = htab_find(&ws.ws_pids, HASH_PID(pid), pid)) == NULL)
return; /* XXX assume running */
wp = HASHABLE_TO_WP(hash);
PID_ENTER_STATE(wp, preempted, ev);
}
static void wait_run(tstamp_event_entry_t *ev)
{
pid_t pid = (pid_t) ev->qual[0];
wait_pid_state *wp;
hashable *hash;
if ((hash = htab_find(&ws.ws_pids, HASH_PID(pid), pid)) == NULL)
return; /* XXX assume running */
wp = HASHABLE_TO_WP(hash);
PID_ENTER_STATE(wp, running, ev);
}
static void
wait_exit(tstamp_event_entry_t *ev)
{
pid_t pid = (pid_t) ev->qual[0];
wait_pid_state *wp;
hashable *hash;
if ((hash = htab_find(&ws.ws_pids, HASH_PID(pid), pid)) == NULL)
return; /* XXX missed one */
wp = HASHABLE_TO_WP(hash);
PID_ENTER_STATE(wp, dead, ev);
htab_remove(&ws.ws_pids, HASH_PID(pid), hash);
wp_free(&wp_freelist, wp);
}
/*ARGSUSED*/
int topwait_handle_event(top_state *ts, tstamp_event_entry_t *ev)
{
switch (ev->evt) {
case TSTAMP_EV_EXIT: /* process exit */
wait_exit(ev);
break;
case EVENT_TASK_STATECHANGE: /* task suspend */
wait_block(ev);
break;
case TSTAMP_EV_EODISP: /* dispatch to runq */
wait_unblock(ev);
break;
case TSTAMP_EV_EOSWITCH:
case TSTAMP_EV_EOSWITCH_RTPRI: /* switch in (runq to processor) */
wait_run(ev);
break;
default:
return 0;
}
return 1;
}
static void
wait_init(wait_state *ws)
{
htab_init(&ws->ws_pids, N_PID_BUCKETS);
htab_init(&ws->ws_locks, N_LOCK_BUCKETS);
ws->ws_running = 0;
ws->ws_preempted = 0;
ws->ws_yielding = 0;
ws->ws_waiting = 0;
ws->ws_stopped = 0;
ws->ws_dead = 0;
ws->ws_unknown = 0;
}
static sort_order_proto(order_load)
{
const wait_lock_state *wla, *wlb;
wla = *(wait_lock_state**)a;
wlb = *(wait_lock_state**)b;
return (wlb->wl_max_load - wla->wl_max_load);
}
static sort_order_proto(order_swtch)
{
const wait_lock_state *wla, *wlb;
wla = *(wait_lock_state**)a;
wlb = *(wait_lock_state**)b;
return (wlb->wl_nswtch - wla->wl_nswtch);
}
static filter_proto(wait_filter)
{
const wait_lock_state *wl;
wl = *(wait_lock_state**)obj;
if (wl->wl_max_load == 0)
return 0;
if (show_type == SHOW_ALL_TYPES)
return 1;
else
return (wl->wl_type == show_type);
}
/*ARGSUSED*/
void*
topwait_get_first(top_state *ts)
{
hashable *wl_hash;
wait_lock_state *wl;
wl_hash = htab_begin(&ws.ws_locks);
if (wl_hash == NULL)
return NULL;
wl = HASHABLE_TO_WL(wl_hash);
return wl;
}
/*ARGSUSED*/
void*
topwait_get_next(top_state *ts, void *last)
{
hashable *wl_hash;
wait_lock_state *wl;
wl = (wait_lock_state*) last;
wl_hash = WL_TO_HASHABLE(wl);
wl_hash = htab_next(&ws.ws_locks, HASH_LOCK(wl->wl_addr), wl_hash);
if (wl_hash == NULL)
return NULL;
wl = HASHABLE_TO_WL(wl_hash);
return wl;
}
static int
set_show_type(const char *type)
{
wait_lock_type_t *wt;
for (wt = lock_types; wt->wt_name != NULL; wt++)
if (!strcmp(wt->wt_name, type))
break;
if (wt->wt_name != NULL) {
show_type = wt->wt_type;
return 1;
}
else
return 0;
}
static void
display_init(void)
{
sprintf(title, "%16s %16s %7s %4s %6s %7s",
"WCHAN",
"NAME",
"TYPE",
"LOAD",
"%SWTCH",
"SWT/SEC");
}
int
topwait_display_help_title(top_state *ts, int line_start)
{
return term_draw_lines(top_term(ts), line_start, topwait_help_title);
}
int
topwait_display_help_text(top_state *ts, int line_start)
{
return term_draw_lines(top_term(ts), line_start, topwait_help_text);
}
int
topwait_display_stats(top_state *ts, int line_start)
{
int line;
char linebuf[160];
int nprocs = ws.ws_preempted + ws.ws_yielding + ws.ws_waiting
+ ws.ws_stopped + ws.ws_unknown;
line = line_start;
#define proc_percent(type) int_percent(ws.ws_ ## type, nprocs)
sprintf(linebuf, "%d processes: %d%% (%d) preempt, %d%% (%d) yield, "
"%d%% (%d) wait,",
nprocs,
proc_percent(preempted), ws.ws_preempted,
proc_percent(yielding), ws.ws_yielding,
proc_percent(waiting), ws.ws_waiting);
term_draw_line(top_term(ts), line++, linebuf);
sprintf(linebuf, " %d%% (%d) stop, %d%% (%d) unk",
proc_percent(stopped), ws.ws_stopped,
proc_percent(unknown), ws.ws_unknown);
term_draw_line(top_term(ts), line++, linebuf);
#undef proc_percent
#define swtch_percent(type) \
int_percent(ws.ws_nswtch. ## type ,ws.ws_nswtch.total)
sprintf(linebuf, "%llu switches: %d%% (%llu) preempt, "
"%d%% (%llu) wait, "
"%d%% (%llu) yield",
ws.ws_nswtch.total,
swtch_percent(preempt), ws.ws_nswtch.preempt,
swtch_percent(wait), ws.ws_nswtch.wait,
swtch_percent(yield), ws.ws_nswtch.yield);
term_draw_line(top_term(ts), line++, linebuf);
#undef swtch_percent
return line - line_start;
}
int
topwait_display_objs(top_state *ts, int line_start)
{
char linebuf[160];
int ii;
wait_lock_state **wl;
double period;
int line;
line = line_start;
term_draw_bold_line(top_term(ts), line++, title);
wl = (wait_lock_state**) ts->ts_top_objs;
period = (LAST_TSTAMP - FIRST_TSTAMP) / cycle_rate;
for (ii = 0; ii < ts->ts_ntop_objs; ii++, wl++) {
const char *locktype;
double swtch_percent;
int swtch_rate;
switch ((*wl)->wl_type) {
case SEMAWAIT: locktype = "sema"; break;
case MUTEXWAIT: locktype = "mutex"; break;
case SVWAIT: locktype = "sv"; break;
case MRWAIT: locktype = "mrlock"; break;
default: locktype = "???"; break;
}
if (ws.ws_nswtch.total == 0)
swtch_percent = 0;
else
swtch_percent = ((double) (*wl)->wl_nswtch) * 100 / ws.ws_nswtch.total;
if (period)
swtch_rate = (*wl)->wl_nswtch / period;
else
swtch_rate = 0;
sprintf(linebuf, "%016llx %16s %7s %4d %6.1f %7d",
(*wl)->wl_addr,
(*wl)->wl_name,
locktype,
(*wl)->wl_max_load,
swtch_percent,
swtch_rate);
term_draw_line(top_term(ts), line++, linebuf);
}
return line - line_start;
}
/*ARGSUSED*/
void
topwait_zero_interval_counters(top_state *ts)
{
wait_lock_state *wl;
hashable *wl_hash;
for (wl_hash = htab_begin(&ws.ws_locks);
wl_hash != NULL;
wl_hash = htab_next(&ws.ws_locks,
HASH_LOCK(wl->wl_addr), WL_TO_HASHABLE(wl)))
{
wl = HASHABLE_TO_WL(wl_hash);
wl->wl_max_load = wl->wl_load;
wl->wl_nswtch = 0;
wl->wl_wait_total = 0;
}
memset(&ws.ws_nswtch, 0, sizeof(ws.ws_nswtch));
}
int
topwait_handle_keypress(top_state *ts, int ch)
{
switch (ch) {
case 't':
{
char line[80];
term_getstr(top_term(ts), 0, top_prompt_line(ts),
"Type to display",
line, sizeof(line));
if (!*line)
break;
if (set_show_type(line)) {
top_update(ts);
}
else {
top_error("Invalid lock type: %s", line);
}
}
break;
default:
return 0;
}
return 1;
}
int
top_impl_init(top_state *ts)
{
unsigned int cycle_period;
FREELIST_INIT(&wp_freelist);
FREELIST_INIT(&wl_freelist);
wait_init(&ws);
display_init();
top_set_impl(ts, &topwait_impl);
top_set_sort_order(ts, order_swtch);
top_set_filter_out(ts, wait_filter);
syssgi(SGI_QUERY_CYCLECNTR, &cycle_period);
cycle_rate = 1.0E12/cycle_period;
return 1;
}
#ifndef NDEBUG
void
topwait_lock_htab_dump(void)
{
htab_dump(&ws.ws_locks, stdout);
}
#endif /* !NDEBUG */