1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26
27 #include <mdb/mdb_modapi.h>
28 #include <sys/types.h>
29 #include <sys/thread.h>
30 #include <sys/lwp.h>
31 #include <sys/proc.h>
32 #include <sys/cpuvar.h>
33 #include <sys/cpupart.h>
34 #include <sys/disp.h>
35 #include <sys/taskq_impl.h>
36 #include <sys/stack.h>
37
38 #ifndef STACK_BIAS
39 #define STACK_BIAS 0
40 #endif
41
42 typedef struct thread_walk {
43 kthread_t *tw_thread;
44 uintptr_t tw_last;
45 uint_t tw_inproc;
46 uint_t tw_step;
47 } thread_walk_t;
48
49 int
thread_walk_init(mdb_walk_state_t * wsp)50 thread_walk_init(mdb_walk_state_t *wsp)
51 {
52 thread_walk_t *twp = mdb_alloc(sizeof (thread_walk_t), UM_SLEEP);
53
54 if (wsp->walk_addr == NULL) {
55 if (mdb_readvar(&wsp->walk_addr, "allthreads") == -1) {
56 mdb_warn("failed to read 'allthreads'");
57 mdb_free(twp, sizeof (thread_walk_t));
58 return (WALK_ERR);
59 }
60
61 twp->tw_inproc = FALSE;
62
63 } else {
64 proc_t pr;
65
66 if (mdb_vread(&pr, sizeof (proc_t), wsp->walk_addr) == -1) {
67 mdb_warn("failed to read proc at %p", wsp->walk_addr);
68 mdb_free(twp, sizeof (thread_walk_t));
69 return (WALK_ERR);
70 }
71
72 wsp->walk_addr = (uintptr_t)pr.p_tlist;
73 twp->tw_inproc = TRUE;
74 }
75
76 twp->tw_thread = mdb_alloc(sizeof (kthread_t), UM_SLEEP);
77 twp->tw_last = wsp->walk_addr;
78 twp->tw_step = FALSE;
79
80 wsp->walk_data = twp;
81 return (WALK_NEXT);
82 }
83
84 int
thread_walk_step(mdb_walk_state_t * wsp)85 thread_walk_step(mdb_walk_state_t *wsp)
86 {
87 thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
88 int status;
89
90 if (wsp->walk_addr == NULL)
91 return (WALK_DONE); /* Proc has 0 threads or allthreads = 0 */
92
93 if (twp->tw_step && wsp->walk_addr == twp->tw_last)
94 return (WALK_DONE); /* We've wrapped around */
95
96 if (mdb_vread(twp->tw_thread, sizeof (kthread_t),
97 wsp->walk_addr) == -1) {
98 mdb_warn("failed to read thread at %p", wsp->walk_addr);
99 return (WALK_DONE);
100 }
101
102 status = wsp->walk_callback(wsp->walk_addr, twp->tw_thread,
103 wsp->walk_cbdata);
104
105 if (twp->tw_inproc)
106 wsp->walk_addr = (uintptr_t)twp->tw_thread->t_forw;
107 else
108 wsp->walk_addr = (uintptr_t)twp->tw_thread->t_next;
109
110 twp->tw_step = TRUE;
111 return (status);
112 }
113
114 void
thread_walk_fini(mdb_walk_state_t * wsp)115 thread_walk_fini(mdb_walk_state_t *wsp)
116 {
117 thread_walk_t *twp = (thread_walk_t *)wsp->walk_data;
118
119 mdb_free(twp->tw_thread, sizeof (kthread_t));
120 mdb_free(twp, sizeof (thread_walk_t));
121 }
122
123 int
deathrow_walk_init(mdb_walk_state_t * wsp)124 deathrow_walk_init(mdb_walk_state_t *wsp)
125 {
126 if (mdb_layered_walk("thread_deathrow", wsp) == -1) {
127 mdb_warn("couldn't walk 'thread_deathrow'");
128 return (WALK_ERR);
129 }
130
131 if (mdb_layered_walk("lwp_deathrow", wsp) == -1) {
132 mdb_warn("couldn't walk 'lwp_deathrow'");
133 return (WALK_ERR);
134 }
135
136 return (WALK_NEXT);
137 }
138
139 int
deathrow_walk_step(mdb_walk_state_t * wsp)140 deathrow_walk_step(mdb_walk_state_t *wsp)
141 {
142 kthread_t t;
143 uintptr_t addr = wsp->walk_addr;
144
145 if (addr == NULL)
146 return (WALK_DONE);
147
148 if (mdb_vread(&t, sizeof (t), addr) == -1) {
149 mdb_warn("couldn't read deathrow thread at %p", addr);
150 return (WALK_ERR);
151 }
152
153 wsp->walk_addr = (uintptr_t)t.t_forw;
154
155 return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
156 }
157
158 int
thread_deathrow_walk_init(mdb_walk_state_t * wsp)159 thread_deathrow_walk_init(mdb_walk_state_t *wsp)
160 {
161 if (mdb_readvar(&wsp->walk_addr, "thread_deathrow") == -1) {
162 mdb_warn("couldn't read symbol 'thread_deathrow'");
163 return (WALK_ERR);
164 }
165
166 return (WALK_NEXT);
167 }
168
169 int
lwp_deathrow_walk_init(mdb_walk_state_t * wsp)170 lwp_deathrow_walk_init(mdb_walk_state_t *wsp)
171 {
172 if (mdb_readvar(&wsp->walk_addr, "lwp_deathrow") == -1) {
173 mdb_warn("couldn't read symbol 'lwp_deathrow'");
174 return (WALK_ERR);
175 }
176
177 return (WALK_NEXT);
178 }
179
180
181 typedef struct dispq_walk {
182 int dw_npri;
183 uintptr_t dw_dispq;
184 uintptr_t dw_last;
185 } dispq_walk_t;
186
187 int
cpu_dispq_walk_init(mdb_walk_state_t * wsp)188 cpu_dispq_walk_init(mdb_walk_state_t *wsp)
189 {
190 uintptr_t addr = wsp->walk_addr;
191 dispq_walk_t *dw;
192 cpu_t cpu;
193 dispq_t dispq;
194 disp_t disp;
195
196 if (addr == NULL) {
197 mdb_warn("cpu_dispq walk needs a cpu_t address\n");
198 return (WALK_ERR);
199 }
200
201 if (mdb_vread(&cpu, sizeof (cpu_t), addr) == -1) {
202 mdb_warn("failed to read cpu_t at %p", addr);
203 return (WALK_ERR);
204 }
205
206 if (mdb_vread(&disp, sizeof (disp_t), (uintptr_t)cpu.cpu_disp) == -1) {
207 mdb_warn("failed to read disp_t at %p", cpu.cpu_disp);
208 return (WALK_ERR);
209 }
210
211 if (mdb_vread(&dispq, sizeof (dispq_t),
212 (uintptr_t)disp.disp_q) == -1) {
213 mdb_warn("failed to read dispq_t at %p", disp.disp_q);
214 return (WALK_ERR);
215 }
216
217 dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
218
219 dw->dw_npri = disp.disp_npri;
220 dw->dw_dispq = (uintptr_t)disp.disp_q;
221 dw->dw_last = (uintptr_t)dispq.dq_last;
222
223 wsp->walk_addr = (uintptr_t)dispq.dq_first;
224 wsp->walk_data = dw;
225
226 return (WALK_NEXT);
227 }
228
229 int
cpupart_dispq_walk_init(mdb_walk_state_t * wsp)230 cpupart_dispq_walk_init(mdb_walk_state_t *wsp)
231 {
232 uintptr_t addr = wsp->walk_addr;
233 dispq_walk_t *dw;
234 cpupart_t cpupart;
235 dispq_t dispq;
236
237 if (addr == NULL) {
238 mdb_warn("cpupart_dispq walk needs a cpupart_t address\n");
239 return (WALK_ERR);
240 }
241
242 if (mdb_vread(&cpupart, sizeof (cpupart_t), addr) == -1) {
243 mdb_warn("failed to read cpupart_t at %p", addr);
244 return (WALK_ERR);
245 }
246
247 if (mdb_vread(&dispq, sizeof (dispq_t),
248 (uintptr_t)cpupart.cp_kp_queue.disp_q) == -1) {
249 mdb_warn("failed to read dispq_t at %p",
250 cpupart.cp_kp_queue.disp_q);
251 return (WALK_ERR);
252 }
253
254 dw = mdb_alloc(sizeof (dispq_walk_t), UM_SLEEP);
255
256 dw->dw_npri = cpupart.cp_kp_queue.disp_npri;
257 dw->dw_dispq = (uintptr_t)cpupart.cp_kp_queue.disp_q;
258 dw->dw_last = (uintptr_t)dispq.dq_last;
259
260 wsp->walk_addr = (uintptr_t)dispq.dq_first;
261 wsp->walk_data = dw;
262
263 return (WALK_NEXT);
264 }
265
266 int
dispq_walk_step(mdb_walk_state_t * wsp)267 dispq_walk_step(mdb_walk_state_t *wsp)
268 {
269 uintptr_t addr = wsp->walk_addr;
270 dispq_walk_t *dw = wsp->walk_data;
271 dispq_t dispq;
272 kthread_t t;
273
274 while (addr == NULL) {
275 if (--dw->dw_npri == 0)
276 return (WALK_DONE);
277
278 dw->dw_dispq += sizeof (dispq_t);
279
280 if (mdb_vread(&dispq, sizeof (dispq_t), dw->dw_dispq) == -1) {
281 mdb_warn("failed to read dispq_t at %p", dw->dw_dispq);
282 return (WALK_ERR);
283 }
284
285 dw->dw_last = (uintptr_t)dispq.dq_last;
286 addr = (uintptr_t)dispq.dq_first;
287 }
288
289 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
290 mdb_warn("failed to read kthread_t at %p", addr);
291 return (WALK_ERR);
292 }
293
294 if (addr == dw->dw_last)
295 wsp->walk_addr = NULL;
296 else
297 wsp->walk_addr = (uintptr_t)t.t_link;
298
299 return (wsp->walk_callback(addr, &t, wsp->walk_cbdata));
300 }
301
302 void
dispq_walk_fini(mdb_walk_state_t * wsp)303 dispq_walk_fini(mdb_walk_state_t *wsp)
304 {
305 mdb_free(wsp->walk_data, sizeof (dispq_walk_t));
306 }
307
308 struct thread_state {
309 uint_t ts_state;
310 const char *ts_name;
311 } thread_states[] = {
312 { TS_FREE, "free" },
313 { TS_SLEEP, "sleep" },
314 { TS_RUN, "run" },
315 { TS_ONPROC, "onproc" },
316 { TS_ZOMB, "zomb" },
317 { TS_STOPPED, "stopped" },
318 { TS_WAIT, "wait" }
319 };
320 #define NUM_THREAD_STATES (sizeof (thread_states) / sizeof (*thread_states))
321
322 void
thread_state_to_text(uint_t state,char * out,size_t out_sz)323 thread_state_to_text(uint_t state, char *out, size_t out_sz)
324 {
325 int idx;
326
327 for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
328 struct thread_state *tsp = &thread_states[idx];
329 if (tsp->ts_state == state) {
330 mdb_snprintf(out, out_sz, "%s", tsp->ts_name);
331 return;
332 }
333 }
334 mdb_snprintf(out, out_sz, "inval/%02x", state);
335 }
336
337 int
thread_text_to_state(const char * state,uint_t * out)338 thread_text_to_state(const char *state, uint_t *out)
339 {
340 int idx;
341
342 for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
343 struct thread_state *tsp = &thread_states[idx];
344 if (strcasecmp(tsp->ts_name, state) == 0) {
345 *out = tsp->ts_state;
346 return (0);
347 }
348 }
349 return (-1);
350 }
351
352 void
thread_walk_states(void (* cbfunc)(uint_t,const char *,void *),void * cbarg)353 thread_walk_states(void (*cbfunc)(uint_t, const char *, void *), void *cbarg)
354 {
355 int idx;
356
357 for (idx = 0; idx < NUM_THREAD_STATES; idx++) {
358 struct thread_state *tsp = &thread_states[idx];
359 cbfunc(tsp->ts_state, tsp->ts_name, cbarg);
360 }
361 }
362
363 #define TF_INTR 0x01
364 #define TF_PROC 0x02
365 #define TF_BLOCK 0x04
366 #define TF_SIG 0x08
367 #define TF_DISP 0x10
368 #define TF_MERGE 0x20
369
370 /*
371 * Display a kthread_t.
372 * This is a little complicated, as there is a lot of information that
373 * the user could be interested in. The flags "ipbsd" are used to
374 * indicate which subset of the thread's members are to be displayed
375 * ('i' is the default). If multiple options are specified, multiple
376 * sets of data will be displayed in a vaguely readable format. If the
377 * 'm' option is specified, all the selected sets will be merged onto a
378 * single line for the benefit of those using wider-than-normal
379 * terminals. Having a generic mechanism for doing this would be
380 * really useful, but is a project best left to another day.
381 */
382
383 int
thread(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)384 thread(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
385 {
386 kthread_t t;
387 uint_t oflags = 0;
388 uint_t fflag = FALSE;
389 int first;
390 char stbuf[20];
391
392 /*
393 * "Gracefully" handle printing a boatload of stuff to the
394 * screen. If we are not printing our first set of data, and
395 * we haven't been instructed to merge sets together, output a
396 * newline and indent such that the thread addresses form a
397 * column of their own.
398 */
399 #define SPACER() \
400 if (first) { \
401 first = FALSE; \
402 } else if (!(oflags & TF_MERGE)) { \
403 mdb_printf("\n%?s", ""); \
404 }
405
406 if (!(flags & DCMD_ADDRSPEC)) {
407 if (mdb_walk_dcmd("thread", "thread", argc, argv) == -1) {
408 mdb_warn("can't walk threads");
409 return (DCMD_ERR);
410 }
411 return (DCMD_OK);
412 }
413
414 if (mdb_getopts(argc, argv,
415 'f', MDB_OPT_SETBITS, TRUE, &fflag,
416 'i', MDB_OPT_SETBITS, TF_INTR, &oflags,
417 'p', MDB_OPT_SETBITS, TF_PROC, &oflags,
418 'b', MDB_OPT_SETBITS, TF_BLOCK, &oflags,
419 's', MDB_OPT_SETBITS, TF_SIG, &oflags,
420 'd', MDB_OPT_SETBITS, TF_DISP, &oflags,
421 'm', MDB_OPT_SETBITS, TF_MERGE, &oflags, NULL) != argc)
422 return (DCMD_USAGE);
423
424 /*
425 * If no sets were specified, choose the 'i' set.
426 */
427 if (!(oflags & ~TF_MERGE))
428 #ifdef _LP64
429 oflags = TF_INTR;
430 #else
431 oflags = TF_INTR | TF_DISP | TF_MERGE;
432 #endif
433
434 /*
435 * Print the relevant headers; note use of SPACER().
436 */
437 if (DCMD_HDRSPEC(flags)) {
438 first = TRUE;
439 mdb_printf("%<u>%?s%</u>", "ADDR");
440 mdb_flush();
441
442 if (oflags & TF_PROC) {
443 SPACER();
444 mdb_printf("%<u> %?s %?s %?s%</u>",
445 "PROC", "LWP", "CRED");
446 }
447
448 if (oflags & TF_INTR) {
449 SPACER();
450 mdb_printf("%<u> %8s %4s %4s %4s %5s %5s %3s %?s%</u>",
451 "STATE", "FLG", "PFLG",
452 "SFLG", "PRI", "EPRI", "PIL", "INTR");
453 }
454
455 if (oflags & TF_BLOCK) {
456 SPACER();
457 mdb_printf("%<u> %?s %?s %?s %11s%</u>",
458 "WCHAN", "TS", "PITS", "SOBJ OPS");
459 }
460
461 if (oflags & TF_SIG) {
462 SPACER();
463 mdb_printf("%<u> %?s %16s %16s%</u>",
464 "SIGQUEUE", "SIG PEND", "SIG HELD");
465 }
466
467 if (oflags & TF_DISP) {
468 SPACER();
469 mdb_printf("%<u> %?s %5s %2s%</u>",
470 "DISPTIME", "BOUND", "PR");
471 }
472 mdb_printf("\n");
473 }
474
475 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
476 mdb_warn("can't read kthread_t at %#lx", addr);
477 return (DCMD_ERR);
478 }
479
480 if (fflag && (t.t_state == TS_FREE))
481 return (DCMD_OK);
482
483 first = TRUE;
484 mdb_printf("%0?lx", addr);
485
486 /* process information */
487 if (oflags & TF_PROC) {
488 SPACER();
489 mdb_printf(" %?p %?p %?p", t.t_procp, t.t_lwp, t.t_cred);
490 }
491
492 /* priority/interrupt information */
493 if (oflags & TF_INTR) {
494 SPACER();
495 thread_state_to_text(t.t_state, stbuf, sizeof (stbuf));
496 if (t.t_intr == NULL) {
497 mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?s",
498 stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag,
499 t.t_pri, t.t_epri, t.t_pil, "n/a");
500 } else {
501 mdb_printf(" %-8s %4x %4x %4x %5d %5d %3d %?p",
502 stbuf, t.t_flag, t.t_proc_flag, t.t_schedflag,
503 t.t_pri, t.t_epri, t.t_pil, t.t_intr);
504 }
505 }
506
507 /* blocking information */
508 if (oflags & TF_BLOCK) {
509 SPACER();
510 (void) mdb_snprintf(stbuf, 20, "%a", t.t_sobj_ops);
511 stbuf[11] = '\0';
512 mdb_printf(" %?p %?p %?p %11s",
513 t.t_wchan, t.t_ts, t.t_prioinv, stbuf);
514 }
515
516 /* signal information */
517 if (oflags & TF_SIG) {
518 SPACER();
519 mdb_printf(" %?p %016llx %016llx",
520 t.t_sigqueue, t.t_sig, t.t_hold);
521 }
522
523 /* dispatcher stuff */
524 if (oflags & TF_DISP) {
525 SPACER();
526 mdb_printf(" %?lx %5d %2d",
527 t.t_disp_time, t.t_bind_cpu, t.t_preempt);
528 }
529
530 mdb_printf("\n");
531
532 #undef SPACER
533
534 return (DCMD_OK);
535 }
536
537 void
thread_help(void)538 thread_help(void)
539 {
540 mdb_printf(
541 "The flags -ipbsd control which information is displayed. When\n"
542 "combined, the fields are displayed on separate lines unless the\n"
543 "-m option is given.\n"
544 "\n"
545 "\t-b\tprint blocked thread state\n"
546 "\t-d\tprint dispatcher state\n"
547 "\t-f\tignore freed threads\n"
548 "\t-i\tprint basic thread state (default)\n"
549 "\t-m\tdisplay results on a single line\n"
550 "\t-p\tprint process and lwp state\n"
551 "\t-s\tprint signal state\n");
552 }
553
554 /*
555 * List a combination of kthread_t and proc_t. Add stack traces in verbose mode.
556 */
557 int
threadlist(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)558 threadlist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
559 {
560 int i;
561 uint_t count = 0;
562 uint_t verbose = FALSE;
563 uint_t notaskq = FALSE;
564 kthread_t t;
565 taskq_t tq;
566 proc_t p;
567 char cmd[80];
568 mdb_arg_t cmdarg;
569
570 if (!(flags & DCMD_ADDRSPEC)) {
571 if (mdb_walk_dcmd("thread", "threadlist", argc, argv) == -1) {
572 mdb_warn("can't walk threads");
573 return (DCMD_ERR);
574 }
575 return (DCMD_OK);
576 }
577
578 i = mdb_getopts(argc, argv,
579 't', MDB_OPT_SETBITS, TRUE, ¬askq,
580 'v', MDB_OPT_SETBITS, TRUE, &verbose, NULL);
581
582 if (i != argc) {
583 if (i != argc - 1 || !verbose)
584 return (DCMD_USAGE);
585
586 if (argv[i].a_type == MDB_TYPE_IMMEDIATE)
587 count = (uint_t)argv[i].a_un.a_val;
588 else
589 count = (uint_t)mdb_strtoull(argv[i].a_un.a_str);
590 }
591
592 if (DCMD_HDRSPEC(flags)) {
593 if (verbose)
594 mdb_printf("%<u>%?s %?s %?s %3s %3s %?s%</u>\n",
595 "ADDR", "PROC", "LWP", "CLS", "PRI", "WCHAN");
596 else
597 mdb_printf("%<u>%?s %?s %?s %s/%s%</u>\n",
598 "ADDR", "PROC", "LWP", "CMD", "LWPID");
599 }
600
601 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
602 mdb_warn("failed to read kthread_t at %p", addr);
603 return (DCMD_ERR);
604 }
605
606 if (notaskq && t.t_taskq != NULL)
607 return (DCMD_OK);
608
609 if (t.t_state == TS_FREE)
610 return (DCMD_OK);
611
612 if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) {
613 mdb_warn("failed to read proc at %p", t.t_procp);
614 return (DCMD_ERR);
615 }
616
617 if (mdb_vread(&tq, sizeof (taskq_t), (uintptr_t)t.t_taskq) == -1)
618 tq.tq_name[0] = '\0';
619
620 if (verbose) {
621 mdb_printf("%0?p %?p %?p %3u %3d %?p\n",
622 addr, t.t_procp, t.t_lwp, t.t_cid, t.t_pri, t.t_wchan);
623
624 mdb_inc_indent(2);
625
626 mdb_printf("PC: %a", t.t_pc);
627 if (t.t_tid == 0) {
628 if (tq.tq_name[0] != '\0')
629 mdb_printf(" TASKQ: %s\n", tq.tq_name);
630 else
631 mdb_printf(" THREAD: %a()\n", t.t_startpc);
632 } else {
633 mdb_printf(" CMD: %s\n", p.p_user.u_psargs);
634 }
635
636 mdb_snprintf(cmd, sizeof (cmd), "<.$c%d", count);
637 cmdarg.a_type = MDB_TYPE_STRING;
638 cmdarg.a_un.a_str = cmd;
639
640 (void) mdb_call_dcmd("findstack", addr, flags, 1, &cmdarg);
641
642 mdb_dec_indent(2);
643
644 mdb_printf("\n");
645 } else {
646 mdb_printf("%0?p %?p %?p", addr, t.t_procp, t.t_lwp);
647 if (t.t_tid == 0) {
648 if (tq.tq_name[0] != '\0')
649 mdb_printf(" tq:%s\n", tq.tq_name);
650 else
651 mdb_printf(" %a()\n", t.t_startpc);
652 } else {
653 mdb_printf(" %s/%u\n", p.p_user.u_comm, t.t_tid);
654 }
655 }
656
657 return (DCMD_OK);
658 }
659
660 void
threadlist_help(void)661 threadlist_help(void)
662 {
663 mdb_printf(
664 " -v print verbose output including C stack trace\n"
665 " -t skip threads belonging to a taskq\n"
666 " count print no more than count arguments (default 0)\n");
667 }
668
669 static size_t
stk_compute_percent(caddr_t t_stk,caddr_t t_stkbase,caddr_t sp)670 stk_compute_percent(caddr_t t_stk, caddr_t t_stkbase, caddr_t sp)
671 {
672 size_t percent;
673 size_t s;
674
675 if (t_stk > t_stkbase) {
676 /* stack grows down */
677 if (sp > t_stk) {
678 return (0);
679 }
680 if (sp < t_stkbase) {
681 return (100);
682 }
683 percent = t_stk - sp + 1;
684 s = t_stk - t_stkbase + 1;
685 } else {
686 /* stack grows up */
687 if (sp < t_stk) {
688 return (0);
689 }
690 if (sp > t_stkbase) {
691 return (100);
692 }
693 percent = sp - t_stk + 1;
694 s = t_stkbase - t_stk + 1;
695 }
696 percent = ((100 * percent) / s) + 1;
697 if (percent > 100) {
698 percent = 100;
699 }
700 return (percent);
701 }
702
703 /*
704 * Display kthread stack infos.
705 */
706 int
stackinfo(uintptr_t addr,uint_t flags,int argc,const mdb_arg_t * argv)707 stackinfo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
708 {
709 kthread_t t;
710 proc_t p;
711 uint64_t *ptr; /* pattern pointer */
712 caddr_t start; /* kernel stack start */
713 caddr_t end; /* kernel stack end */
714 caddr_t ustack; /* userland copy of kernel stack */
715 size_t usize; /* userland copy of kernel stack size */
716 caddr_t ustart; /* userland copy of kernel stack, aligned start */
717 caddr_t uend; /* userland copy of kernel stack, aligned end */
718 size_t percent = 0;
719 uint_t all = FALSE; /* don't show TS_FREE kthread by default */
720 uint_t history = FALSE;
721 int i = 0;
722 unsigned int ukmem_stackinfo;
723 uintptr_t allthreads;
724
725 /* handle options */
726 if (mdb_getopts(argc, argv,
727 'a', MDB_OPT_SETBITS, TRUE, &all,
728 'h', MDB_OPT_SETBITS, TRUE, &history, NULL) != argc) {
729 return (DCMD_USAGE);
730 }
731
732 /* walk all kthread if needed */
733 if ((history == FALSE) && !(flags & DCMD_ADDRSPEC)) {
734 if (mdb_walk_dcmd("thread", "stackinfo", argc, argv) == -1) {
735 mdb_warn("can't walk threads");
736 return (DCMD_ERR);
737 }
738 return (DCMD_OK);
739 }
740
741 /* read 'kmem_stackinfo' */
742 if (mdb_readsym(&ukmem_stackinfo, sizeof (ukmem_stackinfo),
743 "kmem_stackinfo") == -1) {
744 mdb_warn("failed to read 'kmem_stackinfo'\n");
745 ukmem_stackinfo = 0;
746 }
747
748 /* read 'allthreads' */
749 if (mdb_readsym(&allthreads, sizeof (kthread_t *),
750 "allthreads") == -1) {
751 mdb_warn("failed to read 'allthreads'\n");
752 allthreads = NULL;
753 }
754
755 if (history == TRUE) {
756 kmem_stkinfo_t *log;
757 uintptr_t kaddr;
758
759 mdb_printf("Dead kthreads stack usage history:\n");
760 if (ukmem_stackinfo == 0) {
761 mdb_printf("Tunable kmem_stackinfo is unset, history ");
762 mdb_printf("feature is off.\nUse ::help stackinfo ");
763 mdb_printf("for more details.\n");
764 return (DCMD_OK);
765 }
766
767 mdb_printf("%<u>%?s%</u>", "THREAD");
768 mdb_printf(" %<u>%?s%</u>", "STACK");
769 mdb_printf("%<u>%s%</u>", " SIZE MAX CMD/LWPID or STARTPC");
770 mdb_printf("\n");
771 usize = KMEM_STKINFO_LOG_SIZE * sizeof (kmem_stkinfo_t);
772 log = (kmem_stkinfo_t *)mdb_alloc(usize, UM_SLEEP);
773 if (mdb_readsym(&kaddr, sizeof (kaddr),
774 "kmem_stkinfo_log") == -1) {
775 mdb_free((void *)log, usize);
776 mdb_warn("failed to read 'kmem_stkinfo_log'\n");
777 return (DCMD_ERR);
778 }
779 if (kaddr == NULL) {
780 mdb_free((void *)log, usize);
781 return (DCMD_OK);
782 }
783 if (mdb_vread(log, usize, kaddr) == -1) {
784 mdb_free((void *)log, usize);
785 mdb_warn("failed to read %p\n", kaddr);
786 return (DCMD_ERR);
787 }
788 for (i = 0; i < KMEM_STKINFO_LOG_SIZE; i++) {
789 if (log[i].kthread == NULL) {
790 continue;
791 }
792 mdb_printf("%0?p %0?p %6x %3d%%",
793 log[i].kthread,
794 log[i].start,
795 (uint_t)log[i].stksz,
796 (int)log[i].percent);
797 if (log[i].t_tid != 0) {
798 mdb_printf(" %s/%u\n",
799 log[i].cmd, log[i].t_tid);
800 } else {
801 mdb_printf(" %p (%a)\n", log[i].t_startpc,
802 log[i].t_startpc);
803 }
804 }
805 mdb_free((void *)log, usize);
806 return (DCMD_OK);
807 }
808
809 /* display header */
810 if (DCMD_HDRSPEC(flags)) {
811 if (ukmem_stackinfo == 0) {
812 mdb_printf("Tunable kmem_stackinfo is unset, ");
813 mdb_printf("MAX value is not available.\n");
814 mdb_printf("Use ::help stackinfo for more details.\n");
815 }
816 mdb_printf("%<u>%?s%</u>", "THREAD");
817 mdb_printf(" %<u>%?s%</u>", "STACK");
818 mdb_printf("%<u>%s%</u>", " SIZE CUR MAX CMD/LWPID");
819 mdb_printf("\n");
820 }
821
822 /* read kthread */
823 if (mdb_vread(&t, sizeof (kthread_t), addr) == -1) {
824 mdb_warn("can't read kthread_t at %#lx\n", addr);
825 return (DCMD_ERR);
826 }
827
828 if (t.t_state == TS_FREE && all == FALSE) {
829 return (DCMD_OK);
830 }
831
832 /* read proc */
833 if (mdb_vread(&p, sizeof (proc_t), (uintptr_t)t.t_procp) == -1) {
834 mdb_warn("failed to read proc at %p\n", t.t_procp);
835 return (DCMD_ERR);
836 }
837
838 /*
839 * Stack grows up or down, see thread_create(),
840 * compute stack memory aera start and end (start < end).
841 */
842 if (t.t_stk > t.t_stkbase) {
843 /* stack grows down */
844 start = t.t_stkbase;
845 end = t.t_stk;
846 } else {
847 /* stack grows up */
848 start = t.t_stk;
849 end = t.t_stkbase;
850 }
851
852 /* display stack info */
853 mdb_printf("%0?p %0?p", addr, start);
854
855 /* (end - start), kernel stack size as found in kthread_t */
856 if ((end <= start) || ((end - start) > (1024 * 1024))) {
857 /* negative or stack size > 1 meg, assume bogus */
858 mdb_warn(" t_stk/t_stkbase problem\n");
859 return (DCMD_ERR);
860 }
861
862 /* display stack size */
863 mdb_printf(" %6x", end - start);
864
865 /* display current stack usage */
866 percent = stk_compute_percent(t.t_stk, t.t_stkbase,
867 (caddr_t)t.t_sp + STACK_BIAS);
868
869 mdb_printf(" %3d%%", percent);
870 percent = 0;
871
872 if (ukmem_stackinfo == 0) {
873 mdb_printf(" n/a");
874 if (t.t_tid == 0) {
875 mdb_printf(" %a()", t.t_startpc);
876 } else {
877 mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid);
878 }
879 mdb_printf("\n");
880 return (DCMD_OK);
881 }
882
883 if ((((uintptr_t)start) & 0x7) != 0) {
884 start = (caddr_t)((((uintptr_t)start) & (~0x7)) + 8);
885 }
886 end = (caddr_t)(((uintptr_t)end) & (~0x7));
887 /* size to scan in userland copy of kernel stack */
888 usize = end - start; /* is a multiple of 8 bytes */
889
890 /*
891 * Stackinfo pattern size is 8 bytes. Ensure proper 8 bytes
892 * alignement for ustart and uend, in boundaries.
893 */
894 ustart = ustack = (caddr_t)mdb_alloc(usize + 8, UM_SLEEP);
895 if ((((uintptr_t)ustart) & 0x7) != 0) {
896 ustart = (caddr_t)((((uintptr_t)ustart) & (~0x7)) + 8);
897 }
898 uend = ustart + usize;
899
900 /* read the kernel stack */
901 if (mdb_vread(ustart, usize, (uintptr_t)start) != usize) {
902 mdb_free((void *)ustack, usize + 8);
903 mdb_printf("\n");
904 mdb_warn("couldn't read entire stack\n");
905 return (DCMD_ERR);
906 }
907
908 /* scan the stack */
909 if (t.t_stk > t.t_stkbase) {
910 /* stack grows down */
911 #if defined(__i386) || defined(__amd64)
912 /*
913 * 6 longs are pushed on stack, see thread_load(). Skip
914 * them, so if kthread has never run, percent is zero.
915 * 8 bytes alignement is preserved for a 32 bit kernel,
916 * 6 x 4 = 24, 24 is a multiple of 8.
917 */
918 uend -= (6 * sizeof (long));
919 #endif
920 ptr = (uint64_t *)((void *)ustart);
921 while (ptr < (uint64_t *)((void *)uend)) {
922 if (*ptr != KMEM_STKINFO_PATTERN) {
923 percent = stk_compute_percent(uend,
924 ustart, (caddr_t)ptr);
925 break;
926 }
927 ptr++;
928 }
929 } else {
930 /* stack grows up */
931 ptr = (uint64_t *)((void *)uend);
932 ptr--;
933 while (ptr >= (uint64_t *)((void *)ustart)) {
934 if (*ptr != KMEM_STKINFO_PATTERN) {
935 percent = stk_compute_percent(ustart,
936 uend, (caddr_t)ptr);
937 break;
938 }
939 ptr--;
940 }
941 }
942
943 /* thread 't0' stack is not created by thread_create() */
944 if (addr == allthreads) {
945 percent = 0;
946 }
947 if (percent != 0) {
948 mdb_printf(" %3d%%", percent);
949 } else {
950 mdb_printf(" n/a");
951 }
952 if (t.t_tid == 0) {
953 mdb_printf(" %a()", t.t_startpc);
954 } else {
955 mdb_printf(" %s/%u", p.p_user.u_comm, t.t_tid);
956 }
957 mdb_printf("\n");
958 mdb_free((void *)ustack, usize + 8);
959 return (DCMD_OK);
960 }
961
962 void
stackinfo_help(void)963 stackinfo_help(void)
964 {
965 mdb_printf(
966 "Shows kernel stacks real utilization, if /etc/system "
967 "kmem_stackinfo tunable\n");
968 mdb_printf(
969 "(an unsigned integer) is non zero at kthread creation time. ");
970 mdb_printf("For example:\n");
971 mdb_printf(
972 " THREAD STACK SIZE CUR MAX CMD/LWPID\n");
973 mdb_printf(
974 "ffffff014f5f2c20 ffffff0004153000 4f00 4%% 43%% init/1\n");
975 mdb_printf(
976 "The stack size utilization for this kthread is at 4%%"
977 " of its maximum size,\n");
978 mdb_printf(
979 "but has already used up to 43%%, stack size is 4f00 bytes.\n");
980 mdb_printf(
981 "MAX value can be shown as n/a (not available):\n");
982 mdb_printf(
983 " - for the very first kthread (sched/1)\n");
984 mdb_printf(
985 " - kmem_stackinfo was zero at kthread creation time\n");
986 mdb_printf(
987 " - kthread has not yet run\n");
988 mdb_printf("\n");
989 mdb_printf("Options:\n");
990 mdb_printf(
991 "-a shows also TS_FREE kthreads (interrupt kthreads)\n");
992 mdb_printf(
993 "-h shows history, dead kthreads that used their "
994 "kernel stack the most\n");
995 mdb_printf(
996 "\nSee Solaris Modular Debugger Guide for detailed usage.\n");
997 mdb_flush();
998 }
999