/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the
* Common Development and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
* See the License for the specific language governing permissions
* and limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each
* file and include the License file at usr/src/OPENSOLARIS.LICENSE.
* If applicable, add the following below this CDDL HEADER, with the
* fields enclosed by brackets "[]" replaced with your own identifying
* information: Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*/
/*
* Copyright (c) 2012 by Delphix. All rights reserved.
* Copyright 2016, Joyent, Inc.
*/
#include <sys/mdb_modapi.h>
#include <mdb/mdb_whatis.h>
#include <procfs.h>
#include <ucontext.h>
#include <siginfo.h>
#include <signal.h>
#include <setjmp.h>
#include <string.h>
#include <thr_uberdata.h>
#include "findstack.h"
static const char *
{
else {
buf[0] = '\0';
}
return (buf + 1);
}
/*ARGSUSED*/
static int
{
if (argc != 0)
return (DCMD_USAGE);
return (DCMD_ERR);
}
#if defined(__sparc)
mdb_printf(" %%rbx = 0x%lx\n", b[0]);
mdb_printf(" %%ebx = 0x%lx\n", b[0]);
#endif
return (DCMD_OK);
}
#if defined(UC_INTR)
#endif
#if defined(UC_ASR)
#endif
{ NULL, 0, 0 }
};
/*ARGSUSED*/
static int
{
if (argc != 0)
return (DCMD_USAGE);
return (DCMD_ERR);
}
mdb_printf(" sigmask = 0x%08x 0x%08x 0x%08x 0x%08x\n",
mdb_printf(" stack = sp 0x%p size 0x%lx flags %s\n",
mdb_printf(" mcontext = 0x%p\n",
return (DCMD_OK);
}
/*ARGSUSED*/
static int
{
#if defined(__sparc)
struct {
int sjs_flags;
#if defined(_LP64)
#endif
} s;
if (argc != 0)
return (DCMD_USAGE);
return (DCMD_ERR);
}
mdb_printf(" sigset = 0x%08x 0x%08x 0x%08x 0x%08x\n",
#if defined(_LP64)
#endif
mdb_printf(" stack = sp 0x%p size 0x%lx flags %s\n",
return (DCMD_OK);
#endif
}
/*ARGSUSED*/
static int
{
static const char *const msname[] = {
"USER", "SYSTEM", "TRAP", "TFAULT", "DFAULT", "KFAULT",
"USER_LOCK", "SLEEP", "WAIT_CPU", "STOPPED"
};
int i;
if (argc != 0)
return (DCMD_USAGE);
return (DCMD_ERR);
}
case SI_NOINFO:
mdb_printf("no info");
break;
case SI_DTRACE:
mdb_printf("from DTrace raise() action");
break;
case SI_RCTL:
mdb_printf("from rctl action");
break;
case SI_USER:
mdb_printf("user generated via kill");
break;
case SI_LWP:
mdb_printf("user generated via lwp_kill");
break;
case SI_QUEUE:
mdb_printf("user generated via sigqueue");
break;
case SI_TIMER:
mdb_printf("from timer expiration");
break;
case SI_ASYNCIO:
mdb_printf("from async i/o completion");
break;
case SI_MESGQ:
mdb_printf("from message arrival");
break;
default:
if (SI_FROMUSER(&si))
mdb_printf("from user process");
else
mdb_printf("from kernel");
}
mdb_printf(")\n errno %5d (%s)\n",
mdb_printf(" signal sent from PID %d (uid %d)\n",
}
mdb_printf(" signal value = 0t%d / %p\n",
}
case SIGCLD:
mdb_printf(" signal sent from child PID %d (uid %d)\n",
mdb_printf(" usr time = 0t%ld ticks, sys time = 0t%ld ticks\n",
break;
case SIGSEGV:
case SIGBUS:
case SIGILL:
case SIGTRAP:
case SIGFPE:
mdb_printf(" fault address = 0x%p\n trapno = %d\n",
mdb_printf(" instruction address = 0x%p %lA\n",
break;
case SIGPOLL:
case SIGXFSZ:
mdb_printf(" fd = %d band = 0x%lx\n",
break;
case SIGPROF:
mdb_printf(" last fault address = 0x%p fault type = %d\n",
mdb_printf(" timestamp = 0t%ld sec 0t%ld nsec\n",
if (si.si_nsysarg > 0) {
}
mdb_printf(" )\n");
}
mdb_printf(" mstate[\"%s\"] = %d\n",
}
break;
}
return (DCMD_OK);
}
static int
{
return (WALK_DONE);
return (WALK_ERR);
}
}
static int
{
if (nbytes <= 0) {
mdb_warn("lwpstatus information not available");
return (WALK_ERR);
}
mdb_warn("walker only supports global walk\n");
return (WALK_ERR);
}
mdb_warn("failed to read lwpstatus information");
return (WALK_ERR);
}
return (WALK_NEXT);
}
static int
{
return (WALK_NEXT);
return (WALK_NEXT);
}
}
return (WALK_DONE);
}
static void
{
}
/*
* ==================== threads ==========================
* These are the interfaces that used to require libthread.
* Now, libthread has been folded into libc.
* =======================================================
*/
/*
* prt_addr() is called up to three times to generate arguments for
* one call to mdb_printf(). We must return at least three different
* pointers to static storage for consecutive calls to prt_addr().
*/
static const char *
{
static int ix = 0;
char *buf;
ix = 0;
else {
#ifdef _LP64
if (pad)
#else
if (pad)
#endif /* _LP64 */
return (buf);
}
}
/*ARGSUSED*/
static int
{
return (DCMD_USAGE);
return (DCMD_ERR);
}
HD("self uberdata");
HD("tlsent ntlsent");
HD("forw back next");
HD("hash rval stk");
HD("mapsiz guardsize stktop stksiz");
HD("ix lwpid pri epri policy cid");
HD("cursig pleasestop stop signalled dead unwind");
mdb_printf("%-10d %-10d %d\n",
HD("detached writer stopping can'prolog preempt savpreempt");
HD("sigsuspend main fork primarymap m'spinners d'noreserv");
HD("queue_fifo c'w'defer e'detect' async_safe rt rtqueued");
HD("misaligned adapt'spin queue_spin critical sigdefer vfork");
HD("cancelable c'pending c'disabled c'async save_async mutator");
HD("created replace nocancel errno errnop");
HD("clnup_hdr schedctl_called schedctl");
HD("bindflags libc_locks stsd &ftsd");
mdb_printf("%s %s\n",
HD("eventmask[0..1] eventnum eventdata");
HD("td'enable sync'reg qtype cv_wake rtld usropts");
HD("startpc startarg wchan");
HD("link sleepq cvmutex");
HD("mxchain save_state");
HD("rdlockcnt rd_rwlock rd_count");
HD("heldlockcnt heldlocks tpdp");
HD("siglink s'l'spin s'l'spin2 s'l'sleep s'l'wakeup");
HD("&queue_root rtclassid pilocks");
/*
* The remainder of the ulwp_t structure
* is invalid if this is a replacement.
*/
if (ulwp.ul_replace)
return (DCMD_OK);
HD("sigmask[0..3]");
HD("tmpmask[0..3]");
HD("&siginfo &spinlock &fpuenv");
return (DCMD_OK);
}
/*
* Get the address of the unique uberdata_t structure.
*/
static uintptr_t
uberdata_addr(void)
{
return (NULL);
}
return (uaddr);
}
return (NULL);
}
}
/*ARGSUSED*/
static int
{
int i;
if (argc != 0)
return (DCMD_USAGE);
return (DCMD_ERR);
sizeof (uberdata)) {
return (DCMD_ERR);
}
HD("&link_lock &ld_lock &fork_lock");
HD("&atfork_lock &callout_lock &tdb_hash_lock");
HD("&tdb_hash_lock_stats &siguaction[0]");
HD("&bucket free_list chunks");
for (i = 0; i < NBUCKETS; i++) {
}
HD("&atexit_root head exit_frame_monitor");
HD("&quickexit_root head");
HD("&tsd_metadata tsdm_nkeys tsdm_nused tsdm_destro");
HD("&tls_metadata tls_modinfo.data tls_modinfo.size");
HD(" static_tls.data static_tls.size");
" ",
HD("queue_head thr_hash_table hash_size hash_mask");
HD("ulwp_one all_lwps all_zombies");
HD("nthreads nzombies ndaemons pid sigacthandler");
HD("lwp_stacks lwp_laststack nfreestack stk_cache");
HD("ulwp_freelist ulwp_lastfree ulwp_replace_free");
HD("ulwp_replace_last atforklist");
HD("robustlocks robustlist progname");
HD("tdb_bootstrap tdb_sync_addr_hash tdb_'count tdb_'fail");
HD("tdb_sync_addr_free tdb_sync_addr_last tdb_sync_alloc");
HD("tdb_ev_global_mask tdb_events");
return (DCMD_OK);
}
static int
{
!= sizeof (addr))) {
mdb_warn("cannot find 'uberdata.all_lwps'");
return (WALK_ERR);
}
return (WALK_DONE);
return (WALK_NEXT);
}
static int
{
return (WALK_DONE);
return (WALK_ERR);
}
/*
* If we have looped around to the beginning
* of the circular linked list, we are done.
*/
}
/* Avoid classifying NULL pointers as part of the main stack on x86 */
static int
{
"allocated as thread %#r's ulwp_t\n", id);
/*
* The main stack ends up being a little weird, especially if
* the stack ulimit is unlimited. This tries to take that into
* account.
*/
"in [ altstack tid=%#r ]\n", id);
}
return (WHATIS_WALKRET(w));
}
/*ARGSUSED*/
static int
{
mdb_warn("couldn't find ulwps walker");
return (1);
}
return (0);
}
/*
* =======================================================
* End of thread (previously libthread) interfaces.
* ==================== threads ==========================
*/
int
{
/*
* For the user-level variant of ::stacks, we don't bother caching
* state, as even a very large program is unlikely to compare to the
* kernel in terms of number of threads. (And if you find yourself
* here in anger, frustrated about how long ::stacks is running on
* your galactically complicated zillion-thread program, hopefully
* you will find some solace in the irony. Okay, probably not...)
*/
return (rval);
}
typedef struct tid2ulwp_walk {
/*ARGSUSED*/
static int
{
return (WALK_DONE);
}
return (WALK_NEXT);
}
static int
{
mdb_warn("can't walk 'ulwp'");
return (DCMD_ERR);
}
return (DCMD_ERR);
}
return (DCMD_OK);
}
/*ARGSUSED*/
static int
{
int error;
if (argc != 0)
return (DCMD_USAGE);
return (error);
}
typedef struct mdb_libc_ulwp {
/*
* Map from thread pointer to tsd for given key
*/
static int
{
return (DCMD_USAGE);
return (DCMD_USAGE);
return (DCMD_ERR);
return (DCMD_ERR);
/* tsd_t is a union, so we can't use ctf_vread() on it. */
return (DCMD_ERR);
}
mdb_warn("failed to read tsd_t at %p",
u.ul_stsd);
return (DCMD_ERR);
}
}
}
return (DCMD_OK);
return (DCMD_OK);
}
{ "stacks", "?[-afiv] [-c func] [-C func] [-m module] [-M module] ",
{ NULL }
};
{ "ucontext", "walk ucontext_t uc_link list",
{ "oldcontext", "walk per-lwp oldcontext pointers",
{ "ulwps", "walk list of ulwp_t pointers",
{ "ulwp", "walk list of ulwp_t pointers",
{ NULL }
};
const mdb_modinfo_t *
_mdb_init(void)
{
return (&modinfo);
}