17c478bd9Sstevel@tonic-gate /* 27c478bd9Sstevel@tonic-gate * CDDL HEADER START 37c478bd9Sstevel@tonic-gate * 47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5c97ad5cdSakolb * Common Development and Distribution License (the "License"). 6c97ad5cdSakolb * You may not use this file except in compliance with the License. 77c478bd9Sstevel@tonic-gate * 87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions 117c478bd9Sstevel@tonic-gate * and limitations under the License. 127c478bd9Sstevel@tonic-gate * 137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 187c478bd9Sstevel@tonic-gate * 197c478bd9Sstevel@tonic-gate * CDDL HEADER END 207c478bd9Sstevel@tonic-gate */ 217c478bd9Sstevel@tonic-gate /* 22346799e8SJonathan W Adams * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 237c478bd9Sstevel@tonic-gate * Use is subject to license terms. 247c478bd9Sstevel@tonic-gate */ 25*9583f717SMarcel Telka /* 26*9583f717SMarcel Telka * Copyright 2013 Nexenta Systems, Inc. All rights reserved. 27*9583f717SMarcel Telka */ 287c478bd9Sstevel@tonic-gate 297c478bd9Sstevel@tonic-gate 307c478bd9Sstevel@tonic-gate #include <mdb/mdb_modapi.h> 31*9583f717SMarcel Telka #include <mdb/mdb_ks.h> 327c478bd9Sstevel@tonic-gate #include <sys/types.h> 337c478bd9Sstevel@tonic-gate #include <sys/thread.h> 347c478bd9Sstevel@tonic-gate #include <sys/lwp.h> 357c478bd9Sstevel@tonic-gate #include <sys/proc.h> 367c478bd9Sstevel@tonic-gate #include <sys/cpuvar.h> 377c478bd9Sstevel@tonic-gate #include <sys/cpupart.h> 387c478bd9Sstevel@tonic-gate #include <sys/disp.h> 39ab4a9bebSjohnlev #include <sys/taskq_impl.h> 40bff31d89SPhilippe Jung #include <sys/stack.h> 41bff31d89SPhilippe Jung 42bff31d89SPhilippe Jung #ifndef STACK_BIAS 43bff31d89SPhilippe Jung #define STACK_BIAS 0 44bff31d89SPhilippe Jung #endif 457c478bd9Sstevel@tonic-gate 467c478bd9Sstevel@tonic-gate typedef struct thread_walk { 477c478bd9Sstevel@tonic-gate kthread_t *tw_thread; 487c478bd9Sstevel@tonic-gate uintptr_t tw_last; 497c478bd9Sstevel@tonic-gate uint_t tw_inproc; 507c478bd9Sstevel@tonic-gate uint_t tw_step; 517c478bd9Sstevel@tonic-gate } thread_walk_t; 527c478bd9Sstevel@tonic-gate 537c478bd9Sstevel@tonic-gate int 547c478bd9Sstevel@tonic-gate thread_walk_init(mdb_walk_state_t *wsp) 557c478bd9Sstevel@tonic-gate { 567c478bd9Sstevel@tonic-gate thread_walk_t *twp = mdb_alloc(sizeof (thread_walk_t), UM_SLEEP); 577c478bd9Sstevel@tonic-gate 587c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) { 597c478bd9Sstevel@tonic-gate if (mdb_readvar(&wsp->walk_addr, "allthreads") == -1) { 607c478bd9Sstevel@tonic-gate mdb_warn("failed to read 'allthreads'"); 617c478bd9Sstevel@tonic-gate mdb_free(twp, sizeof (thread_walk_t)); 627c478bd9Sstevel@tonic-gate return (WALK_ERR); 637c478bd9Sstevel@tonic-gate } 647c478bd9Sstevel@tonic-gate 657c478bd9Sstevel@tonic-gate twp->tw_inproc = FALSE; 667c478bd9Sstevel@tonic-gate 677c478bd9Sstevel@tonic-gate } else { 687c478bd9Sstevel@tonic-gate proc_t pr; 697c478bd9Sstevel@tonic-gate 707c478bd9Sstevel@tonic-gate if (mdb_vread(&pr, sizeof (proc_t), wsp->walk_addr) == -1) { 717c478bd9Sstevel@tonic-gate mdb_warn("failed to read proc at %p", wsp->walk_addr); 727c478bd9Sstevel@tonic-gate mdb_free(twp, sizeof (thread_walk_t)); 737c478bd9Sstevel@tonic-gate return (WALK_ERR); 747c478bd9Sstevel@tonic-gate } 757c478bd9Sstevel@tonic-gate 767c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)pr.p_tlist; 777c478bd9Sstevel@tonic-gate twp->tw_inproc = TRUE; 787c478bd9Sstevel@tonic-gate } 797c478bd9Sstevel@tonic-gate 807c478bd9Sstevel@tonic-gate twp->tw_thread = mdb_alloc(sizeof (kthread_t), UM_SLEEP); 817c478bd9Sstevel@tonic-gate twp->tw_last = wsp->walk_addr; 827c478bd9Sstevel@tonic-gate twp->tw_step = FALSE; 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate wsp->walk_data = twp; 857c478bd9Sstevel@tonic-gate return (WALK_NEXT); 867c478bd9Sstevel@tonic-gate } 877c478bd9Sstevel@tonic-gate 887c478bd9Sstevel@tonic-gate int 897c478bd9Sstevel@tonic-gate thread_walk_step(mdb_walk_state_t *wsp) 907c478bd9Sstevel@tonic-gate { 917c478bd9Sstevel@tonic-gate thread_walk_t *twp = (thread_walk_t *)wsp->walk_data; 927c478bd9Sstevel@tonic-gate int status; 937c478bd9Sstevel@tonic-gate 947c478bd9Sstevel@tonic-gate if (wsp->walk_addr == NULL) 957c478bd9Sstevel@tonic-gate return (WALK_DONE); /* Proc has 0 threads or allthreads = 0 */ 967c478bd9Sstevel@tonic-gate 977c478bd9Sstevel@tonic-gate if (twp->tw_step && wsp->walk_addr == twp->tw_last) 987c478bd9Sstevel@tonic-gate return (WALK_DONE); /* We've wrapped around */ 997c478bd9Sstevel@tonic-gate 1007c478bd9Sstevel@tonic-gate if (mdb_vread(twp->tw_thread, sizeof (kthread_t), 1017c478bd9Sstevel@tonic-gate wsp->walk_addr) == -1) { 1027c478bd9Sstevel@tonic-gate mdb_warn("failed to read thread at %p", wsp->walk_addr); 1037c478bd9Sstevel@tonic-gate return (WALK_DONE); 1047c478bd9Sstevel@tonic-gate } 1057c478bd9Sstevel@tonic-gate 1067c478bd9Sstevel@tonic-gate status = wsp->walk_callback(wsp->walk_addr, twp->tw_thread, 1077c478bd9Sstevel@tonic-gate wsp->walk_cbdata); 1087c478bd9Sstevel@tonic-gate 1097c478bd9Sstevel@tonic-gate if (twp->tw_inproc) 1107c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)twp->tw_thread->t_forw; 1117c478bd9Sstevel@tonic-gate else 1127c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)twp->tw_thread->t_next; 1137c478bd9Sstevel@tonic-gate 1147c478bd9Sstevel@tonic-gate twp->tw_step = TRUE; 1157c478bd9Sstevel@tonic-gate return (status); 1167c478bd9Sstevel@tonic-gate } 1177c478bd9Sstevel@tonic-gate 1187c478bd9Sstevel@tonic-gate void 1197c478bd9Sstevel@tonic-gate thread_walk_fini(mdb_walk_state_t *wsp) 1207c478bd9Sstevel@tonic-gate { 1217c478bd9Sstevel@tonic-gate thread_walk_t *twp = (thread_walk_t *)wsp->walk_data; 1227c478bd9Sstevel@tonic-gate 1237c478bd9Sstevel@tonic-gate mdb_free(twp->tw_thread, sizeof (kthread_t)); 1247c478bd9Sstevel@tonic-gate mdb_free(twp, sizeof (thread_walk_t)); 1257c478bd9Sstevel@tonic-gate } 1267c478bd9Sstevel@tonic-gate 1277c478bd9Sstevel@tonic-gate int 1287c478bd9Sstevel@tonic-gate deathrow_walk_init(mdb_walk_state_t *wsp) 1297c478bd9Sstevel@tonic-gate { 1307c478bd9Sstevel@tonic-gate if (mdb_layered_walk("thread_deathrow", wsp) == -1) { 1317c478bd9Sstevel@tonic-gate mdb_warn("couldn't walk 'thread_deathrow'"); 1327c478bd9Sstevel@tonic-gate return (WALK_ERR); 1337c478bd9Sstevel@tonic-gate } 1347c478bd9Sstevel@tonic-gate 1357c478bd9Sstevel@tonic-gate if (mdb_layered_walk("lwp_deathrow", wsp) == -1) { 1367c478bd9Sstevel@tonic-gate mdb_warn("couldn't walk 'lwp_deathrow'"); 1377c478bd9Sstevel@tonic-gate return (WALK_ERR); 1387c478bd9Sstevel@tonic-gate } 1397c478bd9Sstevel@tonic-gate 1407c478bd9Sstevel@tonic-gate return (WALK_NEXT); 1417c478bd9Sstevel@tonic-gate } 1427c478bd9Sstevel@tonic-gate 1437c478bd9Sstevel@tonic-gate int 1447c478bd9Sstevel@tonic-gate deathrow_walk_step(mdb_walk_state_t *wsp) 1457c478bd9Sstevel@tonic-gate { 1467c478bd9Sstevel@tonic-gate kthread_t t; 1477c478bd9Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 1487c478bd9Sstevel@tonic-gate 1497c478bd9Sstevel@tonic-gate if (addr == NULL) 1507c478bd9Sstevel@tonic-gate return (WALK_DONE); 1517c478bd9Sstevel@tonic-gate 1527c478bd9Sstevel@tonic-gate if (mdb_vread(&t, sizeof (t), addr) == -1) { 1537c478bd9Sstevel@tonic-gate mdb_warn("couldn't read deathrow thread at %p", addr); 1547c478bd9Sstevel@tonic-gate return (WALK_ERR); 1557c478bd9Sstevel@tonic-gate } 1567c478bd9Sstevel@tonic-gate 1577c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)t.t_forw; 1587c478bd9Sstevel@tonic-gate 1597c478bd9Sstevel@tonic-gate return (wsp->walk_callback(addr, &t, wsp->walk_cbdata)); 1607c478bd9Sstevel@tonic-gate } 1617c478bd9Sstevel@tonic-gate 1627c478bd9Sstevel@tonic-gate int 1637c478bd9Sstevel@tonic-gate thread_deathrow_walk_init(mdb_walk_state_t *wsp) 1647c478bd9Sstevel@tonic-gate { 1657c478bd9Sstevel@tonic-gate if (mdb_readvar(&wsp->walk_addr, "thread_deathrow") == -1) { 1667c478bd9Sstevel@tonic-gate mdb_warn("couldn't read symbol 'thread_deathrow'"); 1677c478bd9Sstevel@tonic-gate return (WALK_ERR); 1687c478bd9Sstevel@tonic-gate } 1697c478bd9Sstevel@tonic-gate 1707c478bd9Sstevel@tonic-gate return (WALK_NEXT); 1717c478bd9Sstevel@tonic-gate } 1727c478bd9Sstevel@tonic-gate 1737c478bd9Sstevel@tonic-gate int 1747c478bd9Sstevel@tonic-gate lwp_deathrow_walk_init(mdb_walk_state_t *wsp) 1757c478bd9Sstevel@tonic-gate { 1767c478bd9Sstevel@tonic-gate if (mdb_readvar(&wsp->walk_addr, "lwp_deathrow") == -1) { 1777c478bd9Sstevel@tonic-gate mdb_warn("couldn't read symbol 'lwp_deathrow'"); 1787c478bd9Sstevel@tonic-gate return (WALK_ERR); 1797c478bd9Sstevel@tonic-gate } 1807c478bd9Sstevel@tonic-gate 1817c478bd9Sstevel@tonic-gate return (WALK_NEXT); 1827c478bd9Sstevel@tonic-gate } 1837c478bd9Sstevel@tonic-gate 1847c478bd9Sstevel@tonic-gate 1857c478bd9Sstevel@tonic-gate typedef struct dispq_walk { 1867c478bd9Sstevel@tonic-gate int dw_npri; 1877c478bd9Sstevel@tonic-gate uintptr_t dw_dispq; 1887c478bd9Sstevel@tonic-gate uintptr_t dw_last; 1897c478bd9Sstevel@tonic-gate } dispq_walk_t; 1907c478bd9Sstevel@tonic-gate 1917c478bd9Sstevel@tonic-gate int 1927c478bd9Sstevel@tonic-gate cpu_dispq_walk_init(mdb_walk_state_t *wsp) 1937c478bd9Sstevel@tonic-gate { 1947c478bd9Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 1957c478bd9Sstevel@tonic-gate dispq_walk_t *dw; 1967c478bd9Sstevel@tonic-gate cpu_t cpu; 1977c478bd9Sstevel@tonic-gate dispq_t dispq; 1987c478bd9Sstevel@tonic-gate disp_t disp; 1997c478bd9Sstevel@tonic-gate 2007c478bd9Sstevel@tonic-gate if (addr == NULL) { 2017c478bd9Sstevel@tonic-gate mdb_warn("cpu_dispq walk needs a cpu_t address\n"); 2027c478bd9Sstevel@tonic-gate return (WALK_ERR); 2037c478bd9Sstevel@tonic-gate } 2047c478bd9Sstevel@tonic-gate 2057c478bd9Sstevel@tonic-gate if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) { 2067c478bd9Sstevel@tonic-gate mdb_warn("failed to read cpu_t at %p", addr); 2077c478bd9Sstevel@tonic-gate return (WALK_ERR); 2087c478bd9Sstevel@tonic-gate } 2097c478bd9Sstevel@tonic-gate 2107c478bd9Sstevel@tonic-gate if (mdb_vread(&disp, sizeof (disp_t), (uintptr_t)cpu.cpu_disp) == -1) { 2117c478bd9Sstevel@tonic-gate mdb_warn("failed to read disp_t at %p", cpu.cpu_disp); 2127c478bd9Sstevel@tonic-gate return (WALK_ERR); 2137c478bd9Sstevel@tonic-gate } 2147c478bd9Sstevel@tonic-gate 2157c478bd9Sstevel@tonic-gate if (mdb_vread(&dispq, sizeof (dispq_t), 2167c478bd9Sstevel@tonic-gate (uintptr_t)disp.disp_q) == -1) { 2177c478bd9Sstevel@tonic-gate mdb_warn("failed to read dispq_t at %p", disp.disp_q); 2187c478bd9Sstevel@tonic-gate return (WALK_ERR); 2197c478bd9Sstevel@tonic-gate } 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP); 2227c478bd9Sstevel@tonic-gate 2237c478bd9Sstevel@tonic-gate dw->dw_npri = disp.disp_npri; 2247c478bd9Sstevel@tonic-gate dw->dw_dispq = (uintptr_t)disp.disp_q; 2257c478bd9Sstevel@tonic-gate dw->dw_last = (uintptr_t)dispq.dq_last; 2267c478bd9Sstevel@tonic-gate 2277c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)dispq.dq_first; 2287c478bd9Sstevel@tonic-gate wsp->walk_data = dw; 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate return (WALK_NEXT); 2317c478bd9Sstevel@tonic-gate } 2327c478bd9Sstevel@tonic-gate 2337c478bd9Sstevel@tonic-gate int 2347c478bd9Sstevel@tonic-gate cpupart_dispq_walk_init(mdb_walk_state_t *wsp) 2357c478bd9Sstevel@tonic-gate { 2367c478bd9Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 2377c478bd9Sstevel@tonic-gate dispq_walk_t *dw; 2387c478bd9Sstevel@tonic-gate cpupart_t cpupart; 2397c478bd9Sstevel@tonic-gate dispq_t dispq; 2407c478bd9Sstevel@tonic-gate 2417c478bd9Sstevel@tonic-gate if (addr == NULL) { 2427c478bd9Sstevel@tonic-gate mdb_warn("cpupart_dispq walk needs a cpupart_t address\n"); 2437c478bd9Sstevel@tonic-gate return (WALK_ERR); 2447c478bd9Sstevel@tonic-gate } 2457c478bd9Sstevel@tonic-gate 2467c478bd9Sstevel@tonic-gate if (mdb_vread(&cpupart, sizeof (cpupart_t), addr) == -1) { 2477c478bd9Sstevel@tonic-gate mdb_warn("failed to read cpupart_t at %p", addr); 2487c478bd9Sstevel@tonic-gate return (WALK_ERR); 2497c478bd9Sstevel@tonic-gate } 2507c478bd9Sstevel@tonic-gate 2517c478bd9Sstevel@tonic-gate if (mdb_vread(&dispq, sizeof (dispq_t), 2527c478bd9Sstevel@tonic-gate (uintptr_t)cpupart.cp_kp_queue.disp_q) == -1) { 2537c478bd9Sstevel@tonic-gate mdb_warn("failed to read dispq_t at %p", 2547c478bd9Sstevel@tonic-gate cpupart.cp_kp_queue.disp_q); 2557c478bd9Sstevel@tonic-gate return (WALK_ERR); 2567c478bd9Sstevel@tonic-gate } 2577c478bd9Sstevel@tonic-gate 2587c478bd9Sstevel@tonic-gate dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP); 2597c478bd9Sstevel@tonic-gate 2607c478bd9Sstevel@tonic-gate dw->dw_npri = cpupart.cp_kp_queue.disp_npri; 2617c478bd9Sstevel@tonic-gate dw->dw_dispq = (uintptr_t)cpupart.cp_kp_queue.disp_q; 2627c478bd9Sstevel@tonic-gate dw->dw_last = (uintptr_t)dispq.dq_last; 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)dispq.dq_first; 2657c478bd9Sstevel@tonic-gate wsp->walk_data = dw; 2667c478bd9Sstevel@tonic-gate 2677c478bd9Sstevel@tonic-gate return (WALK_NEXT); 2687c478bd9Sstevel@tonic-gate } 2697c478bd9Sstevel@tonic-gate 2707c478bd9Sstevel@tonic-gate int 2717c478bd9Sstevel@tonic-gate dispq_walk_step(mdb_walk_state_t *wsp) 2727c478bd9Sstevel@tonic-gate { 2737c478bd9Sstevel@tonic-gate uintptr_t addr = wsp->walk_addr; 2747c478bd9Sstevel@tonic-gate dispq_walk_t *dw = wsp->walk_data; 2757c478bd9Sstevel@tonic-gate dispq_t dispq; 2767c478bd9Sstevel@tonic-gate kthread_t t; 2777c478bd9Sstevel@tonic-gate 2787c478bd9Sstevel@tonic-gate while (addr == NULL) { 2797c478bd9Sstevel@tonic-gate if (--dw->dw_npri == 0) 2807c478bd9Sstevel@tonic-gate return (WALK_DONE); 2817c478bd9Sstevel@tonic-gate 2827c478bd9Sstevel@tonic-gate dw->dw_dispq += sizeof (dispq_t); 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate if (mdb_vread(&dispq, sizeof (dispq_t), dw->dw_dispq) == -1) { 2857c478bd9Sstevel@tonic-gate mdb_warn("failed to read dispq_t at %p", dw->dw_dispq); 2867c478bd9Sstevel@tonic-gate return (WALK_ERR); 2877c478bd9Sstevel@tonic-gate } 2887c478bd9Sstevel@tonic-gate 2897c478bd9Sstevel@tonic-gate dw->dw_last = (uintptr_t)dispq.dq_last; 2907c478bd9Sstevel@tonic-gate addr = (uintptr_t)dispq.dq_first; 2917c478bd9Sstevel@tonic-gate } 2927c478bd9Sstevel@tonic-gate 2937c478bd9Sstevel@tonic-gate if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 2947c478bd9Sstevel@tonic-gate mdb_warn("failed to read kthread_t at %p", addr); 2957c478bd9Sstevel@tonic-gate return (WALK_ERR); 2967c478bd9Sstevel@tonic-gate } 2977c478bd9Sstevel@tonic-gate 2987c478bd9Sstevel@tonic-gate if (addr == dw->dw_last) 2997c478bd9Sstevel@tonic-gate wsp->walk_addr = NULL; 3007c478bd9Sstevel@tonic-gate else 3017c478bd9Sstevel@tonic-gate wsp->walk_addr = (uintptr_t)t.t_link; 3027c478bd9Sstevel@tonic-gate 3037c478bd9Sstevel@tonic-gate return (wsp->walk_callback(addr, &t, wsp->walk_cbdata)); 3047c478bd9Sstevel@tonic-gate } 3057c478bd9Sstevel@tonic-gate 3067c478bd9Sstevel@tonic-gate void 3077c478bd9Sstevel@tonic-gate dispq_walk_fini(mdb_walk_state_t *wsp) 3087c478bd9Sstevel@tonic-gate { 3097c478bd9Sstevel@tonic-gate mdb_free(wsp->walk_data, sizeof (dispq_walk_t)); 3107c478bd9Sstevel@tonic-gate } 3117c478bd9Sstevel@tonic-gate 312346799e8SJonathan W Adams struct thread_state { 313346799e8SJonathan W Adams uint_t ts_state; 314346799e8SJonathan W Adams const char *ts_name; 315346799e8SJonathan W Adams } thread_states[] = { 316346799e8SJonathan W Adams { TS_FREE, "free" }, 317346799e8SJonathan W Adams { TS_SLEEP, "sleep" }, 318346799e8SJonathan W Adams { TS_RUN, "run" }, 319346799e8SJonathan W Adams { TS_ONPROC, "onproc" }, 320346799e8SJonathan W Adams { TS_ZOMB, "zomb" }, 321346799e8SJonathan W Adams { TS_STOPPED, "stopped" }, 322346799e8SJonathan W Adams { TS_WAIT, "wait" } 323346799e8SJonathan W Adams }; 324346799e8SJonathan W Adams #define NUM_THREAD_STATES (sizeof (thread_states) / sizeof (*thread_states)) 325346799e8SJonathan W Adams 326346799e8SJonathan W Adams void 327346799e8SJonathan W Adams thread_state_to_text(uint_t state, char *out, size_t out_sz) 328346799e8SJonathan W Adams { 329346799e8SJonathan W Adams int idx; 330346799e8SJonathan W Adams 331346799e8SJonathan W Adams for (idx = 0; idx < NUM_THREAD_STATES; idx++) { 332346799e8SJonathan W Adams struct thread_state *tsp = &thread_states[idx]; 333346799e8SJonathan W Adams if (tsp->ts_state == state) { 334346799e8SJonathan W Adams mdb_snprintf(out, out_sz, "%s", tsp->ts_name); 335346799e8SJonathan W Adams return; 336346799e8SJonathan W Adams } 337346799e8SJonathan W Adams } 338346799e8SJonathan W Adams mdb_snprintf(out, out_sz, "inval/%02x", state); 339346799e8SJonathan W Adams } 340346799e8SJonathan W Adams 341346799e8SJonathan W Adams int 342346799e8SJonathan W Adams thread_text_to_state(const char *state, uint_t *out) 343346799e8SJonathan W Adams { 344346799e8SJonathan W Adams int idx; 345346799e8SJonathan W Adams 346346799e8SJonathan W Adams for (idx = 0; idx < NUM_THREAD_STATES; idx++) { 347346799e8SJonathan W Adams struct thread_state *tsp = &thread_states[idx]; 348346799e8SJonathan W Adams if (strcasecmp(tsp->ts_name, state) == 0) { 349346799e8SJonathan W Adams *out = tsp->ts_state; 350346799e8SJonathan W Adams return (0); 351346799e8SJonathan W Adams } 352346799e8SJonathan W Adams } 353346799e8SJonathan W Adams return (-1); 354346799e8SJonathan W Adams } 355346799e8SJonathan W Adams 356346799e8SJonathan W Adams void 357346799e8SJonathan W Adams thread_walk_states(void (*cbfunc)(uint_t, const char *, void *), void *cbarg) 358346799e8SJonathan W Adams { 359346799e8SJonathan W Adams int idx; 360346799e8SJonathan W Adams 361346799e8SJonathan W Adams for (idx = 0; idx < NUM_THREAD_STATES; idx++) { 362346799e8SJonathan W Adams struct thread_state *tsp = &thread_states[idx]; 363346799e8SJonathan W Adams cbfunc(tsp->ts_state, tsp->ts_name, cbarg); 364346799e8SJonathan W Adams } 365346799e8SJonathan W Adams } 3667c478bd9Sstevel@tonic-gate 3677c478bd9Sstevel@tonic-gate #define TF_INTR 0x01 3687c478bd9Sstevel@tonic-gate #define TF_PROC 0x02 3697c478bd9Sstevel@tonic-gate #define TF_BLOCK 0x04 3707c478bd9Sstevel@tonic-gate #define TF_SIG 0x08 3717c478bd9Sstevel@tonic-gate #define TF_DISP 0x10 3727c478bd9Sstevel@tonic-gate #define TF_MERGE 0x20 3737c478bd9Sstevel@tonic-gate 3747c478bd9Sstevel@tonic-gate /* 3757c478bd9Sstevel@tonic-gate * Display a kthread_t. 3767c478bd9Sstevel@tonic-gate * This is a little complicated, as there is a lot of information that 3777c478bd9Sstevel@tonic-gate * the user could be interested in. The flags "ipbsd" are used to 3787c478bd9Sstevel@tonic-gate * indicate which subset of the thread's members are to be displayed 3797c478bd9Sstevel@tonic-gate * ('i' is the default). If multiple options are specified, multiple 3807c478bd9Sstevel@tonic-gate * sets of data will be displayed in a vaguely readable format. If the 3817c478bd9Sstevel@tonic-gate * 'm' option is specified, all the selected sets will be merged onto a 3827c478bd9Sstevel@tonic-gate * single line for the benefit of those using wider-than-normal 3837c478bd9Sstevel@tonic-gate * terminals. Having a generic mechanism for doing this would be 3847c478bd9Sstevel@tonic-gate * really useful, but is a project best left to another day. 3857c478bd9Sstevel@tonic-gate */ 3867c478bd9Sstevel@tonic-gate 3877c478bd9Sstevel@tonic-gate int 3887c478bd9Sstevel@tonic-gate thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 3897c478bd9Sstevel@tonic-gate { 3907c478bd9Sstevel@tonic-gate kthread_t t; 3917c478bd9Sstevel@tonic-gate uint_t oflags = 0; 3927c478bd9Sstevel@tonic-gate uint_t fflag = FALSE; 3937c478bd9Sstevel@tonic-gate int first; 3947c478bd9Sstevel@tonic-gate char stbuf[20]; 3957c478bd9Sstevel@tonic-gate 3967c478bd9Sstevel@tonic-gate /* 3977c478bd9Sstevel@tonic-gate * "Gracefully" handle printing a boatload of stuff to the 3987c478bd9Sstevel@tonic-gate * screen. If we are not printing our first set of data, and 3997c478bd9Sstevel@tonic-gate * we haven't been instructed to merge sets together, output a 4007c478bd9Sstevel@tonic-gate * newline and indent such that the thread addresses form a 4017c478bd9Sstevel@tonic-gate * column of their own. 4027c478bd9Sstevel@tonic-gate */ 4037c478bd9Sstevel@tonic-gate #define SPACER() \ 4047c478bd9Sstevel@tonic-gate if (first) { \ 4057c478bd9Sstevel@tonic-gate first = FALSE; \ 4067c478bd9Sstevel@tonic-gate } else if (!(oflags & TF_MERGE)) { \ 4077c478bd9Sstevel@tonic-gate mdb_printf("\n%?s", ""); \ 4087c478bd9Sstevel@tonic-gate } 4097c478bd9Sstevel@tonic-gate 4107c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 4117c478bd9Sstevel@tonic-gate if (mdb_walk_dcmd("thread", "thread", argc, argv) == -1) { 4127c478bd9Sstevel@tonic-gate mdb_warn("can't walk threads"); 4137c478bd9Sstevel@tonic-gate return (DCMD_ERR); 4147c478bd9Sstevel@tonic-gate } 4157c478bd9Sstevel@tonic-gate return (DCMD_OK); 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate 4187c478bd9Sstevel@tonic-gate if (mdb_getopts(argc, argv, 4197c478bd9Sstevel@tonic-gate 'f', MDB_OPT_SETBITS, TRUE, &fflag, 4207c478bd9Sstevel@tonic-gate 'i', MDB_OPT_SETBITS, TF_INTR, &oflags, 4217c478bd9Sstevel@tonic-gate 'p', MDB_OPT_SETBITS, TF_PROC, &oflags, 4227c478bd9Sstevel@tonic-gate 'b', MDB_OPT_SETBITS, TF_BLOCK, &oflags, 4237c478bd9Sstevel@tonic-gate 's', MDB_OPT_SETBITS, TF_SIG, &oflags, 4247c478bd9Sstevel@tonic-gate 'd', MDB_OPT_SETBITS, TF_DISP, &oflags, 4257c478bd9Sstevel@tonic-gate 'm', MDB_OPT_SETBITS, TF_MERGE, &oflags, NULL) != argc) 4267c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 4277c478bd9Sstevel@tonic-gate 4287c478bd9Sstevel@tonic-gate /* 4297c478bd9Sstevel@tonic-gate * If no sets were specified, choose the 'i' set. 4307c478bd9Sstevel@tonic-gate */ 4317c478bd9Sstevel@tonic-gate if (!(oflags & ~TF_MERGE)) 4327c478bd9Sstevel@tonic-gate #ifdef _LP64 4337c478bd9Sstevel@tonic-gate oflags = TF_INTR; 4347c478bd9Sstevel@tonic-gate #else 4357c478bd9Sstevel@tonic-gate oflags = TF_INTR | TF_DISP | TF_MERGE; 4367c478bd9Sstevel@tonic-gate #endif 4377c478bd9Sstevel@tonic-gate 4387c478bd9Sstevel@tonic-gate /* 4397c478bd9Sstevel@tonic-gate * Print the relevant headers; note use of SPACER(). 4407c478bd9Sstevel@tonic-gate */ 4417c478bd9Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) { 4427c478bd9Sstevel@tonic-gate first = TRUE; 4437c478bd9Sstevel@tonic-gate mdb_printf("%<u>%?s%</u>", "ADDR"); 4447c478bd9Sstevel@tonic-gate mdb_flush(); 4457c478bd9Sstevel@tonic-gate 4467c478bd9Sstevel@tonic-gate if (oflags & TF_PROC) { 4477c478bd9Sstevel@tonic-gate SPACER(); 4487c478bd9Sstevel@tonic-gate mdb_printf("%<u> %?s %?s %?s%</u>", 4497c478bd9Sstevel@tonic-gate "PROC", "LWP", "CRED"); 4507c478bd9Sstevel@tonic-gate } 4517c478bd9Sstevel@tonic-gate 4527c478bd9Sstevel@tonic-gate if (oflags & TF_INTR) { 4537c478bd9Sstevel@tonic-gate SPACER(); 4547c478bd9Sstevel@tonic-gate mdb_printf("%<u> %8s %4s %4s %4s %5s %5s %3s %?s%</u>", 4557c478bd9Sstevel@tonic-gate "STATE", "FLG", "PFLG", 4567c478bd9Sstevel@tonic-gate "SFLG", "PRI", "EPRI", "PIL", "INTR"); 4577c478bd9Sstevel@tonic-gate } 4587c478bd9Sstevel@tonic-gate 4597c478bd9Sstevel@tonic-gate if (oflags & TF_BLOCK) { 4607c478bd9Sstevel@tonic-gate SPACER(); 4617c478bd9Sstevel@tonic-gate mdb_printf("%<u> %?s %?s %?s %11s%</u>", 4627c478bd9Sstevel@tonic-gate "WCHAN", "TS", "PITS", "SOBJ OPS"); 4637c478bd9Sstevel@tonic-gate } 4647c478bd9Sstevel@tonic-gate 4657c478bd9Sstevel@tonic-gate if (oflags & TF_SIG) { 4667c478bd9Sstevel@tonic-gate SPACER(); 4677c478bd9Sstevel@tonic-gate mdb_printf("%<u> %?s %16s %16s%</u>", 4687c478bd9Sstevel@tonic-gate "SIGQUEUE", "SIG PEND", "SIG HELD"); 4697c478bd9Sstevel@tonic-gate } 4707c478bd9Sstevel@tonic-gate 4717c478bd9Sstevel@tonic-gate if (oflags & TF_DISP) { 4727c478bd9Sstevel@tonic-gate SPACER(); 473*9583f717SMarcel Telka mdb_printf("%<u> %?s %5s %2s %-6s%</u>", 474*9583f717SMarcel Telka "DISPTIME", "BOUND", "PR", "SWITCH"); 4757c478bd9Sstevel@tonic-gate } 4767c478bd9Sstevel@tonic-gate mdb_printf("\n"); 4777c478bd9Sstevel@tonic-gate } 4787c478bd9Sstevel@tonic-gate 4797c478bd9Sstevel@tonic-gate if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 4807c478bd9Sstevel@tonic-gate mdb_warn("can't read kthread_t at %#lx", addr); 4817c478bd9Sstevel@tonic-gate return (DCMD_ERR); 4827c478bd9Sstevel@tonic-gate } 4837c478bd9Sstevel@tonic-gate 4847c478bd9Sstevel@tonic-gate if (fflag && (t.t_state == TS_FREE)) 4857c478bd9Sstevel@tonic-gate return (DCMD_OK); 4867c478bd9Sstevel@tonic-gate 4877c478bd9Sstevel@tonic-gate first = TRUE; 4887c478bd9Sstevel@tonic-gate mdb_printf("%0?lx", addr); 4897c478bd9Sstevel@tonic-gate 4907c478bd9Sstevel@tonic-gate /* process information */ 4917c478bd9Sstevel@tonic-gate if (oflags & TF_PROC) { 4927c478bd9Sstevel@tonic-gate SPACER(); 493ab4a9bebSjohnlev mdb_printf(" %?p %?p %?p", t.t_procp, t.t_lwp, t.t_cred); 4947c478bd9Sstevel@tonic-gate } 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate /* priority/interrupt information */ 4977c478bd9Sstevel@tonic-gate if (oflags & TF_INTR) { 4987c478bd9Sstevel@tonic-gate SPACER(); 499346799e8SJonathan W Adams thread_state_to_text(t.t_state, stbuf, sizeof (stbuf)); 5007c478bd9Sstevel@tonic-gate if (t.t_intr == NULL) { 5017c478bd9Sstevel@tonic-gate mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?s", 502346799e8SJonathan W Adams stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag, 5037c478bd9Sstevel@tonic-gate t.t_pri, t.t_epri, t.t_pil, "n/a"); 5047c478bd9Sstevel@tonic-gate } else { 5057c478bd9Sstevel@tonic-gate mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?p", 506346799e8SJonathan W Adams stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag, 5077c478bd9Sstevel@tonic-gate t.t_pri, t.t_epri, t.t_pil, t.t_intr); 5087c478bd9Sstevel@tonic-gate } 5097c478bd9Sstevel@tonic-gate } 5107c478bd9Sstevel@tonic-gate 5117c478bd9Sstevel@tonic-gate /* blocking information */ 5127c478bd9Sstevel@tonic-gate if (oflags & TF_BLOCK) { 5137c478bd9Sstevel@tonic-gate SPACER(); 5147c478bd9Sstevel@tonic-gate (void) mdb_snprintf(stbuf, 20, "%a", t.t_sobj_ops); 5157c478bd9Sstevel@tonic-gate stbuf[11] = '\0'; 5167c478bd9Sstevel@tonic-gate mdb_printf(" %?p %?p %?p %11s", 5177c478bd9Sstevel@tonic-gate t.t_wchan, t.t_ts, t.t_prioinv, stbuf); 5187c478bd9Sstevel@tonic-gate } 5197c478bd9Sstevel@tonic-gate 5207c478bd9Sstevel@tonic-gate /* signal information */ 5217c478bd9Sstevel@tonic-gate if (oflags & TF_SIG) { 5227c478bd9Sstevel@tonic-gate SPACER(); 5237c478bd9Sstevel@tonic-gate mdb_printf(" %?p %016llx %016llx", 5247c478bd9Sstevel@tonic-gate t.t_sigqueue, t.t_sig, t.t_hold); 5257c478bd9Sstevel@tonic-gate } 5267c478bd9Sstevel@tonic-gate 5277c478bd9Sstevel@tonic-gate /* dispatcher stuff */ 5287c478bd9Sstevel@tonic-gate if (oflags & TF_DISP) { 5297c478bd9Sstevel@tonic-gate SPACER(); 5307c478bd9Sstevel@tonic-gate mdb_printf(" %?lx %5d %2d ", 5317c478bd9Sstevel@tonic-gate t.t_disp_time, t.t_bind_cpu, t.t_preempt); 532*9583f717SMarcel Telka if (t.t_disp_time != 0) 533*9583f717SMarcel Telka mdb_printf("t-%-4d", 534*9583f717SMarcel Telka (clock_t)mdb_get_lbolt() - t.t_disp_time); 535*9583f717SMarcel Telka else 536*9583f717SMarcel Telka mdb_printf("%-6s", "-"); 5377c478bd9Sstevel@tonic-gate } 5387c478bd9Sstevel@tonic-gate 5397c478bd9Sstevel@tonic-gate mdb_printf("\n"); 5407c478bd9Sstevel@tonic-gate 5417c478bd9Sstevel@tonic-gate #undef SPACER 5427c478bd9Sstevel@tonic-gate 5437c478bd9Sstevel@tonic-gate return (DCMD_OK); 5447c478bd9Sstevel@tonic-gate } 5457c478bd9Sstevel@tonic-gate 5467c478bd9Sstevel@tonic-gate void 5477c478bd9Sstevel@tonic-gate thread_help(void) 5487c478bd9Sstevel@tonic-gate { 5497c478bd9Sstevel@tonic-gate mdb_printf( 5507c478bd9Sstevel@tonic-gate "The flags -ipbsd control which information is displayed. When\n" 5517c478bd9Sstevel@tonic-gate "combined, the fields are displayed on separate lines unless the\n" 5527c478bd9Sstevel@tonic-gate "-m option is given.\n" 5537c478bd9Sstevel@tonic-gate "\n" 5547c478bd9Sstevel@tonic-gate "\t-b\tprint blocked thread state\n" 5557c478bd9Sstevel@tonic-gate "\t-d\tprint dispatcher state\n" 5567c478bd9Sstevel@tonic-gate "\t-f\tignore freed threads\n" 5577c478bd9Sstevel@tonic-gate "\t-i\tprint basic thread state (default)\n" 5587c478bd9Sstevel@tonic-gate "\t-m\tdisplay results on a single line\n" 5597c478bd9Sstevel@tonic-gate "\t-p\tprint process and lwp state\n" 5607c478bd9Sstevel@tonic-gate "\t-s\tprint signal state\n"); 5617c478bd9Sstevel@tonic-gate } 5627c478bd9Sstevel@tonic-gate 5637c478bd9Sstevel@tonic-gate /* 5647c478bd9Sstevel@tonic-gate * List a combination of kthread_t and proc_t. Add stack traces in verbose mode. 5657c478bd9Sstevel@tonic-gate */ 5667c478bd9Sstevel@tonic-gate int 5677c478bd9Sstevel@tonic-gate threadlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 5687c478bd9Sstevel@tonic-gate { 5697c478bd9Sstevel@tonic-gate int i; 5707c478bd9Sstevel@tonic-gate uint_t count = 0; 5717c478bd9Sstevel@tonic-gate uint_t verbose = FALSE; 572ab4a9bebSjohnlev uint_t notaskq = FALSE; 5737c478bd9Sstevel@tonic-gate kthread_t t; 574ab4a9bebSjohnlev taskq_t tq; 5757c478bd9Sstevel@tonic-gate proc_t p; 5767c478bd9Sstevel@tonic-gate char cmd[80]; 5777c478bd9Sstevel@tonic-gate mdb_arg_t cmdarg; 5787c478bd9Sstevel@tonic-gate 5797c478bd9Sstevel@tonic-gate if (!(flags & DCMD_ADDRSPEC)) { 5807c478bd9Sstevel@tonic-gate if (mdb_walk_dcmd("thread", "threadlist", argc, argv) == -1) { 5817c478bd9Sstevel@tonic-gate mdb_warn("can't walk threads"); 5827c478bd9Sstevel@tonic-gate return (DCMD_ERR); 5837c478bd9Sstevel@tonic-gate } 5847c478bd9Sstevel@tonic-gate return (DCMD_OK); 5857c478bd9Sstevel@tonic-gate } 5867c478bd9Sstevel@tonic-gate 5877c478bd9Sstevel@tonic-gate i = mdb_getopts(argc, argv, 588ab4a9bebSjohnlev 't', MDB_OPT_SETBITS, TRUE, ¬askq, 5897c478bd9Sstevel@tonic-gate 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL); 5907c478bd9Sstevel@tonic-gate 5917c478bd9Sstevel@tonic-gate if (i != argc) { 5927c478bd9Sstevel@tonic-gate if (i != argc - 1 || !verbose) 5937c478bd9Sstevel@tonic-gate return (DCMD_USAGE); 5947c478bd9Sstevel@tonic-gate 5957c478bd9Sstevel@tonic-gate if (argv[i].a_type == MDB_TYPE_IMMEDIATE) 5967c478bd9Sstevel@tonic-gate count = (uint_t)argv[i].a_un.a_val; 5977c478bd9Sstevel@tonic-gate else 5987c478bd9Sstevel@tonic-gate count = (uint_t)mdb_strtoull(argv[i].a_un.a_str); 5997c478bd9Sstevel@tonic-gate } 6007c478bd9Sstevel@tonic-gate 6017c478bd9Sstevel@tonic-gate if (DCMD_HDRSPEC(flags)) { 6027c478bd9Sstevel@tonic-gate if (verbose) 6037c478bd9Sstevel@tonic-gate mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n", 6047c478bd9Sstevel@tonic-gate "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN"); 6057c478bd9Sstevel@tonic-gate else 6067c478bd9Sstevel@tonic-gate mdb_printf("%<u>%?s %?s %?s %s/%s%</u>\n", 6077c478bd9Sstevel@tonic-gate "ADDR", "PROC", "LWP", "CMD", "LWPID"); 6087c478bd9Sstevel@tonic-gate } 6097c478bd9Sstevel@tonic-gate 6107c478bd9Sstevel@tonic-gate if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 6117c478bd9Sstevel@tonic-gate mdb_warn("failed to read kthread_t at %p", addr); 6127c478bd9Sstevel@tonic-gate return (DCMD_ERR); 6137c478bd9Sstevel@tonic-gate } 6147c478bd9Sstevel@tonic-gate 615ab4a9bebSjohnlev if (notaskq && t.t_taskq != NULL) 616ab4a9bebSjohnlev return (DCMD_OK); 617ab4a9bebSjohnlev 6187c478bd9Sstevel@tonic-gate if (t.t_state == TS_FREE) 6197c478bd9Sstevel@tonic-gate return (DCMD_OK); 6207c478bd9Sstevel@tonic-gate 6217c478bd9Sstevel@tonic-gate if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) { 6227c478bd9Sstevel@tonic-gate mdb_warn("failed to read proc at %p", t.t_procp); 6237c478bd9Sstevel@tonic-gate return (DCMD_ERR); 6247c478bd9Sstevel@tonic-gate } 6257c478bd9Sstevel@tonic-gate 626ab4a9bebSjohnlev if (mdb_vread(&tq, sizeof (taskq_t), (uintptr_t)t.t_taskq) == -1) 627ab4a9bebSjohnlev tq.tq_name[0] = '\0'; 628ab4a9bebSjohnlev 6297c478bd9Sstevel@tonic-gate if (verbose) { 6307c478bd9Sstevel@tonic-gate mdb_printf("%0?p %?p %?p %3u %3d %?p\n", 6317c478bd9Sstevel@tonic-gate addr, t.t_procp, t.t_lwp, t.t_cid, t.t_pri, t.t_wchan); 6327c478bd9Sstevel@tonic-gate 6337c478bd9Sstevel@tonic-gate mdb_inc_indent(2); 6347c478bd9Sstevel@tonic-gate 6357c478bd9Sstevel@tonic-gate mdb_printf("PC: %a", t.t_pc); 636ab4a9bebSjohnlev if (t.t_tid == 0) { 637ab4a9bebSjohnlev if (tq.tq_name[0] != '\0') 638ab4a9bebSjohnlev mdb_printf(" TASKQ: %s\n", tq.tq_name); 6397c478bd9Sstevel@tonic-gate else 640ab4a9bebSjohnlev mdb_printf(" THREAD: %a()\n", t.t_startpc); 641ab4a9bebSjohnlev } else { 6427c478bd9Sstevel@tonic-gate mdb_printf(" CMD: %s\n", p.p_user.u_psargs); 643ab4a9bebSjohnlev } 6447c478bd9Sstevel@tonic-gate 6457c478bd9Sstevel@tonic-gate mdb_snprintf(cmd, sizeof (cmd), "<.$c%d", count); 6467c478bd9Sstevel@tonic-gate cmdarg.a_type = MDB_TYPE_STRING; 6477c478bd9Sstevel@tonic-gate cmdarg.a_un.a_str = cmd; 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate (void) mdb_call_dcmd("findstack", addr, flags, 1, &cmdarg); 6507c478bd9Sstevel@tonic-gate 6517c478bd9Sstevel@tonic-gate mdb_dec_indent(2); 6527c478bd9Sstevel@tonic-gate 6537c478bd9Sstevel@tonic-gate mdb_printf("\n"); 6547c478bd9Sstevel@tonic-gate } else { 6557c478bd9Sstevel@tonic-gate mdb_printf("%0?p %?p %?p", addr, t.t_procp, t.t_lwp); 656ab4a9bebSjohnlev if (t.t_tid == 0) { 657ab4a9bebSjohnlev if (tq.tq_name[0] != '\0') 658ab4a9bebSjohnlev mdb_printf(" tq:%s\n", tq.tq_name); 6597c478bd9Sstevel@tonic-gate else 660ab4a9bebSjohnlev mdb_printf(" %a()\n", t.t_startpc); 661ab4a9bebSjohnlev } else { 6627c478bd9Sstevel@tonic-gate mdb_printf(" %s/%u\n", p.p_user.u_comm, t.t_tid); 6637c478bd9Sstevel@tonic-gate } 664ab4a9bebSjohnlev } 6657c478bd9Sstevel@tonic-gate 6667c478bd9Sstevel@tonic-gate return (DCMD_OK); 6677c478bd9Sstevel@tonic-gate } 6687c478bd9Sstevel@tonic-gate 6697c478bd9Sstevel@tonic-gate void 6707c478bd9Sstevel@tonic-gate threadlist_help(void) 6717c478bd9Sstevel@tonic-gate { 6727c478bd9Sstevel@tonic-gate mdb_printf( 6737c478bd9Sstevel@tonic-gate " -v print verbose output including C stack trace\n" 674ab4a9bebSjohnlev " -t skip threads belonging to a taskq\n" 6757c478bd9Sstevel@tonic-gate " count print no more than count arguments (default 0)\n"); 6767c478bd9Sstevel@tonic-gate } 677bff31d89SPhilippe Jung 678bff31d89SPhilippe Jung static size_t 679bff31d89SPhilippe Jung stk_compute_percent(caddr_t t_stk, caddr_t t_stkbase, caddr_t sp) 680bff31d89SPhilippe Jung { 681bff31d89SPhilippe Jung size_t percent; 682bff31d89SPhilippe Jung size_t s; 683bff31d89SPhilippe Jung 684bff31d89SPhilippe Jung if (t_stk > t_stkbase) { 685bff31d89SPhilippe Jung /* stack grows down */ 686bff31d89SPhilippe Jung if (sp > t_stk) { 687bff31d89SPhilippe Jung return (0); 688bff31d89SPhilippe Jung } 689bff31d89SPhilippe Jung if (sp < t_stkbase) { 690bff31d89SPhilippe Jung return (100); 691bff31d89SPhilippe Jung } 692bff31d89SPhilippe Jung percent = t_stk - sp + 1; 693bff31d89SPhilippe Jung s = t_stk - t_stkbase + 1; 694bff31d89SPhilippe Jung } else { 695bff31d89SPhilippe Jung /* stack grows up */ 696bff31d89SPhilippe Jung if (sp < t_stk) { 697bff31d89SPhilippe Jung return (0); 698bff31d89SPhilippe Jung } 699bff31d89SPhilippe Jung if (sp > t_stkbase) { 700bff31d89SPhilippe Jung return (100); 701bff31d89SPhilippe Jung } 702bff31d89SPhilippe Jung percent = sp - t_stk + 1; 703bff31d89SPhilippe Jung s = t_stkbase - t_stk + 1; 704bff31d89SPhilippe Jung } 705bff31d89SPhilippe Jung percent = ((100 * percent) / s) + 1; 706bff31d89SPhilippe Jung if (percent > 100) { 707bff31d89SPhilippe Jung percent = 100; 708bff31d89SPhilippe Jung } 709bff31d89SPhilippe Jung return (percent); 710bff31d89SPhilippe Jung } 711bff31d89SPhilippe Jung 712bff31d89SPhilippe Jung /* 713bff31d89SPhilippe Jung * Display kthread stack infos. 714bff31d89SPhilippe Jung */ 715bff31d89SPhilippe Jung int 716bff31d89SPhilippe Jung stackinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 717bff31d89SPhilippe Jung { 718bff31d89SPhilippe Jung kthread_t t; 719bff31d89SPhilippe Jung proc_t p; 720bff31d89SPhilippe Jung uint64_t *ptr; /* pattern pointer */ 721bff31d89SPhilippe Jung caddr_t start; /* kernel stack start */ 722bff31d89SPhilippe Jung caddr_t end; /* kernel stack end */ 723bff31d89SPhilippe Jung caddr_t ustack; /* userland copy of kernel stack */ 724bff31d89SPhilippe Jung size_t usize; /* userland copy of kernel stack size */ 725bff31d89SPhilippe Jung caddr_t ustart; /* userland copy of kernel stack, aligned start */ 726bff31d89SPhilippe Jung caddr_t uend; /* userland copy of kernel stack, aligned end */ 727bff31d89SPhilippe Jung size_t percent = 0; 728bff31d89SPhilippe Jung uint_t all = FALSE; /* don't show TS_FREE kthread by default */ 729bff31d89SPhilippe Jung uint_t history = FALSE; 730bff31d89SPhilippe Jung int i = 0; 731bff31d89SPhilippe Jung unsigned int ukmem_stackinfo; 732bff31d89SPhilippe Jung uintptr_t allthreads; 733bff31d89SPhilippe Jung 734bff31d89SPhilippe Jung /* handle options */ 735bff31d89SPhilippe Jung if (mdb_getopts(argc, argv, 736bff31d89SPhilippe Jung 'a', MDB_OPT_SETBITS, TRUE, &all, 737bff31d89SPhilippe Jung 'h', MDB_OPT_SETBITS, TRUE, &history, NULL) != argc) { 738bff31d89SPhilippe Jung return (DCMD_USAGE); 739bff31d89SPhilippe Jung } 740bff31d89SPhilippe Jung 741bff31d89SPhilippe Jung /* walk all kthread if needed */ 742bff31d89SPhilippe Jung if ((history == FALSE) && !(flags & DCMD_ADDRSPEC)) { 743bff31d89SPhilippe Jung if (mdb_walk_dcmd("thread", "stackinfo", argc, argv) == -1) { 744bff31d89SPhilippe Jung mdb_warn("can't walk threads"); 745bff31d89SPhilippe Jung return (DCMD_ERR); 746bff31d89SPhilippe Jung } 747bff31d89SPhilippe Jung return (DCMD_OK); 748bff31d89SPhilippe Jung } 749bff31d89SPhilippe Jung 750bff31d89SPhilippe Jung /* read 'kmem_stackinfo' */ 751bff31d89SPhilippe Jung if (mdb_readsym(&ukmem_stackinfo, sizeof (ukmem_stackinfo), 752bff31d89SPhilippe Jung "kmem_stackinfo") == -1) { 753bff31d89SPhilippe Jung mdb_warn("failed to read 'kmem_stackinfo'\n"); 754bff31d89SPhilippe Jung ukmem_stackinfo = 0; 755bff31d89SPhilippe Jung } 756bff31d89SPhilippe Jung 757bff31d89SPhilippe Jung /* read 'allthreads' */ 758bff31d89SPhilippe Jung if (mdb_readsym(&allthreads, sizeof (kthread_t *), 759bff31d89SPhilippe Jung "allthreads") == -1) { 760bff31d89SPhilippe Jung mdb_warn("failed to read 'allthreads'\n"); 761bff31d89SPhilippe Jung allthreads = NULL; 762bff31d89SPhilippe Jung } 763bff31d89SPhilippe Jung 764bff31d89SPhilippe Jung if (history == TRUE) { 765bff31d89SPhilippe Jung kmem_stkinfo_t *log; 766bff31d89SPhilippe Jung uintptr_t kaddr; 767bff31d89SPhilippe Jung 768bff31d89SPhilippe Jung mdb_printf("Dead kthreads stack usage history:\n"); 769bff31d89SPhilippe Jung if (ukmem_stackinfo == 0) { 770bff31d89SPhilippe Jung mdb_printf("Tunable kmem_stackinfo is unset, history "); 771bff31d89SPhilippe Jung mdb_printf("feature is off.\nUse ::help stackinfo "); 772bff31d89SPhilippe Jung mdb_printf("for more details.\n"); 773bff31d89SPhilippe Jung return (DCMD_OK); 774bff31d89SPhilippe Jung } 775bff31d89SPhilippe Jung 776bff31d89SPhilippe Jung mdb_printf("%<u>%?s%</u>", "THREAD"); 777bff31d89SPhilippe Jung mdb_printf(" %<u>%?s%</u>", "STACK"); 778bff31d89SPhilippe Jung mdb_printf("%<u>%s%</u>", " SIZE MAX CMD/LWPID or STARTPC"); 779bff31d89SPhilippe Jung mdb_printf("\n"); 780bff31d89SPhilippe Jung usize = KMEM_STKINFO_LOG_SIZE * sizeof (kmem_stkinfo_t); 781bff31d89SPhilippe Jung log = (kmem_stkinfo_t *)mdb_alloc(usize, UM_SLEEP); 782bff31d89SPhilippe Jung if (mdb_readsym(&kaddr, sizeof (kaddr), 783bff31d89SPhilippe Jung "kmem_stkinfo_log") == -1) { 784bff31d89SPhilippe Jung mdb_free((void *)log, usize); 785bff31d89SPhilippe Jung mdb_warn("failed to read 'kmem_stkinfo_log'\n"); 786bff31d89SPhilippe Jung return (DCMD_ERR); 787bff31d89SPhilippe Jung } 788bff31d89SPhilippe Jung if (kaddr == NULL) { 789bff31d89SPhilippe Jung mdb_free((void *)log, usize); 790bff31d89SPhilippe Jung return (DCMD_OK); 791bff31d89SPhilippe Jung } 792bff31d89SPhilippe Jung if (mdb_vread(log, usize, kaddr) == -1) { 793bff31d89SPhilippe Jung mdb_free((void *)log, usize); 794bff31d89SPhilippe Jung mdb_warn("failed to read %p\n", kaddr); 795bff31d89SPhilippe Jung return (DCMD_ERR); 796bff31d89SPhilippe Jung } 797bff31d89SPhilippe Jung for (i = 0; i < KMEM_STKINFO_LOG_SIZE; i++) { 798bff31d89SPhilippe Jung if (log[i].kthread == NULL) { 799bff31d89SPhilippe Jung continue; 800bff31d89SPhilippe Jung } 801bff31d89SPhilippe Jung mdb_printf("%0?p %0?p %6x %3d%%", 802bff31d89SPhilippe Jung log[i].kthread, 803bff31d89SPhilippe Jung log[i].start, 804bff31d89SPhilippe Jung (uint_t)log[i].stksz, 805bff31d89SPhilippe Jung (int)log[i].percent); 806bff31d89SPhilippe Jung if (log[i].t_tid != 0) { 807bff31d89SPhilippe Jung mdb_printf(" %s/%u\n", 808bff31d89SPhilippe Jung log[i].cmd, log[i].t_tid); 809bff31d89SPhilippe Jung } else { 810bff31d89SPhilippe Jung mdb_printf(" %p (%a)\n", log[i].t_startpc, 811bff31d89SPhilippe Jung log[i].t_startpc); 812bff31d89SPhilippe Jung } 813bff31d89SPhilippe Jung } 814bff31d89SPhilippe Jung mdb_free((void *)log, usize); 815bff31d89SPhilippe Jung return (DCMD_OK); 816bff31d89SPhilippe Jung } 817bff31d89SPhilippe Jung 818bff31d89SPhilippe Jung /* display header */ 819bff31d89SPhilippe Jung if (DCMD_HDRSPEC(flags)) { 820bff31d89SPhilippe Jung if (ukmem_stackinfo == 0) { 821bff31d89SPhilippe Jung mdb_printf("Tunable kmem_stackinfo is unset, "); 822bff31d89SPhilippe Jung mdb_printf("MAX value is not available.\n"); 823bff31d89SPhilippe Jung mdb_printf("Use ::help stackinfo for more details.\n"); 824bff31d89SPhilippe Jung } 825bff31d89SPhilippe Jung mdb_printf("%<u>%?s%</u>", "THREAD"); 826bff31d89SPhilippe Jung mdb_printf(" %<u>%?s%</u>", "STACK"); 827bff31d89SPhilippe Jung mdb_printf("%<u>%s%</u>", " SIZE CUR MAX CMD/LWPID"); 828bff31d89SPhilippe Jung mdb_printf("\n"); 829bff31d89SPhilippe Jung } 830bff31d89SPhilippe Jung 831bff31d89SPhilippe Jung /* read kthread */ 832bff31d89SPhilippe Jung if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) { 833bff31d89SPhilippe Jung mdb_warn("can't read kthread_t at %#lx\n", addr); 834bff31d89SPhilippe Jung return (DCMD_ERR); 835bff31d89SPhilippe Jung } 836bff31d89SPhilippe Jung 837bff31d89SPhilippe Jung if (t.t_state == TS_FREE && all == FALSE) { 838bff31d89SPhilippe Jung return (DCMD_OK); 839bff31d89SPhilippe Jung } 840bff31d89SPhilippe Jung 841bff31d89SPhilippe Jung /* read proc */ 842bff31d89SPhilippe Jung if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) { 843bff31d89SPhilippe Jung mdb_warn("failed to read proc at %p\n", t.t_procp); 844bff31d89SPhilippe Jung return (DCMD_ERR); 845bff31d89SPhilippe Jung } 846bff31d89SPhilippe Jung 847bff31d89SPhilippe Jung /* 848bff31d89SPhilippe Jung * Stack grows up or down, see thread_create(), 849bff31d89SPhilippe Jung * compute stack memory aera start and end (start < end). 850bff31d89SPhilippe Jung */ 851bff31d89SPhilippe Jung if (t.t_stk > t.t_stkbase) { 852bff31d89SPhilippe Jung /* stack grows down */ 853bff31d89SPhilippe Jung start = t.t_stkbase; 854bff31d89SPhilippe Jung end = t.t_stk; 855bff31d89SPhilippe Jung } else { 856bff31d89SPhilippe Jung /* stack grows up */ 857bff31d89SPhilippe Jung start = t.t_stk; 858bff31d89SPhilippe Jung end = t.t_stkbase; 859bff31d89SPhilippe Jung } 860bff31d89SPhilippe Jung 861bff31d89SPhilippe Jung /* display stack info */ 862bff31d89SPhilippe Jung mdb_printf("%0?p %0?p", addr, start); 863bff31d89SPhilippe Jung 864bff31d89SPhilippe Jung /* (end - start), kernel stack size as found in kthread_t */ 865bff31d89SPhilippe Jung if ((end <= start) || ((end - start) > (1024 * 1024))) { 866bff31d89SPhilippe Jung /* negative or stack size > 1 meg, assume bogus */ 867bff31d89SPhilippe Jung mdb_warn(" t_stk/t_stkbase problem\n"); 868bff31d89SPhilippe Jung return (DCMD_ERR); 869bff31d89SPhilippe Jung } 870bff31d89SPhilippe Jung 871bff31d89SPhilippe Jung /* display stack size */ 872bff31d89SPhilippe Jung mdb_printf(" %6x", end - start); 873bff31d89SPhilippe Jung 874bff31d89SPhilippe Jung /* display current stack usage */ 875bff31d89SPhilippe Jung percent = stk_compute_percent(t.t_stk, t.t_stkbase, 876bff31d89SPhilippe Jung (caddr_t)t.t_sp + STACK_BIAS); 877bff31d89SPhilippe Jung 878bff31d89SPhilippe Jung mdb_printf(" %3d%%", percent); 879bff31d89SPhilippe Jung percent = 0; 880bff31d89SPhilippe Jung 881bff31d89SPhilippe Jung if (ukmem_stackinfo == 0) { 882bff31d89SPhilippe Jung mdb_printf(" n/a"); 883bff31d89SPhilippe Jung if (t.t_tid == 0) { 884bff31d89SPhilippe Jung mdb_printf(" %a()", t.t_startpc); 885bff31d89SPhilippe Jung } else { 886bff31d89SPhilippe Jung mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid); 887bff31d89SPhilippe Jung } 888bff31d89SPhilippe Jung mdb_printf("\n"); 889bff31d89SPhilippe Jung return (DCMD_OK); 890bff31d89SPhilippe Jung } 891bff31d89SPhilippe Jung 892bff31d89SPhilippe Jung if ((((uintptr_t)start) & 0x7) != 0) { 893bff31d89SPhilippe Jung start = (caddr_t)((((uintptr_t)start) & (~0x7)) + 8); 894bff31d89SPhilippe Jung } 895bff31d89SPhilippe Jung end = (caddr_t)(((uintptr_t)end) & (~0x7)); 896bff31d89SPhilippe Jung /* size to scan in userland copy of kernel stack */ 897bff31d89SPhilippe Jung usize = end - start; /* is a multiple of 8 bytes */ 898bff31d89SPhilippe Jung 899bff31d89SPhilippe Jung /* 900bff31d89SPhilippe Jung * Stackinfo pattern size is 8 bytes. Ensure proper 8 bytes 901bff31d89SPhilippe Jung * alignement for ustart and uend, in boundaries. 902bff31d89SPhilippe Jung */ 903bff31d89SPhilippe Jung ustart = ustack = (caddr_t)mdb_alloc(usize + 8, UM_SLEEP); 904bff31d89SPhilippe Jung if ((((uintptr_t)ustart) & 0x7) != 0) { 905bff31d89SPhilippe Jung ustart = (caddr_t)((((uintptr_t)ustart) & (~0x7)) + 8); 906bff31d89SPhilippe Jung } 907bff31d89SPhilippe Jung uend = ustart + usize; 908bff31d89SPhilippe Jung 909bff31d89SPhilippe Jung /* read the kernel stack */ 910bff31d89SPhilippe Jung if (mdb_vread(ustart, usize, (uintptr_t)start) != usize) { 911bff31d89SPhilippe Jung mdb_free((void *)ustack, usize + 8); 912bff31d89SPhilippe Jung mdb_printf("\n"); 913bff31d89SPhilippe Jung mdb_warn("couldn't read entire stack\n"); 914bff31d89SPhilippe Jung return (DCMD_ERR); 915bff31d89SPhilippe Jung } 916bff31d89SPhilippe Jung 917bff31d89SPhilippe Jung /* scan the stack */ 918bff31d89SPhilippe Jung if (t.t_stk > t.t_stkbase) { 919bff31d89SPhilippe Jung /* stack grows down */ 920bff31d89SPhilippe Jung #if defined(__i386) || defined(__amd64) 921bff31d89SPhilippe Jung /* 922bff31d89SPhilippe Jung * 6 longs are pushed on stack, see thread_load(). Skip 923bff31d89SPhilippe Jung * them, so if kthread has never run, percent is zero. 924bff31d89SPhilippe Jung * 8 bytes alignement is preserved for a 32 bit kernel, 925bff31d89SPhilippe Jung * 6 x 4 = 24, 24 is a multiple of 8. 926bff31d89SPhilippe Jung */ 927bff31d89SPhilippe Jung uend -= (6 * sizeof (long)); 928bff31d89SPhilippe Jung #endif 929bff31d89SPhilippe Jung ptr = (uint64_t *)((void *)ustart); 930bff31d89SPhilippe Jung while (ptr < (uint64_t *)((void *)uend)) { 931bff31d89SPhilippe Jung if (*ptr != KMEM_STKINFO_PATTERN) { 932bff31d89SPhilippe Jung percent = stk_compute_percent(uend, 933bff31d89SPhilippe Jung ustart, (caddr_t)ptr); 934bff31d89SPhilippe Jung break; 935bff31d89SPhilippe Jung } 936bff31d89SPhilippe Jung ptr++; 937bff31d89SPhilippe Jung } 938bff31d89SPhilippe Jung } else { 939bff31d89SPhilippe Jung /* stack grows up */ 940bff31d89SPhilippe Jung ptr = (uint64_t *)((void *)uend); 941bff31d89SPhilippe Jung ptr--; 942bff31d89SPhilippe Jung while (ptr >= (uint64_t *)((void *)ustart)) { 943bff31d89SPhilippe Jung if (*ptr != KMEM_STKINFO_PATTERN) { 944bff31d89SPhilippe Jung percent = stk_compute_percent(ustart, 945bff31d89SPhilippe Jung uend, (caddr_t)ptr); 946bff31d89SPhilippe Jung break; 947bff31d89SPhilippe Jung } 948bff31d89SPhilippe Jung ptr--; 949bff31d89SPhilippe Jung } 950bff31d89SPhilippe Jung } 951bff31d89SPhilippe Jung 952bff31d89SPhilippe Jung /* thread 't0' stack is not created by thread_create() */ 953bff31d89SPhilippe Jung if (addr == allthreads) { 954bff31d89SPhilippe Jung percent = 0; 955bff31d89SPhilippe Jung } 956bff31d89SPhilippe Jung if (percent != 0) { 957bff31d89SPhilippe Jung mdb_printf(" %3d%%", percent); 958bff31d89SPhilippe Jung } else { 959bff31d89SPhilippe Jung mdb_printf(" n/a"); 960bff31d89SPhilippe Jung } 961bff31d89SPhilippe Jung if (t.t_tid == 0) { 962bff31d89SPhilippe Jung mdb_printf(" %a()", t.t_startpc); 963bff31d89SPhilippe Jung } else { 964bff31d89SPhilippe Jung mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid); 965bff31d89SPhilippe Jung } 966bff31d89SPhilippe Jung mdb_printf("\n"); 967bff31d89SPhilippe Jung mdb_free((void *)ustack, usize + 8); 968bff31d89SPhilippe Jung return (DCMD_OK); 969bff31d89SPhilippe Jung } 970bff31d89SPhilippe Jung 971bff31d89SPhilippe Jung void 972bff31d89SPhilippe Jung stackinfo_help(void) 973bff31d89SPhilippe Jung { 974bff31d89SPhilippe Jung mdb_printf( 975bff31d89SPhilippe Jung "Shows kernel stacks real utilization, if /etc/system " 976bff31d89SPhilippe Jung "kmem_stackinfo tunable\n"); 977bff31d89SPhilippe Jung mdb_printf( 978bff31d89SPhilippe Jung "(an unsigned integer) is non zero at kthread creation time. "); 979bff31d89SPhilippe Jung mdb_printf("For example:\n"); 980bff31d89SPhilippe Jung mdb_printf( 981bff31d89SPhilippe Jung " THREAD STACK SIZE CUR MAX CMD/LWPID\n"); 982bff31d89SPhilippe Jung mdb_printf( 983bff31d89SPhilippe Jung "ffffff014f5f2c20 ffffff0004153000 4f00 4%% 43%% init/1\n"); 984bff31d89SPhilippe Jung mdb_printf( 985bff31d89SPhilippe Jung "The stack size utilization for this kthread is at 4%%" 986bff31d89SPhilippe Jung " of its maximum size,\n"); 987bff31d89SPhilippe Jung mdb_printf( 988bff31d89SPhilippe Jung "but has already used up to 43%%, stack size is 4f00 bytes.\n"); 989bff31d89SPhilippe Jung mdb_printf( 990bff31d89SPhilippe Jung "MAX value can be shown as n/a (not available):\n"); 991bff31d89SPhilippe Jung mdb_printf( 992bff31d89SPhilippe Jung " - for the very first kthread (sched/1)\n"); 993bff31d89SPhilippe Jung mdb_printf( 994bff31d89SPhilippe Jung " - kmem_stackinfo was zero at kthread creation time\n"); 995bff31d89SPhilippe Jung mdb_printf( 996bff31d89SPhilippe Jung " - kthread has not yet run\n"); 997bff31d89SPhilippe Jung mdb_printf("\n"); 998bff31d89SPhilippe Jung mdb_printf("Options:\n"); 999bff31d89SPhilippe Jung mdb_printf( 1000bff31d89SPhilippe Jung "-a shows also TS_FREE kthreads (interrupt kthreads)\n"); 1001bff31d89SPhilippe Jung mdb_printf( 1002bff31d89SPhilippe Jung "-h shows history, dead kthreads that used their " 1003bff31d89SPhilippe Jung "kernel stack the most\n"); 1004bff31d89SPhilippe Jung mdb_printf( 1005bff31d89SPhilippe Jung "\nSee Solaris Modular Debugger Guide for detailed usage.\n"); 1006bff31d89SPhilippe Jung mdb_flush(); 1007bff31d89SPhilippe Jung } 1008