- Overhaul the 'ps' command in ddb to be mostly readable again. :) It is

now back to using fixed-size columns for output and each line of output
  should fit in 80 columns on both 32-bit and 64-bit architectures.  In
  general the output is close to that of the userland ps(1) with the
  exception that the 'wmesg' field is mostly similar to the "state" field
  in top(1) in that it will show either a wmesg, a lock name (prefixed with
  an *), "CPU xx" (for a running thread), or nothing if none of those three
  conditions are true.  It also respects td_name when listing threads in
  a multithreaded process.  There is a somewhat evilly-defined PTR64 macro
  I use to make account for the change in the size of the 'wchan' column
  in the formatted output (wchan is now the only pointer in the ps output
  and is available so it can be passed to 'show sleepq', 'show turnstile',
  or 'show lock').
- Add two new commands "show proc [process]" and "show thread [thread]"
  that show details about the specified process or thread (specified
  either by pid/tid or pointer), respectively.  If an address it not
  specified, it uses the current kdb thread.
This commit is contained in:
John Baldwin 2006-04-25 20:34:04 +00:00
parent ae110b53d1
commit c9a08d1cad

View File

@ -32,9 +32,13 @@ __FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/jail.h>
#include <sys/kdb.h>
#include <sys/linker_set.h>
#include <sys/lock.h>
#include <sys/mutex.h>
#include <sys/proc.h>
#include <sys/sysent.h>
#include <sys/cons.h>
#include <vm/vm.h>
#include <vm/vm_param.h>
@ -42,19 +46,47 @@ __FBSDID("$FreeBSD$");
#include <ddb/ddb.h>
static void dumpthread(volatile struct proc *p, volatile struct thread *td);
/* XXX I'd prefer a better way. */
#if defined(__alpha__) || defined(__amd64__) || defined(__ia64__) || defined(__sparc64__)
#define PTR64
#endif
#ifdef PTR64
CTASSERT(sizeof(uintptr_t) == sizeof(uint64_t));
#else
CTASSERT(sizeof(uintptr_t) == sizeof(uint32_t));
#endif
static void dumpthread(volatile struct proc *p, volatile struct thread *td,
int all);
/*
* Layout:
* - column counts
* - header
* - single-threaded process
* - multi-threaded process
* - thread in a MT process
*
* 1 2 3 4 5 6 7
* 1234567890123456789012345678901234567890123456789012345678901234567890
* pid uid ppid pgrp state wmesg wchan cmd
* <pid> <ui> <ppi> <pgi> <stat> < wmesg > < wchan > <name>
* <pid> <ui> <ppi> <pgi> <stat> (threaded) <command>
* <tid > <stat> < wmesg > < wchan > <name>
*
* For machines with 64-bit pointers, we expand the wchan field 8 more
* characters.
*/
void
db_ps(dummy1, dummy2, dummy3, dummy4)
db_expr_t dummy1;
boolean_t dummy2;
db_expr_t dummy3;
char * dummy4;
db_ps(db_expr_t addr, boolean_t hasaddr, db_expr_t count, char *modif)
{
volatile struct proc *p, *pp;
volatile struct thread *td;
char *state;
int np, quit;
struct ucred *cred;
struct pgrp *pgrp;
char state[9];
int np, quit, rflag, sflag, dflag, lflag, wflag;
np = nprocs;
quit = 0;
@ -66,10 +98,14 @@ db_ps(dummy1, dummy2, dummy3, dummy4)
p = &proc0;
db_setup_paging(db_simple_pager, &quit, db_lines_per_page);
db_printf(" pid proc uid ppid pgrp flag stat wmesg wchan cmd\n");
#ifdef PTR64
db_printf(" pid uid ppid pgrp state wmesg wchan cmd\n");
#else
db_printf(" pid uid ppid pgrp state wmesg wchan cmd\n");
#endif
while (--np >= 0 && !quit) {
if (p == NULL) {
printf("oops, ran out of processes early!\n");
db_printf("oops, ran out of processes early!\n");
break;
}
/* PROC_LOCK(p); */
@ -77,33 +113,99 @@ db_ps(dummy1, dummy2, dummy3, dummy4)
if (pp == NULL)
pp = p;
cred = p->p_ucred;
pgrp = p->p_pgrp;
db_printf("%5d %4d %5d %5d ", p->p_pid,
cred != NULL ? cred->cr_ruid : 0, pp->p_pid,
pgrp != NULL ? pgrp->pg_id : 0);
/* Determine our primary process state. */
switch(p->p_state) {
case PRS_NORMAL:
if (P_SHOULDSTOP(p))
state = "stop";
else
state = "";
state[0] = 'T';
else {
/*
* One of D, L, R, S, W. For a
* multithreaded process we will use
* the state of the thread with the
* highest precedence. The
* precendence order from high to low
* is R, L, D, S, W. If no thread is
* in a sane state we use '?' for our
* primary state.
*/
rflag = sflag = dflag = lflag = wflag = 0;
FOREACH_THREAD_IN_PROC(p, td) {
if (td->td_state == TDS_RUNNING ||
td->td_state == TDS_RUNQ ||
td->td_state == TDS_CAN_RUN)
rflag++;
if (TD_ON_LOCK(td))
lflag++;
if (TD_IS_SLEEPING(td)) {
if (!td->td_flags & TDF_SINTR)
dflag++;
else
sflag++;
}
if (TD_AWAITING_INTR(td))
wflag++;
}
if (rflag)
state[0] = 'R';
else if (lflag)
state[0] = 'L';
else if (dflag)
state[0] = 'D';
else if (sflag)
state[0] = 'S';
else if (wflag)
state[0] = 'W';
else
state[0] = '?';
}
break;
case PRS_NEW:
state = "new ";
state[0] = 'N';
break;
case PRS_ZOMBIE:
state = "zomb";
state[0] = 'Z';
break;
default:
state = "Unkn";
state[0] = 'U';
break;
}
db_printf("%5d %8p %4d %5d %5d %07x %s",
p->p_pid, (volatile void *)p,
p->p_ucred != NULL ? p->p_ucred->cr_ruid : 0, pp->p_pid,
p->p_pgrp != NULL ? p->p_pgrp->pg_id : 0, p->p_flag,
state);
state[1] = '\0';
/* Additional process state flags. */
if (!p->p_sflag & PS_INMEM)
strlcat(state, "W", sizeof(state));
if (p->p_flag & P_TRACED)
strlcat(state, "X", sizeof(state));
if (p->p_flag & P_WEXIT && p->p_state != PRS_ZOMBIE)
strlcat(state, "E", sizeof(state));
if (p->p_flag & P_PPWAIT)
strlcat(state, "V", sizeof(state));
if (p->p_flag & P_SYSTEM || p->p_lock > 0)
strlcat(state, "L", sizeof(state));
if (p->p_session != NULL && SESS_LEADER(p))
strlcat(state, "s", sizeof(state));
/* Cheated here and didn't compare pgid's. */
if (p->p_flag & P_CONTROLT)
strlcat(state, "+", sizeof(state));
if (cred != NULL && jailed(cred))
strlcat(state, "J", sizeof(state));
db_printf(" %-6.6s ", state);
if (p->p_flag & P_HADTHREADS)
db_printf("(threaded) %s\n", p->p_comm);
#ifdef PTR64
db_printf(" (threaded) %s\n",
p->p_comm);
#else
db_printf(" (threaded) %s\n", p->p_comm);
#endif
FOREACH_THREAD_IN_PROC(p, td) {
dumpthread(p, td);
dumpthread(p, td, p->p_flag & P_HADTHREADS);
if (quit)
break;
}
@ -117,54 +219,216 @@ db_ps(dummy1, dummy2, dummy3, dummy4)
}
static void
dumpthread(volatile struct proc *p, volatile struct thread *td)
dumpthread(volatile struct proc *p, volatile struct thread *td, int all)
{
char state[9], wprefix;
const char *wmesg;
void *wchan;
if (all) {
db_printf(" %9d ", td->td_tid);
switch (td->td_state) {
case TDS_RUNNING:
snprintf(state, sizeof(state), "Run");
break;
case TDS_RUNQ:
snprintf(state, sizeof(state), "RunQ");
break;
case TDS_CAN_RUN:
snprintf(state, sizeof(state), "CanRun");
break;
case TDS_INACTIVE:
snprintf(state, sizeof(state), "Inactv");
break;
case TDS_INHIBITED:
state[0] = '\0';
if (TD_ON_LOCK(td))
strlcat(state, "L", sizeof(state));
if (TD_IS_SLEEPING(td)) {
if (td->td_flags & TDF_SINTR)
strlcat(state, "S", sizeof(state));
else
strlcat(state, "D", sizeof(state));
}
if (TD_IS_SWAPPED(td))
strlcat(state, "W", sizeof(state));
if (TD_AWAITING_INTR(td))
strlcat(state, "I", sizeof(state));
if (TD_IS_SUSPENDED(td))
strlcat(state, "s", sizeof(state));
if (state[0] != '\0')
break;
default:
snprintf(state, sizeof(state), "???");
}
db_printf(" %-6.6s ", state);
}
wprefix = ' ';
if (TD_ON_LOCK(td)) {
wprefix = '*';
wmesg = td->td_lockname;
wchan = td->td_blocked;
} else if (TD_ON_SLEEPQ(td)) {
wmesg = td->td_wmesg;
wchan = td->td_wchan;
} else if (TD_IS_RUNNING(td)) {
snprintf(state, sizeof(state), "CPU %d", td->td_oncpu);
wmesg = state;
wchan = NULL;
} else {
wmesg = "";
wchan = NULL;
}
db_printf("%c%-8.8s ", wprefix, wmesg);
if (wchan == NULL)
#ifdef PTR64
db_printf("%18s ", "");
#else
db_printf("%10s ", "");
#endif
else
db_printf("%p ", wchan);
if (p->p_flag & P_SYSTEM)
db_printf("[");
if (td->td_name[0] != '\0')
db_printf("%s", td->td_name);
else
db_printf("%s", td->td_proc->p_comm);
if (p->p_flag & P_SYSTEM)
db_printf("]");
db_printf("\n");
}
if (p->p_flag & P_HADTHREADS)
db_printf( " thread %p ksegrp %p ", td, td->td_ksegrp);
if (TD_ON_SLEEPQ(td))
db_printf("[SLPQ %s %p]", td->td_wmesg, (void *)td->td_wchan);
DB_SHOW_COMMAND(thread, db_show_thread)
{
struct thread *td;
boolean_t comma;
/* Determine which thread to examine. */
if (have_addr)
td = db_lookup_thread(addr, FALSE);
else
td = kdb_thread;
db_printf("Thread %d at %p:\n", td->td_tid, td);
db_printf(" proc (pid %d): %p ", td->td_proc->p_pid, td->td_proc);
db_printf(" ksegrp: %p\n", td->td_ksegrp);
if (td->td_name[0] != '\0')
db_printf(" name: %s\n", td->td_name);
db_printf(" flags: %#x ", td->td_flags);
db_printf(" pflags: %#x\n", td->td_pflags);
db_printf(" state: ");
switch (td->td_state) {
case TDS_INHIBITED:
if (TD_ON_LOCK(td)) {
db_printf("[LOCK %6s %8p]",
td->td_lockname,
(void *)td->td_blocked);
}
if (TD_IS_SLEEPING(td)) {
db_printf("[SLP]");
}
if (TD_IS_SWAPPED(td)) {
db_printf("[SWAP]");
}
if (TD_IS_SUSPENDED(td)) {
db_printf("[SUSP]");
}
if (TD_AWAITING_INTR(td)) {
db_printf("[IWAIT]");
}
case TDS_INACTIVE:
db_printf("INACTIVE\n");
break;
case TDS_CAN_RUN:
db_printf("[Can run]");
db_printf("CAN RUN\n");
break;
case TDS_RUNQ:
db_printf("[RUNQ]");
db_printf("RUNQ\n");
break;
case TDS_RUNNING:
db_printf("[CPU %d]", td->td_oncpu);
db_printf("RUNNING (CPU %d)\n", td->td_oncpu);
break;
case TDS_INACTIVE:
db_printf("[INACTIVE]");
case TDS_INHIBITED:
db_printf("INHIBITED: {");
comma = FALSE;
if (TD_IS_SLEEPING(td)) {
db_printf("SLEEPING");
comma = TRUE;
}
if (TD_IS_SUSPENDED(td)) {
if (comma)
db_printf(", ");
db_printf("SUSPENDED");
comma = TRUE;
}
if (TD_IS_SWAPPED(td)) {
if (comma)
db_printf(", ");
db_printf("SWAPPED");
comma = TRUE;
}
if (TD_ON_LOCK(td)) {
if (comma)
db_printf(", ");
db_printf("LOCK");
comma = TRUE;
}
if (TD_AWAITING_INTR(td)) {
if (comma)
db_printf(", ");
db_printf("IWAIT");
}
db_printf("}\n");
break;
default:
db_printf("[UNK: %#x]", td->td_state);
db_printf("??? (%#x)\n", td->td_state);
break;
}
if (TD_ON_LOCK(td))
db_printf(" lock: %s turnstile: %p\n", td->td_lockname,
td->td_blocked);
if (TD_ON_SLEEPQ(td))
db_printf(" wmesg: %s wchan: %p\n", td->td_wmesg,
td->td_wchan);
db_printf(" priority: %d\n", td->td_priority);
}
DB_SHOW_COMMAND(proc, db_show_proc)
{
struct thread *td;
struct proc *p;
int i, quit;
/* Determine which process to examine. */
if (have_addr)
p = db_lookup_proc(addr);
else
p = kdb_thread->td_proc;
quit = 0;
db_setup_paging(db_simple_pager, &quit, db_lines_per_page);
db_printf("Process %d (%s) at %p:\n", p->p_pid, p->p_comm, p);
db_printf(" state: ");
switch (p->p_state) {
case PRS_NEW:
db_printf("NEW\n");
break;
case PRS_NORMAL:
db_printf("NORMAL\n");
break;
case PRS_ZOMBIE:
db_printf("ZOMBIE\n");
break;
default:
db_printf("??? (%#x)\n", p->p_state);
}
if (p->p_ucred != NULL) {
db_printf(" uid: %d gids: ", p->p_ucred->cr_uid);
for (i = 0; i < p->p_ucred->cr_ngroups; i++) {
db_printf("%d", p->p_ucred->cr_groups[i]);
if (i < (p->p_ucred->cr_ngroups - 1))
db_printf(", ");
}
db_printf("\n");
}
if (p->p_pptr != NULL)
db_printf(" parent: pid %d at %p\n", p->p_pptr->p_pid,
p->p_pptr);
if (p->p_leader != NULL && p->p_leader != p)
db_printf(" leader: pid %d at %p\n", p->p_leader->p_pid,
p->p_leader);
if (p->p_sysent != NULL)
db_printf(" ABI: %s\n", p->p_sysent->sv_name);
if (p->p_args != NULL)
db_printf(" arguments: %.*s\n", (int)p->p_args->ar_length,
p->p_args->ar_args);
db_printf(" threads: %d\n", p->p_numthreads);
FOREACH_THREAD_IN_PROC(p, td) {
dumpthread(p, td, 1);
if (quit)
break;
}
if (p->p_flag & P_HADTHREADS) {
#ifdef KEF_DIDRUN
if (td->td_kse)
db_printf("[kse %p]", td->td_kse);
#endif
db_printf("\n");
} else
db_printf(" %s\n", p->p_comm);
}