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 2005 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 <mdb/mdb_modapi.h> 30*0Sstevel@tonic-gate #include <mdb/mdb_ctf.h> 31*0Sstevel@tonic-gate #include <sys/cpuvar.h> 32*0Sstevel@tonic-gate #include <sys/systm.h> 33*0Sstevel@tonic-gate #include <sys/traptrace.h> 34*0Sstevel@tonic-gate #include <sys/avintr.h> 35*0Sstevel@tonic-gate #include <sys/systm.h> 36*0Sstevel@tonic-gate #include <sys/trap.h> 37*0Sstevel@tonic-gate #include <sys/mutex.h> 38*0Sstevel@tonic-gate #include <sys/mutex_impl.h> 39*0Sstevel@tonic-gate #include "i86mmu.h" 40*0Sstevel@tonic-gate 41*0Sstevel@tonic-gate #define TT_HDLR_WIDTH 17 42*0Sstevel@tonic-gate 43*0Sstevel@tonic-gate static int 44*0Sstevel@tonic-gate ttrace_ttr_size_check(void) 45*0Sstevel@tonic-gate { 46*0Sstevel@tonic-gate mdb_ctf_id_t ttrtid; 47*0Sstevel@tonic-gate ssize_t ttr_size; 48*0Sstevel@tonic-gate 49*0Sstevel@tonic-gate if (mdb_ctf_lookup_by_name("trap_trace_rec_t", &ttrtid) != 0 || 50*0Sstevel@tonic-gate mdb_ctf_type_resolve(ttrtid, &ttrtid) != 0) { 51*0Sstevel@tonic-gate mdb_warn("failed to determine size of trap_trace_rec_t; " 52*0Sstevel@tonic-gate "non-TRAPTRACE kernel?\n"); 53*0Sstevel@tonic-gate return (0); 54*0Sstevel@tonic-gate } 55*0Sstevel@tonic-gate 56*0Sstevel@tonic-gate if ((ttr_size = mdb_ctf_type_size(ttrtid)) != 57*0Sstevel@tonic-gate sizeof (trap_trace_rec_t)) { 58*0Sstevel@tonic-gate /* 59*0Sstevel@tonic-gate * On Intel machines, this will happen when TTR_STACK_DEPTH 60*0Sstevel@tonic-gate * is changed. This code could be smarter, and could 61*0Sstevel@tonic-gate * dynamically adapt to different depths, but not until a 62*0Sstevel@tonic-gate * need for such adaptation is demonstrated. 63*0Sstevel@tonic-gate */ 64*0Sstevel@tonic-gate mdb_warn("size of trap_trace_rec_t (%d bytes) doesn't " 65*0Sstevel@tonic-gate "match expected %d\n", ttr_size, sizeof (trap_trace_rec_t)); 66*0Sstevel@tonic-gate return (0); 67*0Sstevel@tonic-gate } 68*0Sstevel@tonic-gate 69*0Sstevel@tonic-gate return (1); 70*0Sstevel@tonic-gate } 71*0Sstevel@tonic-gate 72*0Sstevel@tonic-gate int 73*0Sstevel@tonic-gate ttrace_walk_init(mdb_walk_state_t *wsp) 74*0Sstevel@tonic-gate { 75*0Sstevel@tonic-gate trap_trace_ctl_t *ttcp; 76*0Sstevel@tonic-gate size_t ttc_size = sizeof (trap_trace_ctl_t) * NCPU; 77*0Sstevel@tonic-gate int i; 78*0Sstevel@tonic-gate 79*0Sstevel@tonic-gate if (!ttrace_ttr_size_check()) 80*0Sstevel@tonic-gate return (WALK_ERR); 81*0Sstevel@tonic-gate 82*0Sstevel@tonic-gate ttcp = mdb_zalloc(ttc_size, UM_SLEEP); 83*0Sstevel@tonic-gate 84*0Sstevel@tonic-gate if (wsp->walk_addr != NULL) { 85*0Sstevel@tonic-gate mdb_warn("ttrace only supports global walks\n"); 86*0Sstevel@tonic-gate return (WALK_ERR); 87*0Sstevel@tonic-gate } 88*0Sstevel@tonic-gate 89*0Sstevel@tonic-gate if (mdb_readsym(ttcp, ttc_size, "trap_trace_ctl") == -1) { 90*0Sstevel@tonic-gate mdb_warn("symbol 'trap_trace_ctl' not found; " 91*0Sstevel@tonic-gate "non-TRAPTRACE kernel?\n"); 92*0Sstevel@tonic-gate mdb_free(ttcp, ttc_size); 93*0Sstevel@tonic-gate return (WALK_ERR); 94*0Sstevel@tonic-gate } 95*0Sstevel@tonic-gate 96*0Sstevel@tonic-gate /* 97*0Sstevel@tonic-gate * We'll poach the ttc_current pointer (which isn't used for 98*0Sstevel@tonic-gate * anything) to store a pointer to our current TRAPTRACE record. 99*0Sstevel@tonic-gate * This allows us to only keep the array of trap_trace_ctl structures 100*0Sstevel@tonic-gate * as our walker state (ttc_current may be the only kernel data 101*0Sstevel@tonic-gate * structure member added exclusively to make writing the mdb walker 102*0Sstevel@tonic-gate * a little easier). 103*0Sstevel@tonic-gate */ 104*0Sstevel@tonic-gate for (i = 0; i < NCPU; i++) { 105*0Sstevel@tonic-gate trap_trace_ctl_t *ttc = &ttcp[i]; 106*0Sstevel@tonic-gate 107*0Sstevel@tonic-gate if (ttc->ttc_first == NULL) 108*0Sstevel@tonic-gate continue; 109*0Sstevel@tonic-gate 110*0Sstevel@tonic-gate /* 111*0Sstevel@tonic-gate * Assign ttc_current to be the last completed record. 112*0Sstevel@tonic-gate * Note that the error checking (i.e. in the ttc_next == 113*0Sstevel@tonic-gate * ttc_first case) is performed in the step function. 114*0Sstevel@tonic-gate */ 115*0Sstevel@tonic-gate ttc->ttc_current = ttc->ttc_next - sizeof (trap_trace_rec_t); 116*0Sstevel@tonic-gate } 117*0Sstevel@tonic-gate 118*0Sstevel@tonic-gate wsp->walk_data = ttcp; 119*0Sstevel@tonic-gate return (WALK_NEXT); 120*0Sstevel@tonic-gate } 121*0Sstevel@tonic-gate 122*0Sstevel@tonic-gate int 123*0Sstevel@tonic-gate ttrace_walk_step(mdb_walk_state_t *wsp) 124*0Sstevel@tonic-gate { 125*0Sstevel@tonic-gate trap_trace_ctl_t *ttcp = wsp->walk_data, *ttc, *latest_ttc; 126*0Sstevel@tonic-gate trap_trace_rec_t rec; 127*0Sstevel@tonic-gate int rval, i, recsize = sizeof (trap_trace_rec_t); 128*0Sstevel@tonic-gate hrtime_t latest = 0; 129*0Sstevel@tonic-gate 130*0Sstevel@tonic-gate /* 131*0Sstevel@tonic-gate * Loop through the CPUs, looking for the latest trap trace record 132*0Sstevel@tonic-gate * (we want to walk through the trap trace records in reverse 133*0Sstevel@tonic-gate * chronological order). 134*0Sstevel@tonic-gate */ 135*0Sstevel@tonic-gate for (i = 0; i < NCPU; i++) { 136*0Sstevel@tonic-gate ttc = &ttcp[i]; 137*0Sstevel@tonic-gate 138*0Sstevel@tonic-gate if (ttc->ttc_current == NULL) 139*0Sstevel@tonic-gate continue; 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gate if (ttc->ttc_current < ttc->ttc_first) 142*0Sstevel@tonic-gate ttc->ttc_current = ttc->ttc_limit - recsize; 143*0Sstevel@tonic-gate 144*0Sstevel@tonic-gate if (mdb_vread(&rec, sizeof (rec), ttc->ttc_current) == -1) { 145*0Sstevel@tonic-gate mdb_warn("couldn't read rec at %p", ttc->ttc_current); 146*0Sstevel@tonic-gate return (WALK_ERR); 147*0Sstevel@tonic-gate } 148*0Sstevel@tonic-gate 149*0Sstevel@tonic-gate if (rec.ttr_stamp > latest) { 150*0Sstevel@tonic-gate latest = rec.ttr_stamp; 151*0Sstevel@tonic-gate latest_ttc = ttc; 152*0Sstevel@tonic-gate } 153*0Sstevel@tonic-gate } 154*0Sstevel@tonic-gate 155*0Sstevel@tonic-gate if (latest == 0) 156*0Sstevel@tonic-gate return (WALK_DONE); 157*0Sstevel@tonic-gate 158*0Sstevel@tonic-gate ttc = latest_ttc; 159*0Sstevel@tonic-gate 160*0Sstevel@tonic-gate if (mdb_vread(&rec, sizeof (rec), ttc->ttc_current) == -1) { 161*0Sstevel@tonic-gate mdb_warn("couldn't read rec at %p", ttc->ttc_current); 162*0Sstevel@tonic-gate return (WALK_ERR); 163*0Sstevel@tonic-gate } 164*0Sstevel@tonic-gate 165*0Sstevel@tonic-gate rval = wsp->walk_callback(ttc->ttc_current, &rec, wsp->walk_cbdata); 166*0Sstevel@tonic-gate 167*0Sstevel@tonic-gate if (ttc->ttc_current == ttc->ttc_next) 168*0Sstevel@tonic-gate ttc->ttc_current = NULL; 169*0Sstevel@tonic-gate else 170*0Sstevel@tonic-gate ttc->ttc_current -= sizeof (trap_trace_rec_t); 171*0Sstevel@tonic-gate 172*0Sstevel@tonic-gate return (rval); 173*0Sstevel@tonic-gate } 174*0Sstevel@tonic-gate 175*0Sstevel@tonic-gate void 176*0Sstevel@tonic-gate ttrace_walk_fini(mdb_walk_state_t *wsp) 177*0Sstevel@tonic-gate { 178*0Sstevel@tonic-gate mdb_free(wsp->walk_data, sizeof (trap_trace_ctl_t) * NCPU); 179*0Sstevel@tonic-gate } 180*0Sstevel@tonic-gate 181*0Sstevel@tonic-gate static int 182*0Sstevel@tonic-gate ttrace_syscall(trap_trace_rec_t *rec) 183*0Sstevel@tonic-gate { 184*0Sstevel@tonic-gate GElf_Sym sym; 185*0Sstevel@tonic-gate int sysnum = rec->ttr_sysnum; 186*0Sstevel@tonic-gate uintptr_t addr; 187*0Sstevel@tonic-gate struct sysent sys; 188*0Sstevel@tonic-gate 189*0Sstevel@tonic-gate mdb_printf("%s%-*x", sysnum < 0x10 ? " " : "", 190*0Sstevel@tonic-gate sysnum < 0x10 ? 2 : 3, sysnum); 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate if (rec->ttr_sysnum > NSYSCALL) { 193*0Sstevel@tonic-gate mdb_printf("%-*d", TT_HDLR_WIDTH, rec->ttr_sysnum); 194*0Sstevel@tonic-gate return (0); 195*0Sstevel@tonic-gate } 196*0Sstevel@tonic-gate 197*0Sstevel@tonic-gate if (mdb_lookup_by_name("sysent", &sym) == -1) { 198*0Sstevel@tonic-gate mdb_warn("\ncouldn't find 'sysent'"); 199*0Sstevel@tonic-gate return (-1); 200*0Sstevel@tonic-gate } 201*0Sstevel@tonic-gate 202*0Sstevel@tonic-gate addr = (uintptr_t)sym.st_value + sysnum * sizeof (struct sysent); 203*0Sstevel@tonic-gate 204*0Sstevel@tonic-gate if (addr >= (uintptr_t)sym.st_value + sym.st_size) { 205*0Sstevel@tonic-gate mdb_warn("\nsysnum %d out-of-range\n", sysnum); 206*0Sstevel@tonic-gate return (-1); 207*0Sstevel@tonic-gate } 208*0Sstevel@tonic-gate 209*0Sstevel@tonic-gate if (mdb_vread(&sys, sizeof (sys), addr) == -1) { 210*0Sstevel@tonic-gate mdb_warn("\nfailed to read sysent at %p", addr); 211*0Sstevel@tonic-gate return (-1); 212*0Sstevel@tonic-gate } 213*0Sstevel@tonic-gate 214*0Sstevel@tonic-gate mdb_printf("%-*a", TT_HDLR_WIDTH, sys.sy_callc); 215*0Sstevel@tonic-gate 216*0Sstevel@tonic-gate return (0); 217*0Sstevel@tonic-gate } 218*0Sstevel@tonic-gate 219*0Sstevel@tonic-gate static int 220*0Sstevel@tonic-gate ttrace_interrupt(trap_trace_rec_t *rec) 221*0Sstevel@tonic-gate { 222*0Sstevel@tonic-gate GElf_Sym sym; 223*0Sstevel@tonic-gate uintptr_t addr; 224*0Sstevel@tonic-gate struct av_head hd; 225*0Sstevel@tonic-gate struct autovec av; 226*0Sstevel@tonic-gate 227*0Sstevel@tonic-gate if (rec->ttr_regs.r_trapno == T_SOFTINT) { 228*0Sstevel@tonic-gate mdb_printf("%2s %-*s", "-", TT_HDLR_WIDTH, "(fakesoftint)"); 229*0Sstevel@tonic-gate return (0); 230*0Sstevel@tonic-gate } 231*0Sstevel@tonic-gate 232*0Sstevel@tonic-gate mdb_printf("%2x ", rec->ttr_vector); 233*0Sstevel@tonic-gate 234*0Sstevel@tonic-gate if (mdb_lookup_by_name("autovect", &sym) == -1) { 235*0Sstevel@tonic-gate mdb_warn("\ncouldn't find 'autovect'"); 236*0Sstevel@tonic-gate return (-1); 237*0Sstevel@tonic-gate } 238*0Sstevel@tonic-gate 239*0Sstevel@tonic-gate addr = (uintptr_t)sym.st_value + 240*0Sstevel@tonic-gate rec->ttr_vector * sizeof (struct av_head); 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate if (addr >= (uintptr_t)sym.st_value + sym.st_size) { 243*0Sstevel@tonic-gate mdb_warn("\nav_head for vec %x is corrupt\n", rec->ttr_vector); 244*0Sstevel@tonic-gate return (-1); 245*0Sstevel@tonic-gate } 246*0Sstevel@tonic-gate 247*0Sstevel@tonic-gate if (mdb_vread(&hd, sizeof (hd), addr) == -1) { 248*0Sstevel@tonic-gate mdb_warn("\ncouldn't read av_head for vec %x", rec->ttr_vector); 249*0Sstevel@tonic-gate return (-1); 250*0Sstevel@tonic-gate } 251*0Sstevel@tonic-gate 252*0Sstevel@tonic-gate if (hd.avh_link == NULL) { 253*0Sstevel@tonic-gate mdb_printf("%-*s", TT_HDLR_WIDTH, "(spurious)"); 254*0Sstevel@tonic-gate } else { 255*0Sstevel@tonic-gate if (mdb_vread(&av, sizeof (av), (uintptr_t)hd.avh_link) == -1) { 256*0Sstevel@tonic-gate mdb_warn("couldn't read autovec at %p", 257*0Sstevel@tonic-gate (uintptr_t)hd.avh_link); 258*0Sstevel@tonic-gate } 259*0Sstevel@tonic-gate 260*0Sstevel@tonic-gate mdb_printf("%-*a", TT_HDLR_WIDTH, av.av_vector); 261*0Sstevel@tonic-gate } 262*0Sstevel@tonic-gate 263*0Sstevel@tonic-gate return (0); 264*0Sstevel@tonic-gate } 265*0Sstevel@tonic-gate 266*0Sstevel@tonic-gate static struct { 267*0Sstevel@tonic-gate int tt_trapno; 268*0Sstevel@tonic-gate char *tt_name; 269*0Sstevel@tonic-gate } ttrace_traps[] = { 270*0Sstevel@tonic-gate { T_ZERODIV, "divide-error" }, 271*0Sstevel@tonic-gate { T_SGLSTP, "debug-exception" }, 272*0Sstevel@tonic-gate { T_NMIFLT, "nmi-interrupt" }, 273*0Sstevel@tonic-gate { T_BPTFLT, "breakpoint" }, 274*0Sstevel@tonic-gate { T_OVFLW, "into-overflow" }, 275*0Sstevel@tonic-gate { T_BOUNDFLT, "bound-exceeded" }, 276*0Sstevel@tonic-gate { T_ILLINST, "invalid-opcode" }, 277*0Sstevel@tonic-gate { T_NOEXTFLT, "device-not-avail" }, 278*0Sstevel@tonic-gate { T_DBLFLT, "double-fault" }, 279*0Sstevel@tonic-gate { T_EXTOVRFLT, "segment-overrun" }, 280*0Sstevel@tonic-gate { T_TSSFLT, "invalid-tss" }, 281*0Sstevel@tonic-gate { T_SEGFLT, "segment-not-pres" }, 282*0Sstevel@tonic-gate { T_STKFLT, "stack-fault" }, 283*0Sstevel@tonic-gate { T_GPFLT, "general-protectn" }, 284*0Sstevel@tonic-gate { T_PGFLT, "page-fault" }, 285*0Sstevel@tonic-gate { T_EXTERRFLT, "error-fault" }, 286*0Sstevel@tonic-gate { T_ALIGNMENT, "alignment-check" }, 287*0Sstevel@tonic-gate { T_MCE, "machine-check" }, 288*0Sstevel@tonic-gate { T_SIMDFPE, "sse-exception" }, 289*0Sstevel@tonic-gate { 0, NULL } 290*0Sstevel@tonic-gate }; 291*0Sstevel@tonic-gate 292*0Sstevel@tonic-gate static int 293*0Sstevel@tonic-gate ttrace_trap(trap_trace_rec_t *rec) 294*0Sstevel@tonic-gate { 295*0Sstevel@tonic-gate int i; 296*0Sstevel@tonic-gate 297*0Sstevel@tonic-gate mdb_printf("%2x ", rec->ttr_regs.r_trapno); 298*0Sstevel@tonic-gate 299*0Sstevel@tonic-gate for (i = 0; ttrace_traps[i].tt_name != NULL; i++) { 300*0Sstevel@tonic-gate if (rec->ttr_regs.r_trapno == ttrace_traps[i].tt_trapno) 301*0Sstevel@tonic-gate break; 302*0Sstevel@tonic-gate } 303*0Sstevel@tonic-gate 304*0Sstevel@tonic-gate if (ttrace_traps[i].tt_name == NULL) 305*0Sstevel@tonic-gate mdb_printf("%-*s", TT_HDLR_WIDTH, "(unknown)"); 306*0Sstevel@tonic-gate else 307*0Sstevel@tonic-gate mdb_printf("%-*s", TT_HDLR_WIDTH, ttrace_traps[i].tt_name); 308*0Sstevel@tonic-gate 309*0Sstevel@tonic-gate return (0); 310*0Sstevel@tonic-gate } 311*0Sstevel@tonic-gate 312*0Sstevel@tonic-gate static struct { 313*0Sstevel@tonic-gate uchar_t t_marker; 314*0Sstevel@tonic-gate char *t_name; 315*0Sstevel@tonic-gate int (*t_hdlr)(trap_trace_rec_t *); 316*0Sstevel@tonic-gate } ttrace_hdlr[] = { 317*0Sstevel@tonic-gate { TT_SYSCALL, "sysc", ttrace_syscall }, 318*0Sstevel@tonic-gate { TT_SYSENTER, "syse", ttrace_syscall }, 319*0Sstevel@tonic-gate { TT_SYSC, "asys", ttrace_syscall }, 320*0Sstevel@tonic-gate { TT_SYSC64, "sc64", ttrace_syscall }, 321*0Sstevel@tonic-gate { TT_INTERRUPT, "intr", ttrace_interrupt }, 322*0Sstevel@tonic-gate { TT_TRAP, "trap", ttrace_trap }, 323*0Sstevel@tonic-gate { 0, NULL, NULL } 324*0Sstevel@tonic-gate }; 325*0Sstevel@tonic-gate 326*0Sstevel@tonic-gate typedef struct ttrace_dcmd { 327*0Sstevel@tonic-gate processorid_t ttd_cpu; 328*0Sstevel@tonic-gate uint_t ttd_extended; 329*0Sstevel@tonic-gate trap_trace_ctl_t ttd_ttc[NCPU]; 330*0Sstevel@tonic-gate } ttrace_dcmd_t; 331*0Sstevel@tonic-gate 332*0Sstevel@tonic-gate #if defined(__amd64) 333*0Sstevel@tonic-gate 334*0Sstevel@tonic-gate #define DUMP(reg) #reg, regs->r_##reg 335*0Sstevel@tonic-gate #define THREEREGS " %3s: %16lx %3s: %16lx %3s: %16lx\n" 336*0Sstevel@tonic-gate #define TWOREGS " %3s: %16lx %3s: %16lx\n" 337*0Sstevel@tonic-gate 338*0Sstevel@tonic-gate static void 339*0Sstevel@tonic-gate ttrace_dumpregs(trap_trace_rec_t *rec) 340*0Sstevel@tonic-gate { 341*0Sstevel@tonic-gate struct regs *regs = &rec->ttr_regs; 342*0Sstevel@tonic-gate 343*0Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(rdi), DUMP(rsi), DUMP(rdx)); 344*0Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(rcx), DUMP(r8), DUMP(r9)); 345*0Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(rax), DUMP(rbx), DUMP(rbp)); 346*0Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(r10), DUMP(r11), DUMP(r12)); 347*0Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(r13), DUMP(r14), DUMP(r15)); 348*0Sstevel@tonic-gate mdb_printf(THREEREGS, "fsb", regs->r_fsbase, "gsb", regs->r_gsbase, 349*0Sstevel@tonic-gate DUMP(ds)); 350*0Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(es), DUMP(fs), DUMP(gs)); 351*0Sstevel@tonic-gate mdb_printf(THREEREGS, "trp", regs->r_trapno, DUMP(err), DUMP(rip)); 352*0Sstevel@tonic-gate mdb_printf(THREEREGS, DUMP(cs), DUMP(rfl), DUMP(rsp)); 353*0Sstevel@tonic-gate mdb_printf(TWOREGS, DUMP(ss), "cr2", rec->ttr_cr2); 354*0Sstevel@tonic-gate mdb_printf("\n"); 355*0Sstevel@tonic-gate } 356*0Sstevel@tonic-gate 357*0Sstevel@tonic-gate #else 358*0Sstevel@tonic-gate 359*0Sstevel@tonic-gate #define DUMP(reg) #reg, regs->r_##reg 360*0Sstevel@tonic-gate #define FOURREGS " %3s: %08x %3s: %08x %3s: %08x %3s: %08x\n" 361*0Sstevel@tonic-gate 362*0Sstevel@tonic-gate static void 363*0Sstevel@tonic-gate ttrace_dumpregs(trap_trace_rec_t *rec) 364*0Sstevel@tonic-gate { 365*0Sstevel@tonic-gate struct regs *regs = &rec->ttr_regs; 366*0Sstevel@tonic-gate 367*0Sstevel@tonic-gate mdb_printf(FOURREGS, DUMP(gs), DUMP(fs), DUMP(es), DUMP(ds)); 368*0Sstevel@tonic-gate mdb_printf(FOURREGS, DUMP(edi), DUMP(esi), DUMP(ebp), DUMP(esp)); 369*0Sstevel@tonic-gate mdb_printf(FOURREGS, DUMP(ebx), DUMP(edx), DUMP(ecx), DUMP(eax)); 370*0Sstevel@tonic-gate mdb_printf(FOURREGS, "trp", regs->r_trapno, DUMP(err), 371*0Sstevel@tonic-gate DUMP(pc), DUMP(cs)); 372*0Sstevel@tonic-gate mdb_printf(FOURREGS, DUMP(efl), "usp", regs->r_uesp, DUMP(ss), 373*0Sstevel@tonic-gate "cr2", rec->ttr_cr2); 374*0Sstevel@tonic-gate mdb_printf("\n"); 375*0Sstevel@tonic-gate } 376*0Sstevel@tonic-gate 377*0Sstevel@tonic-gate #endif /* __amd64 */ 378*0Sstevel@tonic-gate 379*0Sstevel@tonic-gate int 380*0Sstevel@tonic-gate ttrace_walk(uintptr_t addr, trap_trace_rec_t *rec, ttrace_dcmd_t *dcmd) 381*0Sstevel@tonic-gate { 382*0Sstevel@tonic-gate struct regs *regs = &rec->ttr_regs; 383*0Sstevel@tonic-gate processorid_t cpu = -1, i; 384*0Sstevel@tonic-gate 385*0Sstevel@tonic-gate for (i = 0; i < NCPU; i++) { 386*0Sstevel@tonic-gate if (addr >= dcmd->ttd_ttc[i].ttc_first && 387*0Sstevel@tonic-gate addr < dcmd->ttd_ttc[i].ttc_limit) { 388*0Sstevel@tonic-gate cpu = i; 389*0Sstevel@tonic-gate break; 390*0Sstevel@tonic-gate } 391*0Sstevel@tonic-gate } 392*0Sstevel@tonic-gate 393*0Sstevel@tonic-gate if (cpu == -1) { 394*0Sstevel@tonic-gate mdb_warn("couldn't find %p in any trap trace ctl\n", addr); 395*0Sstevel@tonic-gate return (WALK_ERR); 396*0Sstevel@tonic-gate } 397*0Sstevel@tonic-gate 398*0Sstevel@tonic-gate if (dcmd->ttd_cpu != -1 && cpu != dcmd->ttd_cpu) 399*0Sstevel@tonic-gate return (WALK_NEXT); 400*0Sstevel@tonic-gate 401*0Sstevel@tonic-gate mdb_printf("%3d %15llx ", cpu, rec->ttr_stamp); 402*0Sstevel@tonic-gate 403*0Sstevel@tonic-gate for (i = 0; ttrace_hdlr[i].t_hdlr != NULL; i++) { 404*0Sstevel@tonic-gate if (rec->ttr_marker != ttrace_hdlr[i].t_marker) 405*0Sstevel@tonic-gate continue; 406*0Sstevel@tonic-gate mdb_printf("%4s ", ttrace_hdlr[i].t_name); 407*0Sstevel@tonic-gate if (ttrace_hdlr[i].t_hdlr(rec) == -1) 408*0Sstevel@tonic-gate return (WALK_ERR); 409*0Sstevel@tonic-gate } 410*0Sstevel@tonic-gate 411*0Sstevel@tonic-gate mdb_printf("%a\n", regs->r_pc); 412*0Sstevel@tonic-gate 413*0Sstevel@tonic-gate if (dcmd->ttd_extended == FALSE) 414*0Sstevel@tonic-gate return (WALK_NEXT); 415*0Sstevel@tonic-gate 416*0Sstevel@tonic-gate ttrace_dumpregs(rec); 417*0Sstevel@tonic-gate 418*0Sstevel@tonic-gate if (rec->ttr_sdepth > 0) { 419*0Sstevel@tonic-gate for (i = 0; i < rec->ttr_sdepth; i++) { 420*0Sstevel@tonic-gate if (i >= TTR_STACK_DEPTH) { 421*0Sstevel@tonic-gate mdb_printf("%17s*** invalid ttr_sdepth (is %d, " 422*0Sstevel@tonic-gate "should be <= %d)\n", " ", rec->ttr_sdepth, 423*0Sstevel@tonic-gate TTR_STACK_DEPTH); 424*0Sstevel@tonic-gate break; 425*0Sstevel@tonic-gate } 426*0Sstevel@tonic-gate 427*0Sstevel@tonic-gate mdb_printf("%17s %a()\n", " ", rec->ttr_stack[i]); 428*0Sstevel@tonic-gate } 429*0Sstevel@tonic-gate mdb_printf("\n"); 430*0Sstevel@tonic-gate } 431*0Sstevel@tonic-gate 432*0Sstevel@tonic-gate return (WALK_NEXT); 433*0Sstevel@tonic-gate } 434*0Sstevel@tonic-gate 435*0Sstevel@tonic-gate int 436*0Sstevel@tonic-gate ttrace(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 437*0Sstevel@tonic-gate { 438*0Sstevel@tonic-gate ttrace_dcmd_t dcmd; 439*0Sstevel@tonic-gate trap_trace_ctl_t *ttc = dcmd.ttd_ttc; 440*0Sstevel@tonic-gate trap_trace_rec_t rec; 441*0Sstevel@tonic-gate size_t ttc_size = sizeof (trap_trace_ctl_t) * NCPU; 442*0Sstevel@tonic-gate 443*0Sstevel@tonic-gate if (!ttrace_ttr_size_check()) 444*0Sstevel@tonic-gate return (WALK_ERR); 445*0Sstevel@tonic-gate 446*0Sstevel@tonic-gate bzero(&dcmd, sizeof (dcmd)); 447*0Sstevel@tonic-gate dcmd.ttd_cpu = -1; 448*0Sstevel@tonic-gate dcmd.ttd_extended = FALSE; 449*0Sstevel@tonic-gate 450*0Sstevel@tonic-gate if (mdb_readsym(ttc, ttc_size, "trap_trace_ctl") == -1) { 451*0Sstevel@tonic-gate mdb_warn("symbol 'trap_trace_ctl' not found; " 452*0Sstevel@tonic-gate "non-TRAPTRACE kernel?\n"); 453*0Sstevel@tonic-gate return (DCMD_ERR); 454*0Sstevel@tonic-gate } 455*0Sstevel@tonic-gate 456*0Sstevel@tonic-gate if (mdb_getopts(argc, argv, 457*0Sstevel@tonic-gate 'x', MDB_OPT_SETBITS, TRUE, &dcmd.ttd_extended, NULL) != argc) 458*0Sstevel@tonic-gate return (DCMD_USAGE); 459*0Sstevel@tonic-gate 460*0Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) { 461*0Sstevel@tonic-gate mdb_printf("%3s %15s %4s %2s %-*s%s\n", "CPU", 462*0Sstevel@tonic-gate "TIMESTAMP", "TYPE", "VC", TT_HDLR_WIDTH, "HANDLER", 463*0Sstevel@tonic-gate "EIP"); 464*0Sstevel@tonic-gate } 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate if (flags & DCMD_ADDRSPEC) { 467*0Sstevel@tonic-gate if (addr >= NCPU) { 468*0Sstevel@tonic-gate if (mdb_vread(&rec, sizeof (rec), addr) == -1) { 469*0Sstevel@tonic-gate mdb_warn("couldn't read trap trace record " 470*0Sstevel@tonic-gate "at %p", addr); 471*0Sstevel@tonic-gate return (DCMD_ERR); 472*0Sstevel@tonic-gate } 473*0Sstevel@tonic-gate 474*0Sstevel@tonic-gate if (ttrace_walk(addr, &rec, &dcmd) == WALK_ERR) 475*0Sstevel@tonic-gate return (DCMD_ERR); 476*0Sstevel@tonic-gate 477*0Sstevel@tonic-gate return (DCMD_OK); 478*0Sstevel@tonic-gate } 479*0Sstevel@tonic-gate dcmd.ttd_cpu = addr; 480*0Sstevel@tonic-gate } 481*0Sstevel@tonic-gate 482*0Sstevel@tonic-gate if (mdb_walk("ttrace", (mdb_walk_cb_t)ttrace_walk, &dcmd) == -1) { 483*0Sstevel@tonic-gate mdb_warn("couldn't walk 'ttrace'"); 484*0Sstevel@tonic-gate return (DCMD_ERR); 485*0Sstevel@tonic-gate } 486*0Sstevel@tonic-gate 487*0Sstevel@tonic-gate return (DCMD_OK); 488*0Sstevel@tonic-gate } 489*0Sstevel@tonic-gate 490*0Sstevel@tonic-gate /*ARGSUSED*/ 491*0Sstevel@tonic-gate int 492*0Sstevel@tonic-gate mutex_owner_init(mdb_walk_state_t *wsp) 493*0Sstevel@tonic-gate { 494*0Sstevel@tonic-gate return (WALK_NEXT); 495*0Sstevel@tonic-gate } 496*0Sstevel@tonic-gate 497*0Sstevel@tonic-gate int 498*0Sstevel@tonic-gate mutex_owner_step(mdb_walk_state_t *wsp) 499*0Sstevel@tonic-gate { 500*0Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 501*0Sstevel@tonic-gate mutex_impl_t mtx; 502*0Sstevel@tonic-gate uintptr_t owner; 503*0Sstevel@tonic-gate kthread_t thr; 504*0Sstevel@tonic-gate 505*0Sstevel@tonic-gate if (mdb_vread(&mtx, sizeof (mtx), addr) == -1) 506*0Sstevel@tonic-gate return (WALK_ERR); 507*0Sstevel@tonic-gate 508*0Sstevel@tonic-gate if (!MUTEX_TYPE_ADAPTIVE(&mtx)) 509*0Sstevel@tonic-gate return (WALK_DONE); 510*0Sstevel@tonic-gate 511*0Sstevel@tonic-gate if ((owner = (uintptr_t)MUTEX_OWNER(&mtx)) == NULL) 512*0Sstevel@tonic-gate return (WALK_DONE); 513*0Sstevel@tonic-gate 514*0Sstevel@tonic-gate if (mdb_vread(&thr, sizeof (thr), owner) != -1) 515*0Sstevel@tonic-gate (void) wsp->walk_callback(owner, &thr, wsp->walk_cbdata); 516*0Sstevel@tonic-gate 517*0Sstevel@tonic-gate return (WALK_DONE); 518*0Sstevel@tonic-gate } 519*0Sstevel@tonic-gate 520*0Sstevel@tonic-gate static void 521*0Sstevel@tonic-gate gate_desc_dump(gate_desc_t *gate, const char *label, int header) 522*0Sstevel@tonic-gate { 523*0Sstevel@tonic-gate const char *lastnm; 524*0Sstevel@tonic-gate uint_t lastval; 525*0Sstevel@tonic-gate char type[4]; 526*0Sstevel@tonic-gate 527*0Sstevel@tonic-gate switch (gate->sgd_type) { 528*0Sstevel@tonic-gate case SDT_SYSIGT: 529*0Sstevel@tonic-gate strcpy(type, "int"); 530*0Sstevel@tonic-gate break; 531*0Sstevel@tonic-gate case SDT_SYSTGT: 532*0Sstevel@tonic-gate strcpy(type, "trp"); 533*0Sstevel@tonic-gate break; 534*0Sstevel@tonic-gate case SDT_SYSTASKGT: 535*0Sstevel@tonic-gate strcpy(type, "tsk"); 536*0Sstevel@tonic-gate break; 537*0Sstevel@tonic-gate default: 538*0Sstevel@tonic-gate (void) mdb_snprintf(type, sizeof (type), "%3x", gate->sgd_type); 539*0Sstevel@tonic-gate } 540*0Sstevel@tonic-gate 541*0Sstevel@tonic-gate #if defined(__amd64) 542*0Sstevel@tonic-gate lastnm = "IST"; 543*0Sstevel@tonic-gate lastval = gate->sgd_ist; 544*0Sstevel@tonic-gate #else 545*0Sstevel@tonic-gate lastnm = "STK"; 546*0Sstevel@tonic-gate lastval = gate->sgd_stkcpy; 547*0Sstevel@tonic-gate #endif 548*0Sstevel@tonic-gate 549*0Sstevel@tonic-gate if (header) { 550*0Sstevel@tonic-gate mdb_printf("%*s%<u>%-30s%</u> %<u>%-4s%</u> %<u>%3s%</u> " 551*0Sstevel@tonic-gate "%<u>%1s%</u> %<u>%3s%</u> %<u>%3s%</u>\n", strlen(label), 552*0Sstevel@tonic-gate "", "HANDLER", "SEL", "DPL", "P", "TYP", lastnm); 553*0Sstevel@tonic-gate } 554*0Sstevel@tonic-gate 555*0Sstevel@tonic-gate mdb_printf("%s", label); 556*0Sstevel@tonic-gate 557*0Sstevel@tonic-gate if (gate->sgd_type == SDT_SYSTASKGT) 558*0Sstevel@tonic-gate mdb_printf("%-30s ", "-"); 559*0Sstevel@tonic-gate else 560*0Sstevel@tonic-gate mdb_printf("%-30a ", GATESEG_GETOFFSET(gate)); 561*0Sstevel@tonic-gate 562*0Sstevel@tonic-gate mdb_printf("%4x %d %c %3s %2x\n", gate->sgd_selector, 563*0Sstevel@tonic-gate gate->sgd_dpl, (gate->sgd_p ? '+' : ' '), type, lastval); 564*0Sstevel@tonic-gate } 565*0Sstevel@tonic-gate 566*0Sstevel@tonic-gate /*ARGSUSED*/ 567*0Sstevel@tonic-gate static int 568*0Sstevel@tonic-gate gate_desc(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 569*0Sstevel@tonic-gate { 570*0Sstevel@tonic-gate gate_desc_t gate; 571*0Sstevel@tonic-gate 572*0Sstevel@tonic-gate if (argc != 0 || !(flags & DCMD_ADDRSPEC)) 573*0Sstevel@tonic-gate return (DCMD_USAGE); 574*0Sstevel@tonic-gate 575*0Sstevel@tonic-gate if (mdb_vread(&gate, sizeof (gate_desc_t), addr) != 576*0Sstevel@tonic-gate sizeof (gate_desc_t)) { 577*0Sstevel@tonic-gate mdb_warn("failed to read gate descriptor at %p\n", addr); 578*0Sstevel@tonic-gate return (DCMD_ERR); 579*0Sstevel@tonic-gate } 580*0Sstevel@tonic-gate 581*0Sstevel@tonic-gate gate_desc_dump(&gate, "", DCMD_HDRSPEC(flags)); 582*0Sstevel@tonic-gate 583*0Sstevel@tonic-gate return (DCMD_OK); 584*0Sstevel@tonic-gate } 585*0Sstevel@tonic-gate 586*0Sstevel@tonic-gate /*ARGSUSED*/ 587*0Sstevel@tonic-gate static int 588*0Sstevel@tonic-gate idt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 589*0Sstevel@tonic-gate { 590*0Sstevel@tonic-gate int i; 591*0Sstevel@tonic-gate 592*0Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 593*0Sstevel@tonic-gate GElf_Sym idt; 594*0Sstevel@tonic-gate 595*0Sstevel@tonic-gate if (mdb_lookup_by_name("idt0", &idt) < 0) { 596*0Sstevel@tonic-gate mdb_warn("failed to find idt0"); 597*0Sstevel@tonic-gate return (DCMD_ERR); 598*0Sstevel@tonic-gate } 599*0Sstevel@tonic-gate 600*0Sstevel@tonic-gate addr = idt.st_value; 601*0Sstevel@tonic-gate } 602*0Sstevel@tonic-gate 603*0Sstevel@tonic-gate for (i = 0; i < NIDT; i++, addr += sizeof (gate_desc_t)) { 604*0Sstevel@tonic-gate gate_desc_t gate; 605*0Sstevel@tonic-gate char label[6]; 606*0Sstevel@tonic-gate 607*0Sstevel@tonic-gate if (mdb_vread(&gate, sizeof (gate_desc_t), addr) != 608*0Sstevel@tonic-gate sizeof (gate_desc_t)) { 609*0Sstevel@tonic-gate mdb_warn("failed to read gate descriptor at %p\n", 610*0Sstevel@tonic-gate addr); 611*0Sstevel@tonic-gate return (DCMD_ERR); 612*0Sstevel@tonic-gate } 613*0Sstevel@tonic-gate 614*0Sstevel@tonic-gate (void) mdb_snprintf(label, sizeof (label), "%3d: ", i); 615*0Sstevel@tonic-gate gate_desc_dump(&gate, label, i == 0); 616*0Sstevel@tonic-gate } 617*0Sstevel@tonic-gate 618*0Sstevel@tonic-gate return (DCMD_OK); 619*0Sstevel@tonic-gate } 620*0Sstevel@tonic-gate 621*0Sstevel@tonic-gate static const mdb_dcmd_t dcmds[] = { 622*0Sstevel@tonic-gate { "gate_desc", ":", "dump a gate descriptor", gate_desc }, 623*0Sstevel@tonic-gate { "idt", ":[-v]", "dump an IDT", idt }, 624*0Sstevel@tonic-gate { "ttrace", "[-x]", "dump trap trace buffers", ttrace }, 625*0Sstevel@tonic-gate { "vatopfn", ":[-a as]", "translate address to physical page", 626*0Sstevel@tonic-gate va2pfn_dcmd }, 627*0Sstevel@tonic-gate { "report_maps", "", "Given PFN, report mappings / page table usage", 628*0Sstevel@tonic-gate report_maps_dcmd }, 629*0Sstevel@tonic-gate { "ptable", "", "Dump contents of a page table", ptable_dcmd }, 630*0Sstevel@tonic-gate { "pte", ":[-p XXXXX] [-l N]", "print human readable page table entry", 631*0Sstevel@tonic-gate pte_dcmd }, 632*0Sstevel@tonic-gate { "page_num2pp", ":", "page frame number to page structure", 633*0Sstevel@tonic-gate page_num2pp }, 634*0Sstevel@tonic-gate { "memseg_list", ":", "show memseg list", memseg_list }, 635*0Sstevel@tonic-gate { NULL } 636*0Sstevel@tonic-gate }; 637*0Sstevel@tonic-gate 638*0Sstevel@tonic-gate static const mdb_walker_t walkers[] = { 639*0Sstevel@tonic-gate { "ttrace", "walks trap trace buffers in reverse chronological order", 640*0Sstevel@tonic-gate ttrace_walk_init, ttrace_walk_step, ttrace_walk_fini }, 641*0Sstevel@tonic-gate { "mutex_owner", "walks the owner of a mutex", 642*0Sstevel@tonic-gate mutex_owner_init, mutex_owner_step }, 643*0Sstevel@tonic-gate { "memseg", "walk the memseg structures", 644*0Sstevel@tonic-gate memseg_walk_init, memseg_walk_step, memseg_walk_fini }, 645*0Sstevel@tonic-gate { NULL } 646*0Sstevel@tonic-gate }; 647*0Sstevel@tonic-gate 648*0Sstevel@tonic-gate static const mdb_modinfo_t modinfo = { MDB_API_VERSION, dcmds, walkers }; 649*0Sstevel@tonic-gate 650*0Sstevel@tonic-gate const mdb_modinfo_t * 651*0Sstevel@tonic-gate _mdb_init(void) 652*0Sstevel@tonic-gate { 653*0Sstevel@tonic-gate return (&modinfo); 654*0Sstevel@tonic-gate } 655