1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 28*0Sstevel@tonic-gate 29*0Sstevel@tonic-gate #include <sys/isa_defs.h> 30*0Sstevel@tonic-gate 31*0Sstevel@tonic-gate #include <stdio.h> 32*0Sstevel@tonic-gate #include <fcntl.h> 33*0Sstevel@tonic-gate #include <ctype.h> 34*0Sstevel@tonic-gate #include <string.h> 35*0Sstevel@tonic-gate #include <signal.h> 36*0Sstevel@tonic-gate #include <dirent.h> 37*0Sstevel@tonic-gate #include <errno.h> 38*0Sstevel@tonic-gate #include <stdlib.h> 39*0Sstevel@tonic-gate #include <stdarg.h> 40*0Sstevel@tonic-gate #include <unistd.h> 41*0Sstevel@tonic-gate #include <sys/types.h> 42*0Sstevel@tonic-gate #include <sys/stat.h> 43*0Sstevel@tonic-gate #include <sys/stack.h> 44*0Sstevel@tonic-gate #include <link.h> 45*0Sstevel@tonic-gate #include <limits.h> 46*0Sstevel@tonic-gate #include <libelf.h> 47*0Sstevel@tonic-gate #include <thread_db.h> 48*0Sstevel@tonic-gate #include <libproc.h> 49*0Sstevel@tonic-gate #include <setjmp.h> 50*0Sstevel@tonic-gate 51*0Sstevel@tonic-gate static char *command; 52*0Sstevel@tonic-gate static int Fflag; 53*0Sstevel@tonic-gate static int is64; 54*0Sstevel@tonic-gate static GElf_Sym sigh; 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate /* 57*0Sstevel@tonic-gate * To keep the list of user-level threads for a multithreaded process. 58*0Sstevel@tonic-gate */ 59*0Sstevel@tonic-gate struct threadinfo { 60*0Sstevel@tonic-gate struct threadinfo *next; 61*0Sstevel@tonic-gate id_t threadid; 62*0Sstevel@tonic-gate id_t lwpid; 63*0Sstevel@tonic-gate td_thr_state_e state; 64*0Sstevel@tonic-gate uintptr_t startfunc; 65*0Sstevel@tonic-gate uintptr_t exitval; 66*0Sstevel@tonic-gate prgregset_t regs; 67*0Sstevel@tonic-gate }; 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gate static struct threadinfo *thr_head, *thr_tail; 70*0Sstevel@tonic-gate 71*0Sstevel@tonic-gate #define TRUE 1 72*0Sstevel@tonic-gate #define FALSE 0 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gate #define MAX_ARGS 8 75*0Sstevel@tonic-gate 76*0Sstevel@tonic-gate /* 77*0Sstevel@tonic-gate * To support debugging java programs, we display java frames within a stack. 78*0Sstevel@tonic-gate * The logic to walk the java frames is contained in libjvm_db.so, which is 79*0Sstevel@tonic-gate * found in the same directory as libjvm.so, linked with the program. If we are 80*0Sstevel@tonic-gate * debugging a 32-bit app with a 64-binary, then the debugging library is found 81*0Sstevel@tonic-gate * in the '64' subdirectory. If we find libjvm_db.so, then we fill in these 82*0Sstevel@tonic-gate * stub routines. 83*0Sstevel@tonic-gate */ 84*0Sstevel@tonic-gate typedef struct jvm_agent jvm_agent_t; 85*0Sstevel@tonic-gate typedef int java_stack_f(void *, prgregset_t, const char *, int, int, void *); 86*0Sstevel@tonic-gate 87*0Sstevel@tonic-gate /* 88*0Sstevel@tonic-gate * The j_agent_create function takes a version parameter. This ensures that the 89*0Sstevel@tonic-gate * interface can evolve appropriately. 90*0Sstevel@tonic-gate */ 91*0Sstevel@tonic-gate #define JVM_DB_VERSION 1 92*0Sstevel@tonic-gate static void *libjvm; 93*0Sstevel@tonic-gate typedef jvm_agent_t *(*j_agent_create_f)(struct ps_prochandle *, int); 94*0Sstevel@tonic-gate typedef void (*j_agent_destroy_f)(jvm_agent_t *); 95*0Sstevel@tonic-gate typedef int (*j_frame_iter_f)(jvm_agent_t *, prgregset_t, java_stack_f *, 96*0Sstevel@tonic-gate void *); 97*0Sstevel@tonic-gate 98*0Sstevel@tonic-gate static j_agent_create_f j_agent_create; 99*0Sstevel@tonic-gate static j_agent_destroy_f j_agent_destroy; 100*0Sstevel@tonic-gate static j_frame_iter_f j_frame_iter; 101*0Sstevel@tonic-gate 102*0Sstevel@tonic-gate static jvm_agent_t *load_libjvm(struct ps_prochandle *P); 103*0Sstevel@tonic-gate static void reset_libjvm(jvm_agent_t *); 104*0Sstevel@tonic-gate 105*0Sstevel@tonic-gate /* 106*0Sstevel@tonic-gate * Since we must maintain both a proc handle and a jvm handle, this structure 107*0Sstevel@tonic-gate * is the basic type that gets passed around. 108*0Sstevel@tonic-gate */ 109*0Sstevel@tonic-gate typedef struct pstack_handle { 110*0Sstevel@tonic-gate struct ps_prochandle *proc; 111*0Sstevel@tonic-gate jvm_agent_t *jvm; 112*0Sstevel@tonic-gate int ignore_frame; 113*0Sstevel@tonic-gate const char *lwps; 114*0Sstevel@tonic-gate int count; 115*0Sstevel@tonic-gate } pstack_handle_t; 116*0Sstevel@tonic-gate 117*0Sstevel@tonic-gate static int thr_stack(const td_thrhandle_t *, void *); 118*0Sstevel@tonic-gate static void free_threadinfo(void); 119*0Sstevel@tonic-gate static struct threadinfo *find_thread(id_t); 120*0Sstevel@tonic-gate static int all_call_stacks(pstack_handle_t *, int); 121*0Sstevel@tonic-gate static void tlhead(id_t, id_t); 122*0Sstevel@tonic-gate static int print_frame(void *, prgregset_t, uint_t, const long *); 123*0Sstevel@tonic-gate static void print_zombie(struct ps_prochandle *, struct threadinfo *); 124*0Sstevel@tonic-gate static void print_syscall(const lwpstatus_t *, prgregset_t); 125*0Sstevel@tonic-gate static void call_stack(pstack_handle_t *, const lwpstatus_t *); 126*0Sstevel@tonic-gate 127*0Sstevel@tonic-gate /* 128*0Sstevel@tonic-gate * The number of active and zombie threads. 129*0Sstevel@tonic-gate */ 130*0Sstevel@tonic-gate static int nthreads; 131*0Sstevel@tonic-gate 132*0Sstevel@tonic-gate int 133*0Sstevel@tonic-gate main(int argc, char **argv) 134*0Sstevel@tonic-gate { 135*0Sstevel@tonic-gate int retc = 0; 136*0Sstevel@tonic-gate int opt; 137*0Sstevel@tonic-gate int errflg = FALSE; 138*0Sstevel@tonic-gate core_content_t content = CC_CONTENT_DATA | CC_CONTENT_ANON | 139*0Sstevel@tonic-gate CC_CONTENT_STACK; 140*0Sstevel@tonic-gate struct rlimit rlim; 141*0Sstevel@tonic-gate 142*0Sstevel@tonic-gate if ((command = strrchr(argv[0], '/')) != NULL) 143*0Sstevel@tonic-gate command++; 144*0Sstevel@tonic-gate else 145*0Sstevel@tonic-gate command = argv[0]; 146*0Sstevel@tonic-gate 147*0Sstevel@tonic-gate /* options */ 148*0Sstevel@tonic-gate while ((opt = getopt(argc, argv, "F")) != EOF) { 149*0Sstevel@tonic-gate switch (opt) { 150*0Sstevel@tonic-gate case 'F': 151*0Sstevel@tonic-gate /* 152*0Sstevel@tonic-gate * If the user specifies the force option, we'll 153*0Sstevel@tonic-gate * consent to printing out other threads' stacks 154*0Sstevel@tonic-gate * even if the main stack is absent. 155*0Sstevel@tonic-gate */ 156*0Sstevel@tonic-gate content &= ~CC_CONTENT_STACK; 157*0Sstevel@tonic-gate Fflag = PGRAB_FORCE; 158*0Sstevel@tonic-gate break; 159*0Sstevel@tonic-gate default: 160*0Sstevel@tonic-gate errflg = TRUE; 161*0Sstevel@tonic-gate break; 162*0Sstevel@tonic-gate } 163*0Sstevel@tonic-gate } 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate argc -= optind; 166*0Sstevel@tonic-gate argv += optind; 167*0Sstevel@tonic-gate 168*0Sstevel@tonic-gate if (errflg || argc <= 0) { 169*0Sstevel@tonic-gate (void) fprintf(stderr, 170*0Sstevel@tonic-gate "usage:\t%s [-F] { pid | core }[/lwps] ...\n", command); 171*0Sstevel@tonic-gate (void) fprintf(stderr, " (show process call stack)\n"); 172*0Sstevel@tonic-gate (void) fprintf(stderr, 173*0Sstevel@tonic-gate " -F: force grabbing of the target process\n"); 174*0Sstevel@tonic-gate exit(2); 175*0Sstevel@tonic-gate } 176*0Sstevel@tonic-gate 177*0Sstevel@tonic-gate /* 178*0Sstevel@tonic-gate * Make sure we'll have enough file descriptors to handle a target 179*0Sstevel@tonic-gate * that has many many mappings. 180*0Sstevel@tonic-gate */ 181*0Sstevel@tonic-gate if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { 182*0Sstevel@tonic-gate rlim.rlim_cur = rlim.rlim_max; 183*0Sstevel@tonic-gate (void) setrlimit(RLIMIT_NOFILE, &rlim); 184*0Sstevel@tonic-gate } 185*0Sstevel@tonic-gate 186*0Sstevel@tonic-gate (void) proc_initstdio(); 187*0Sstevel@tonic-gate 188*0Sstevel@tonic-gate while (--argc >= 0) { 189*0Sstevel@tonic-gate int gcode; 190*0Sstevel@tonic-gate psinfo_t psinfo; 191*0Sstevel@tonic-gate const psinfo_t *tpsinfo; 192*0Sstevel@tonic-gate struct ps_prochandle *Pr = NULL; 193*0Sstevel@tonic-gate td_thragent_t *Tap; 194*0Sstevel@tonic-gate int threaded; 195*0Sstevel@tonic-gate pstack_handle_t handle; 196*0Sstevel@tonic-gate const char *lwps, *arg; 197*0Sstevel@tonic-gate 198*0Sstevel@tonic-gate (void) proc_flushstdio(); 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate arg = *argv++; 201*0Sstevel@tonic-gate 202*0Sstevel@tonic-gate if ((Pr = proc_arg_xgrab(arg, NULL, PR_ARG_ANY, 203*0Sstevel@tonic-gate Fflag, &gcode, &lwps)) == NULL) { 204*0Sstevel@tonic-gate (void) fprintf(stderr, "%s: cannot examine %s: %s\n", 205*0Sstevel@tonic-gate command, arg, Pgrab_error(gcode)); 206*0Sstevel@tonic-gate retc++; 207*0Sstevel@tonic-gate continue; 208*0Sstevel@tonic-gate } 209*0Sstevel@tonic-gate 210*0Sstevel@tonic-gate if ((tpsinfo = Ppsinfo(Pr)) == NULL) { 211*0Sstevel@tonic-gate (void) fprintf(stderr, "%s: cannot examine %s: " 212*0Sstevel@tonic-gate "lost control of process\n", command, arg); 213*0Sstevel@tonic-gate Prelease(Pr, 0); 214*0Sstevel@tonic-gate retc++; 215*0Sstevel@tonic-gate continue; 216*0Sstevel@tonic-gate } 217*0Sstevel@tonic-gate (void) memcpy(&psinfo, tpsinfo, sizeof (psinfo_t)); 218*0Sstevel@tonic-gate proc_unctrl_psinfo(&psinfo); 219*0Sstevel@tonic-gate 220*0Sstevel@tonic-gate if (Pstate(Pr) == PS_DEAD) { 221*0Sstevel@tonic-gate if ((Pcontent(Pr) & content) != content) { 222*0Sstevel@tonic-gate (void) fprintf(stderr, "%s: core '%s' has " 223*0Sstevel@tonic-gate "insufficient content\n", command, arg); 224*0Sstevel@tonic-gate retc++; 225*0Sstevel@tonic-gate continue; 226*0Sstevel@tonic-gate } 227*0Sstevel@tonic-gate (void) printf("core '%s' of %d:\t%.70s\n", 228*0Sstevel@tonic-gate arg, (int)psinfo.pr_pid, psinfo.pr_psargs); 229*0Sstevel@tonic-gate } else { 230*0Sstevel@tonic-gate (void) printf("%d:\t%.70s\n", 231*0Sstevel@tonic-gate (int)psinfo.pr_pid, psinfo.pr_psargs); 232*0Sstevel@tonic-gate } 233*0Sstevel@tonic-gate 234*0Sstevel@tonic-gate is64 = (psinfo.pr_dmodel == PR_MODEL_LP64); 235*0Sstevel@tonic-gate 236*0Sstevel@tonic-gate if (Pgetauxval(Pr, AT_BASE) != -1L && Prd_agent(Pr) == NULL) { 237*0Sstevel@tonic-gate (void) fprintf(stderr, "%s: warning: librtld_db failed " 238*0Sstevel@tonic-gate "to initialize; symbols from shared libraries will " 239*0Sstevel@tonic-gate "not be available\n", command); 240*0Sstevel@tonic-gate } 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate /* 243*0Sstevel@tonic-gate * First we need to get a thread agent handle. 244*0Sstevel@tonic-gate */ 245*0Sstevel@tonic-gate if (td_init() != TD_OK || 246*0Sstevel@tonic-gate td_ta_new(Pr, &Tap) != TD_OK) /* no libc */ 247*0Sstevel@tonic-gate threaded = FALSE; 248*0Sstevel@tonic-gate else { 249*0Sstevel@tonic-gate /* 250*0Sstevel@tonic-gate * Iterate over all threads, calling: 251*0Sstevel@tonic-gate * thr_stack(td_thrhandle_t *Thp, NULL); 252*0Sstevel@tonic-gate * for each one to generate the list of threads. 253*0Sstevel@tonic-gate */ 254*0Sstevel@tonic-gate nthreads = 0; 255*0Sstevel@tonic-gate (void) td_ta_thr_iter(Tap, thr_stack, NULL, 256*0Sstevel@tonic-gate TD_THR_ANY_STATE, TD_THR_LOWEST_PRIORITY, 257*0Sstevel@tonic-gate TD_SIGNO_MASK, TD_THR_ANY_USER_FLAGS); 258*0Sstevel@tonic-gate 259*0Sstevel@tonic-gate (void) td_ta_delete(Tap); 260*0Sstevel@tonic-gate threaded = TRUE; 261*0Sstevel@tonic-gate } 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate handle.proc = Pr; 264*0Sstevel@tonic-gate handle.jvm = load_libjvm(Pr); 265*0Sstevel@tonic-gate handle.lwps = lwps; 266*0Sstevel@tonic-gate handle.count = 0; 267*0Sstevel@tonic-gate 268*0Sstevel@tonic-gate if (all_call_stacks(&handle, threaded) != 0) 269*0Sstevel@tonic-gate retc++; 270*0Sstevel@tonic-gate if (threaded) 271*0Sstevel@tonic-gate free_threadinfo(); 272*0Sstevel@tonic-gate 273*0Sstevel@tonic-gate reset_libjvm(handle.jvm); 274*0Sstevel@tonic-gate Prelease(Pr, 0); 275*0Sstevel@tonic-gate 276*0Sstevel@tonic-gate if (handle.count == 0) 277*0Sstevel@tonic-gate (void) fprintf(stderr, "%s: no matching LWPs found\n", 278*0Sstevel@tonic-gate command); 279*0Sstevel@tonic-gate } 280*0Sstevel@tonic-gate 281*0Sstevel@tonic-gate (void) proc_finistdio(); 282*0Sstevel@tonic-gate 283*0Sstevel@tonic-gate return (retc); 284*0Sstevel@tonic-gate } 285*0Sstevel@tonic-gate 286*0Sstevel@tonic-gate /* 287*0Sstevel@tonic-gate * Thread iteration call-back function. 288*0Sstevel@tonic-gate * Called once for each user-level thread. 289*0Sstevel@tonic-gate * Used to build the list of all threads. 290*0Sstevel@tonic-gate */ 291*0Sstevel@tonic-gate /* ARGSUSED1 */ 292*0Sstevel@tonic-gate static int 293*0Sstevel@tonic-gate thr_stack(const td_thrhandle_t *Thp, void *cd) 294*0Sstevel@tonic-gate { 295*0Sstevel@tonic-gate td_thrinfo_t thrinfo; 296*0Sstevel@tonic-gate struct threadinfo *tip; 297*0Sstevel@tonic-gate td_err_e error; 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gate if (td_thr_get_info(Thp, &thrinfo) != TD_OK) 300*0Sstevel@tonic-gate return (0); 301*0Sstevel@tonic-gate 302*0Sstevel@tonic-gate tip = malloc(sizeof (struct threadinfo)); 303*0Sstevel@tonic-gate tip->next = NULL; 304*0Sstevel@tonic-gate tip->threadid = thrinfo.ti_tid; 305*0Sstevel@tonic-gate tip->lwpid = thrinfo.ti_lid; 306*0Sstevel@tonic-gate tip->state = thrinfo.ti_state; 307*0Sstevel@tonic-gate tip->startfunc = thrinfo.ti_startfunc; 308*0Sstevel@tonic-gate tip->exitval = (uintptr_t)thrinfo.ti_exitval; 309*0Sstevel@tonic-gate nthreads++; 310*0Sstevel@tonic-gate 311*0Sstevel@tonic-gate if (thrinfo.ti_state == TD_THR_ZOMBIE || 312*0Sstevel@tonic-gate ((error = td_thr_getgregs(Thp, tip->regs)) != TD_OK && 313*0Sstevel@tonic-gate error != TD_PARTIALREG)) 314*0Sstevel@tonic-gate (void) memset(tip->regs, 0, sizeof (prgregset_t)); 315*0Sstevel@tonic-gate 316*0Sstevel@tonic-gate if (thr_tail) 317*0Sstevel@tonic-gate thr_tail->next = tip; 318*0Sstevel@tonic-gate else 319*0Sstevel@tonic-gate thr_head = tip; 320*0Sstevel@tonic-gate thr_tail = tip; 321*0Sstevel@tonic-gate 322*0Sstevel@tonic-gate return (0); 323*0Sstevel@tonic-gate } 324*0Sstevel@tonic-gate 325*0Sstevel@tonic-gate static void 326*0Sstevel@tonic-gate free_threadinfo() 327*0Sstevel@tonic-gate { 328*0Sstevel@tonic-gate struct threadinfo *tip = thr_head; 329*0Sstevel@tonic-gate struct threadinfo *next; 330*0Sstevel@tonic-gate 331*0Sstevel@tonic-gate while (tip) { 332*0Sstevel@tonic-gate next = tip->next; 333*0Sstevel@tonic-gate free(tip); 334*0Sstevel@tonic-gate tip = next; 335*0Sstevel@tonic-gate } 336*0Sstevel@tonic-gate 337*0Sstevel@tonic-gate thr_head = thr_tail = NULL; 338*0Sstevel@tonic-gate } 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate /* 341*0Sstevel@tonic-gate * Find and eliminate the thread corresponding to the given lwpid. 342*0Sstevel@tonic-gate */ 343*0Sstevel@tonic-gate static struct threadinfo * 344*0Sstevel@tonic-gate find_thread(id_t lwpid) 345*0Sstevel@tonic-gate { 346*0Sstevel@tonic-gate struct threadinfo *tip; 347*0Sstevel@tonic-gate 348*0Sstevel@tonic-gate for (tip = thr_head; tip; tip = tip->next) { 349*0Sstevel@tonic-gate if (lwpid == tip->lwpid) { 350*0Sstevel@tonic-gate tip->lwpid = 0; 351*0Sstevel@tonic-gate return (tip); 352*0Sstevel@tonic-gate } 353*0Sstevel@tonic-gate } 354*0Sstevel@tonic-gate return (NULL); 355*0Sstevel@tonic-gate } 356*0Sstevel@tonic-gate 357*0Sstevel@tonic-gate static int 358*0Sstevel@tonic-gate thread_call_stack(void *data, const lwpstatus_t *psp, 359*0Sstevel@tonic-gate const lwpsinfo_t *pip) 360*0Sstevel@tonic-gate { 361*0Sstevel@tonic-gate pstack_handle_t *h = data; 362*0Sstevel@tonic-gate lwpstatus_t lwpstatus; 363*0Sstevel@tonic-gate struct threadinfo *tip; 364*0Sstevel@tonic-gate 365*0Sstevel@tonic-gate if (!proc_lwp_in_set(h->lwps, pip->pr_lwpid)) 366*0Sstevel@tonic-gate return (0); 367*0Sstevel@tonic-gate h->count++; 368*0Sstevel@tonic-gate 369*0Sstevel@tonic-gate if ((tip = find_thread(pip->pr_lwpid)) == NULL) 370*0Sstevel@tonic-gate return (0); 371*0Sstevel@tonic-gate 372*0Sstevel@tonic-gate tlhead(tip->threadid, pip->pr_lwpid); 373*0Sstevel@tonic-gate tip->threadid = 0; /* finish eliminating tid */ 374*0Sstevel@tonic-gate if (psp) 375*0Sstevel@tonic-gate call_stack(h, psp); 376*0Sstevel@tonic-gate else { 377*0Sstevel@tonic-gate if (tip->state == TD_THR_ZOMBIE) 378*0Sstevel@tonic-gate print_zombie(h->proc, tip); 379*0Sstevel@tonic-gate else { 380*0Sstevel@tonic-gate (void) memset(&lwpstatus, 0, sizeof (lwpstatus)); 381*0Sstevel@tonic-gate (void) memcpy(lwpstatus.pr_reg, tip->regs, 382*0Sstevel@tonic-gate sizeof (prgregset_t)); 383*0Sstevel@tonic-gate call_stack(h, &lwpstatus); 384*0Sstevel@tonic-gate } 385*0Sstevel@tonic-gate } 386*0Sstevel@tonic-gate return (0); 387*0Sstevel@tonic-gate } 388*0Sstevel@tonic-gate 389*0Sstevel@tonic-gate static int 390*0Sstevel@tonic-gate lwp_call_stack(void *data, 391*0Sstevel@tonic-gate const lwpstatus_t *psp, const lwpsinfo_t *pip) 392*0Sstevel@tonic-gate { 393*0Sstevel@tonic-gate pstack_handle_t *h = data; 394*0Sstevel@tonic-gate 395*0Sstevel@tonic-gate if (!proc_lwp_in_set(h->lwps, pip->pr_lwpid)) 396*0Sstevel@tonic-gate return (0); 397*0Sstevel@tonic-gate h->count++; 398*0Sstevel@tonic-gate 399*0Sstevel@tonic-gate tlhead(0, pip->pr_lwpid); 400*0Sstevel@tonic-gate if (psp) 401*0Sstevel@tonic-gate call_stack(h, psp); 402*0Sstevel@tonic-gate else 403*0Sstevel@tonic-gate (void) printf("\t** zombie " 404*0Sstevel@tonic-gate "(exited, not detached, not yet joined) **\n"); 405*0Sstevel@tonic-gate return (0); 406*0Sstevel@tonic-gate } 407*0Sstevel@tonic-gate 408*0Sstevel@tonic-gate static int 409*0Sstevel@tonic-gate all_call_stacks(pstack_handle_t *h, int dothreads) 410*0Sstevel@tonic-gate { 411*0Sstevel@tonic-gate struct ps_prochandle *Pr = h->proc; 412*0Sstevel@tonic-gate pstatus_t status = *Pstatus(Pr); 413*0Sstevel@tonic-gate 414*0Sstevel@tonic-gate (void) memset(&sigh, 0, sizeof (GElf_Sym)); 415*0Sstevel@tonic-gate (void) Plookup_by_name(Pr, "libc.so", "sigacthandler", &sigh); 416*0Sstevel@tonic-gate 417*0Sstevel@tonic-gate if ((status.pr_nlwp + status.pr_nzomb) <= 1 && 418*0Sstevel@tonic-gate !(dothreads && nthreads > 1)) { 419*0Sstevel@tonic-gate if (proc_lwp_in_set(h->lwps, status.pr_lwp.pr_lwpid)) { 420*0Sstevel@tonic-gate call_stack(h, &status.pr_lwp); 421*0Sstevel@tonic-gate h->count++; 422*0Sstevel@tonic-gate } 423*0Sstevel@tonic-gate } else { 424*0Sstevel@tonic-gate lwpstatus_t lwpstatus; 425*0Sstevel@tonic-gate struct threadinfo *tip; 426*0Sstevel@tonic-gate id_t tid; 427*0Sstevel@tonic-gate 428*0Sstevel@tonic-gate if (dothreads) 429*0Sstevel@tonic-gate (void) Plwp_iter_all(Pr, thread_call_stack, h); 430*0Sstevel@tonic-gate else 431*0Sstevel@tonic-gate (void) Plwp_iter_all(Pr, lwp_call_stack, h); 432*0Sstevel@tonic-gate 433*0Sstevel@tonic-gate /* for each remaining thread w/o an lwp */ 434*0Sstevel@tonic-gate (void) memset(&lwpstatus, 0, sizeof (lwpstatus)); 435*0Sstevel@tonic-gate for (tip = thr_head; tip; tip = tip->next) { 436*0Sstevel@tonic-gate 437*0Sstevel@tonic-gate if (!proc_lwp_in_set(h->lwps, tip->lwpid)) 438*0Sstevel@tonic-gate tip->threadid = 0; 439*0Sstevel@tonic-gate 440*0Sstevel@tonic-gate if ((tid = tip->threadid) != 0) { 441*0Sstevel@tonic-gate (void) memcpy(lwpstatus.pr_reg, tip->regs, 442*0Sstevel@tonic-gate sizeof (prgregset_t)); 443*0Sstevel@tonic-gate tlhead(tid, tip->lwpid); 444*0Sstevel@tonic-gate if (tip->state == TD_THR_ZOMBIE) 445*0Sstevel@tonic-gate print_zombie(Pr, tip); 446*0Sstevel@tonic-gate else 447*0Sstevel@tonic-gate call_stack(h, &lwpstatus); 448*0Sstevel@tonic-gate } 449*0Sstevel@tonic-gate tip->threadid = 0; 450*0Sstevel@tonic-gate tip->lwpid = 0; 451*0Sstevel@tonic-gate } 452*0Sstevel@tonic-gate } 453*0Sstevel@tonic-gate return (0); 454*0Sstevel@tonic-gate } 455*0Sstevel@tonic-gate 456*0Sstevel@tonic-gate static void 457*0Sstevel@tonic-gate tlhead(id_t threadid, id_t lwpid) 458*0Sstevel@tonic-gate { 459*0Sstevel@tonic-gate if (threadid == 0 && lwpid == 0) 460*0Sstevel@tonic-gate return; 461*0Sstevel@tonic-gate 462*0Sstevel@tonic-gate (void) printf("-----------------"); 463*0Sstevel@tonic-gate 464*0Sstevel@tonic-gate if (threadid && lwpid) 465*0Sstevel@tonic-gate (void) printf(" lwp# %d / thread# %d ", 466*0Sstevel@tonic-gate (int)lwpid, (int)threadid); 467*0Sstevel@tonic-gate else if (threadid) 468*0Sstevel@tonic-gate (void) printf("--------- thread# %d ", (int)threadid); 469*0Sstevel@tonic-gate else if (lwpid) 470*0Sstevel@tonic-gate (void) printf(" lwp# %d ------------", (int)lwpid); 471*0Sstevel@tonic-gate 472*0Sstevel@tonic-gate (void) printf("--------------------\n"); 473*0Sstevel@tonic-gate } 474*0Sstevel@tonic-gate 475*0Sstevel@tonic-gate /*ARGSUSED*/ 476*0Sstevel@tonic-gate static int 477*0Sstevel@tonic-gate print_java_frame(void *cld, prgregset_t gregs, const char *name, int bci, 478*0Sstevel@tonic-gate int line, void *handle) 479*0Sstevel@tonic-gate { 480*0Sstevel@tonic-gate int length = (is64 ? 16 : 8); 481*0Sstevel@tonic-gate 482*0Sstevel@tonic-gate (void) printf(" %.*lx * %s", length, (long)gregs[R_PC], name); 483*0Sstevel@tonic-gate 484*0Sstevel@tonic-gate if (bci != -1) { 485*0Sstevel@tonic-gate (void) printf("+%d", bci); 486*0Sstevel@tonic-gate if (line) 487*0Sstevel@tonic-gate (void) printf(" (line %d)", line); 488*0Sstevel@tonic-gate } 489*0Sstevel@tonic-gate (void) printf("\n"); 490*0Sstevel@tonic-gate 491*0Sstevel@tonic-gate return (0); 492*0Sstevel@tonic-gate } 493*0Sstevel@tonic-gate 494*0Sstevel@tonic-gate static sigjmp_buf jumpbuf; 495*0Sstevel@tonic-gate 496*0Sstevel@tonic-gate /*ARGSUSED*/ 497*0Sstevel@tonic-gate static void 498*0Sstevel@tonic-gate fatal_signal(int signo) 499*0Sstevel@tonic-gate { 500*0Sstevel@tonic-gate siglongjmp(jumpbuf, 1); 501*0Sstevel@tonic-gate } 502*0Sstevel@tonic-gate 503*0Sstevel@tonic-gate static int 504*0Sstevel@tonic-gate print_frame(void *cd, prgregset_t gregs, uint_t argc, const long *argv) 505*0Sstevel@tonic-gate { 506*0Sstevel@tonic-gate pstack_handle_t *h = cd; 507*0Sstevel@tonic-gate struct ps_prochandle *Pr = h->proc; 508*0Sstevel@tonic-gate uintptr_t pc = gregs[R_PC]; 509*0Sstevel@tonic-gate char buff[255]; 510*0Sstevel@tonic-gate GElf_Sym sym; 511*0Sstevel@tonic-gate uintptr_t start; 512*0Sstevel@tonic-gate int length = (is64? 16 : 8); 513*0Sstevel@tonic-gate int i; 514*0Sstevel@tonic-gate 515*0Sstevel@tonic-gate /* 516*0Sstevel@tonic-gate * If we are in a system call, we display the entry frame in a more 517*0Sstevel@tonic-gate * readable manner, using the name of the system call. In this case, we 518*0Sstevel@tonic-gate * want to ignore this first frame, since we already displayed it 519*0Sstevel@tonic-gate * separately. 520*0Sstevel@tonic-gate */ 521*0Sstevel@tonic-gate if (h->ignore_frame) { 522*0Sstevel@tonic-gate h->ignore_frame = 0; 523*0Sstevel@tonic-gate return (0); 524*0Sstevel@tonic-gate } 525*0Sstevel@tonic-gate 526*0Sstevel@tonic-gate (void) sprintf(buff, "%.*lx", length, (long)pc); 527*0Sstevel@tonic-gate (void) strcpy(buff + length, " ????????"); 528*0Sstevel@tonic-gate if (Plookup_by_addr(Pr, pc, 529*0Sstevel@tonic-gate buff + 1 + length, sizeof (buff) - 1 - length, &sym) == 0) { 530*0Sstevel@tonic-gate start = sym.st_value; 531*0Sstevel@tonic-gate } else if (h->jvm != NULL) { 532*0Sstevel@tonic-gate int ret; 533*0Sstevel@tonic-gate void (*segv)(int), (*bus)(int), (*ill)(int); 534*0Sstevel@tonic-gate 535*0Sstevel@tonic-gate segv = signal(SIGSEGV, fatal_signal); 536*0Sstevel@tonic-gate bus = signal(SIGBUS, fatal_signal); 537*0Sstevel@tonic-gate ill = signal(SIGILL, fatal_signal); 538*0Sstevel@tonic-gate 539*0Sstevel@tonic-gate /* Insure against a bad libjvm_db */ 540*0Sstevel@tonic-gate if (sigsetjmp(jumpbuf, 0) == 0) 541*0Sstevel@tonic-gate ret = j_frame_iter(h->jvm, gregs, print_java_frame, 542*0Sstevel@tonic-gate NULL); 543*0Sstevel@tonic-gate else 544*0Sstevel@tonic-gate ret = -1; 545*0Sstevel@tonic-gate 546*0Sstevel@tonic-gate (void) signal(SIGSEGV, segv); 547*0Sstevel@tonic-gate (void) signal(SIGBUS, bus); 548*0Sstevel@tonic-gate (void) signal(SIGILL, ill); 549*0Sstevel@tonic-gate 550*0Sstevel@tonic-gate if (ret == 0) 551*0Sstevel@tonic-gate return (ret); 552*0Sstevel@tonic-gate } else { 553*0Sstevel@tonic-gate start = pc; 554*0Sstevel@tonic-gate } 555*0Sstevel@tonic-gate 556*0Sstevel@tonic-gate (void) printf(" %-17s (", buff); 557*0Sstevel@tonic-gate for (i = 0; i < argc && i < MAX_ARGS; i++) 558*0Sstevel@tonic-gate (void) printf((i+1 == argc)? "%lx" : "%lx, ", 559*0Sstevel@tonic-gate argv[i]); 560*0Sstevel@tonic-gate if (i != argc) 561*0Sstevel@tonic-gate (void) printf("..."); 562*0Sstevel@tonic-gate (void) printf((start != pc)? 563*0Sstevel@tonic-gate ") + %lx\n" : ")\n", (long)(pc - start)); 564*0Sstevel@tonic-gate 565*0Sstevel@tonic-gate /* 566*0Sstevel@tonic-gate * If the frame's pc is in the "sigh" (a.k.a. signal handler, signal 567*0Sstevel@tonic-gate * hack, or *sigh* ...) range, then we're about to cross a signal 568*0Sstevel@tonic-gate * frame. The signal number is the first argument to this function. 569*0Sstevel@tonic-gate */ 570*0Sstevel@tonic-gate if (pc - sigh.st_value < sigh.st_size) { 571*0Sstevel@tonic-gate if (sig2str((int)argv[0], buff) == -1) 572*0Sstevel@tonic-gate (void) strcpy(buff, " Unknown"); 573*0Sstevel@tonic-gate (void) printf(" --- called from signal handler with " 574*0Sstevel@tonic-gate "signal %d (SIG%s) ---\n", (int)argv[0], buff); 575*0Sstevel@tonic-gate } 576*0Sstevel@tonic-gate 577*0Sstevel@tonic-gate return (0); 578*0Sstevel@tonic-gate } 579*0Sstevel@tonic-gate 580*0Sstevel@tonic-gate static void 581*0Sstevel@tonic-gate print_zombie(struct ps_prochandle *Pr, struct threadinfo *tip) 582*0Sstevel@tonic-gate { 583*0Sstevel@tonic-gate char buff[255]; 584*0Sstevel@tonic-gate GElf_Sym sym; 585*0Sstevel@tonic-gate uintptr_t start; 586*0Sstevel@tonic-gate int length = (is64? 16 : 8); 587*0Sstevel@tonic-gate 588*0Sstevel@tonic-gate (void) sprintf(buff, "%.*lx", length, (long)tip->startfunc); 589*0Sstevel@tonic-gate (void) strcpy(buff + length, " ????????"); 590*0Sstevel@tonic-gate if (Plookup_by_addr(Pr, tip->startfunc, 591*0Sstevel@tonic-gate buff + 1 + length, sizeof (buff) - 1 - length, &sym) == 0) 592*0Sstevel@tonic-gate start = sym.st_value; 593*0Sstevel@tonic-gate else 594*0Sstevel@tonic-gate start = tip->startfunc; 595*0Sstevel@tonic-gate (void) printf(" %s()", buff); 596*0Sstevel@tonic-gate if (start != tip->startfunc) /* doesn't happen? */ 597*0Sstevel@tonic-gate (void) printf("+%lx", (long)(tip->startfunc - start)); 598*0Sstevel@tonic-gate (void) printf(", exit value = 0x%.*lx\n", length, (long)tip->exitval); 599*0Sstevel@tonic-gate (void) printf("\t** zombie " 600*0Sstevel@tonic-gate "(exited, not detached, not yet joined) **\n"); 601*0Sstevel@tonic-gate } 602*0Sstevel@tonic-gate 603*0Sstevel@tonic-gate static void 604*0Sstevel@tonic-gate print_syscall(const lwpstatus_t *psp, prgregset_t reg) 605*0Sstevel@tonic-gate { 606*0Sstevel@tonic-gate char sname[32]; 607*0Sstevel@tonic-gate int length = (is64? 16 : 8); 608*0Sstevel@tonic-gate uint_t i; 609*0Sstevel@tonic-gate 610*0Sstevel@tonic-gate (void) proc_sysname(psp->pr_syscall, sname, sizeof (sname)); 611*0Sstevel@tonic-gate (void) printf(" %.*lx %-8s (", length, (long)reg[R_PC], sname); 612*0Sstevel@tonic-gate for (i = 0; i < psp->pr_nsysarg; i++) 613*0Sstevel@tonic-gate (void) printf((i+1 == psp->pr_nsysarg)? "%lx" : "%lx, ", 614*0Sstevel@tonic-gate (long)psp->pr_sysarg[i]); 615*0Sstevel@tonic-gate (void) printf(")\n"); 616*0Sstevel@tonic-gate } 617*0Sstevel@tonic-gate 618*0Sstevel@tonic-gate static void 619*0Sstevel@tonic-gate call_stack(pstack_handle_t *h, const lwpstatus_t *psp) 620*0Sstevel@tonic-gate { 621*0Sstevel@tonic-gate prgregset_t reg; 622*0Sstevel@tonic-gate 623*0Sstevel@tonic-gate (void) memcpy(reg, psp->pr_reg, sizeof (reg)); 624*0Sstevel@tonic-gate 625*0Sstevel@tonic-gate if ((psp->pr_flags & (PR_ASLEEP|PR_VFORKP)) || 626*0Sstevel@tonic-gate ((psp->pr_flags & PR_ISTOP) && 627*0Sstevel@tonic-gate (psp->pr_why == PR_SYSENTRY || 628*0Sstevel@tonic-gate psp->pr_why == PR_SYSEXIT))) { 629*0Sstevel@tonic-gate print_syscall(psp, reg); 630*0Sstevel@tonic-gate h->ignore_frame = 1; 631*0Sstevel@tonic-gate } else { 632*0Sstevel@tonic-gate h->ignore_frame = 0; 633*0Sstevel@tonic-gate } 634*0Sstevel@tonic-gate 635*0Sstevel@tonic-gate (void) Pstack_iter(h->proc, reg, print_frame, h); 636*0Sstevel@tonic-gate } 637*0Sstevel@tonic-gate 638*0Sstevel@tonic-gate /*ARGSUSED*/ 639*0Sstevel@tonic-gate static int 640*0Sstevel@tonic-gate jvm_object_iter(void *cd, const prmap_t *pmp, const char *obj) 641*0Sstevel@tonic-gate { 642*0Sstevel@tonic-gate char path[PATH_MAX]; 643*0Sstevel@tonic-gate char *name; 644*0Sstevel@tonic-gate char *s1, *s2; 645*0Sstevel@tonic-gate struct ps_prochandle *Pr = cd; 646*0Sstevel@tonic-gate 647*0Sstevel@tonic-gate if ((name = strstr(obj, "/libjvm.so")) == NULL) 648*0Sstevel@tonic-gate name = strstr(obj, "/libjvm_g.so"); 649*0Sstevel@tonic-gate 650*0Sstevel@tonic-gate if (name) { 651*0Sstevel@tonic-gate (void) strcpy(path, obj); 652*0Sstevel@tonic-gate if (Pstatus(Pr)->pr_dmodel != PR_MODEL_NATIVE) { 653*0Sstevel@tonic-gate s1 = name; 654*0Sstevel@tonic-gate s2 = path + (s1 - obj); 655*0Sstevel@tonic-gate (void) strcpy(s2, "/64"); 656*0Sstevel@tonic-gate s2 += 3; 657*0Sstevel@tonic-gate (void) strcpy(s2, s1); 658*0Sstevel@tonic-gate } 659*0Sstevel@tonic-gate 660*0Sstevel@tonic-gate s1 = strstr(obj, ".so"); 661*0Sstevel@tonic-gate s2 = strstr(path, ".so"); 662*0Sstevel@tonic-gate (void) strcpy(s2, "_db"); 663*0Sstevel@tonic-gate s2 += 3; 664*0Sstevel@tonic-gate (void) strcpy(s2, s1); 665*0Sstevel@tonic-gate 666*0Sstevel@tonic-gate if ((libjvm = dlopen(path, RTLD_LAZY|RTLD_GLOBAL)) != NULL) 667*0Sstevel@tonic-gate return (1); 668*0Sstevel@tonic-gate } 669*0Sstevel@tonic-gate 670*0Sstevel@tonic-gate return (0); 671*0Sstevel@tonic-gate } 672*0Sstevel@tonic-gate 673*0Sstevel@tonic-gate static jvm_agent_t * 674*0Sstevel@tonic-gate load_libjvm(struct ps_prochandle *Pr) 675*0Sstevel@tonic-gate { 676*0Sstevel@tonic-gate jvm_agent_t *ret; 677*0Sstevel@tonic-gate 678*0Sstevel@tonic-gate (void) Pobject_iter(Pr, jvm_object_iter, Pr); 679*0Sstevel@tonic-gate 680*0Sstevel@tonic-gate if (libjvm) { 681*0Sstevel@tonic-gate j_agent_create = (j_agent_create_f) 682*0Sstevel@tonic-gate dlsym(libjvm, "Jagent_create"); 683*0Sstevel@tonic-gate j_agent_destroy = (j_agent_destroy_f) 684*0Sstevel@tonic-gate dlsym(libjvm, "Jagent_destroy"); 685*0Sstevel@tonic-gate j_frame_iter = (j_frame_iter_f) 686*0Sstevel@tonic-gate dlsym(libjvm, "Jframe_iter"); 687*0Sstevel@tonic-gate 688*0Sstevel@tonic-gate if (j_agent_create == NULL || j_agent_destroy == NULL || 689*0Sstevel@tonic-gate j_frame_iter == NULL || 690*0Sstevel@tonic-gate (ret = j_agent_create(Pr, JVM_DB_VERSION)) == NULL) { 691*0Sstevel@tonic-gate reset_libjvm(NULL); 692*0Sstevel@tonic-gate return (NULL); 693*0Sstevel@tonic-gate } 694*0Sstevel@tonic-gate 695*0Sstevel@tonic-gate return (ret); 696*0Sstevel@tonic-gate } 697*0Sstevel@tonic-gate 698*0Sstevel@tonic-gate return (NULL); 699*0Sstevel@tonic-gate } 700*0Sstevel@tonic-gate 701*0Sstevel@tonic-gate static void 702*0Sstevel@tonic-gate reset_libjvm(jvm_agent_t *agent) 703*0Sstevel@tonic-gate { 704*0Sstevel@tonic-gate if (libjvm) { 705*0Sstevel@tonic-gate if (agent) 706*0Sstevel@tonic-gate j_agent_destroy(agent); 707*0Sstevel@tonic-gate 708*0Sstevel@tonic-gate (void) dlclose(libjvm); 709*0Sstevel@tonic-gate } 710*0Sstevel@tonic-gate 711*0Sstevel@tonic-gate j_agent_create = NULL; 712*0Sstevel@tonic-gate j_agent_destroy = NULL; 713*0Sstevel@tonic-gate j_frame_iter = NULL; 714*0Sstevel@tonic-gate libjvm = NULL; 715*0Sstevel@tonic-gate } 716