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 /* 23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 #include <mdb/mdb_modapi.h> 27 #include <fcntl.h> 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <sys/avl.h> 31 #include <sys/lwp.h> 32 #include <thr_uberdata.h> 33 #include <stddef.h> 34 #include "findstack.h" 35 36 #if defined(__i386) || defined(__amd64) 37 struct rwindow { 38 uintptr_t rw_fp; 39 uintptr_t rw_rtn; 40 }; 41 #endif 42 43 #ifndef STACK_BIAS 44 #define STACK_BIAS 0 45 #endif 46 47 #ifdef __amd64 48 #define STACKS_REGS_FP "rbp" 49 #define STACKS_REGS_RC "rip" 50 #else 51 #ifdef __i386 52 #define STACKS_REGS_FP "ebp" 53 #define STACKS_REGS_RC "eip" 54 #else 55 #define STACKS_REGS_FP "fp" 56 #define STACKS_REGS_RC "pc" 57 #endif 58 #endif 59 60 #define STACKS_SOBJ_MX (uintptr_t)"MX" 61 #define STACKS_SOBJ_CV (uintptr_t)"CV" 62 63 int 64 thread_text_to_state(const char *state, uint_t *out) 65 { 66 if (strcmp(state, "PARKED") == 0) { 67 *out = B_TRUE; 68 } else if (strcmp(state, "UNPARKED") == 0) { 69 *out = B_FALSE; 70 } else if (strcmp(state, "FREE") == 0) { 71 /* 72 * When run with "-i", ::stacks filters out "FREE" threads. 73 * We therefore need to recognize "FREE", and set it to a 74 * value that will never match fsi_tstate. 75 */ 76 *out = UINT_MAX; 77 } else { 78 return (-1); 79 } 80 81 return (0); 82 } 83 84 void 85 thread_state_to_text(uint_t state, char *out, size_t out_sz) 86 { 87 (void) snprintf(out, out_sz, state ? "PARKED" : "UNPARKED"); 88 } 89 90 int 91 sobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out) 92 { 93 if (strcmp(name, "MX") == 0) { 94 *sobj_ops_out = STACKS_SOBJ_MX; 95 } else if (strcmp(name, "CV") == 0) { 96 *sobj_ops_out = STACKS_SOBJ_CV; 97 } else { 98 mdb_warn("sobj \"%s\" not recognized\n", name); 99 return (-1); 100 } 101 102 return (0); 103 } 104 105 void 106 sobj_ops_to_text(uintptr_t addr, char *out, size_t sz) 107 { 108 (void) snprintf(out, sz, "%s", addr == NULL ? "<none>" : (char *)addr); 109 } 110 111 static int 112 stacks_module_callback(mdb_object_t *obj, void *arg) 113 { 114 stacks_module_t *smp = arg; 115 boolean_t match = (strcmp(obj->obj_name, smp->sm_name) == 0); 116 char *suffix = ".so"; 117 const char *s, *next; 118 size_t len; 119 120 if (smp->sm_size != 0) 121 return (0); 122 123 /* 124 * It doesn't match the name, but -- for convenience -- we want to 125 * allow matches before ".so.[suffix]". An aside: why doesn't 126 * strrstr() exist? (Don't google that. I'm serious, don't do it. 127 * If you do, and you read the thread of "why doesn't strrstr() exist?" 128 * circa 2005 you will see things that you will NEVER be able to unsee!) 129 */ 130 if (!match && (s = strstr(obj->obj_name, suffix)) != NULL) { 131 while ((next = strstr(s + 1, suffix)) != NULL) { 132 s = next; 133 continue; 134 } 135 136 len = s - obj->obj_name; 137 138 match = (strncmp(smp->sm_name, obj->obj_name, len) == 0 && 139 smp->sm_name[len] == '\0'); 140 } 141 142 /* 143 * If we have a library that has the libc directory in the path, we 144 * want to match against anything that would match libc.so.1. (This 145 * is necessary to be able to easily deal with libc implementations 146 * that have alternate hardware capabilities.) 147 */ 148 if (!match && strstr(obj->obj_fullname, "/libc/") != NULL) { 149 mdb_object_t libc = *obj; 150 151 libc.obj_name = "libc.so.1"; 152 libc.obj_fullname = ""; 153 154 return (stacks_module_callback(&libc, arg)); 155 } 156 157 if (match) { 158 smp->sm_text = obj->obj_base; 159 smp->sm_size = obj->obj_size; 160 } 161 162 return (0); 163 } 164 165 int 166 stacks_module(stacks_module_t *smp) 167 { 168 if (mdb_object_iter(stacks_module_callback, smp) != 0) 169 return (-1); 170 171 return (0); 172 } 173 174 typedef struct stacks_ulwp { 175 avl_node_t sulwp_node; 176 lwpid_t sulwp_id; 177 uintptr_t sulwp_addr; 178 } stacks_ulwp_t; 179 180 boolean_t stacks_ulwp_initialized; 181 avl_tree_t stacks_ulwp_byid; 182 183 /*ARGSUSED*/ 184 int 185 stacks_ulwp_walk(uintptr_t addr, ulwp_t *ulwp, void *ignored) 186 { 187 stacks_ulwp_t *sulwp = mdb_alloc(sizeof (stacks_ulwp_t), UM_SLEEP); 188 189 sulwp->sulwp_id = ulwp->ul_lwpid; 190 sulwp->sulwp_addr = addr; 191 192 if (avl_find(&stacks_ulwp_byid, sulwp, NULL) != NULL) { 193 mdb_warn("found multiple LWPs with ID %d!", ulwp->ul_lwpid); 194 return (WALK_ERR); 195 } 196 197 avl_add(&stacks_ulwp_byid, sulwp); 198 199 return (WALK_NEXT); 200 } 201 202 static int 203 stacks_ulwp_compare(const void *l, const void *r) 204 { 205 const stacks_ulwp_t *lhs = l; 206 const stacks_ulwp_t *rhs = r; 207 208 if (lhs->sulwp_id > rhs->sulwp_id) 209 return (1); 210 211 if (lhs->sulwp_id < rhs->sulwp_id) 212 return (-1); 213 214 return (0); 215 } 216 217 /*ARGSUSED*/ 218 int 219 stacks_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings) 220 { 221 mdb_reg_t reg; 222 uintptr_t fp; 223 struct rwindow frame; 224 avl_tree_t *tree = &stacks_ulwp_byid; 225 stacks_ulwp_t *sulwp, cmp; 226 ulwp_t ulwp; 227 228 fsip->fsi_failed = 0; 229 fsip->fsi_pc = 0; 230 fsip->fsi_sp = 0; 231 fsip->fsi_depth = 0; 232 fsip->fsi_overflow = 0; 233 234 if (!stacks_ulwp_initialized) { 235 avl_create(tree, stacks_ulwp_compare, sizeof (stacks_ulwp_t), 236 offsetof(stacks_ulwp_t, sulwp_node)); 237 238 if (mdb_walk("ulwp", 239 (mdb_walk_cb_t)stacks_ulwp_walk, NULL) != 0) { 240 mdb_warn("couldn't walk 'ulwp'"); 241 return (-1); 242 } 243 244 stacks_ulwp_initialized = B_TRUE; 245 } 246 247 bzero(&cmp, sizeof (cmp)); 248 cmp.sulwp_id = (lwpid_t)addr; 249 250 if ((sulwp = avl_find(tree, &cmp, NULL)) == NULL) { 251 mdb_warn("couldn't find ulwp_t for tid %d\n", cmp.sulwp_id); 252 return (-1); 253 } 254 255 if (mdb_vread(&ulwp, sizeof (ulwp), sulwp->sulwp_addr) == -1) { 256 mdb_warn("couldn't read ulwp_t for tid %d at %p", 257 cmp.sulwp_id, sulwp->sulwp_addr); 258 return (-1); 259 } 260 261 fsip->fsi_tstate = ulwp.ul_sleepq != NULL; 262 fsip->fsi_sobj_ops = (uintptr_t)(ulwp.ul_sleepq == NULL ? NULL : 263 (ulwp.ul_qtype == MX ? STACKS_SOBJ_MX : STACKS_SOBJ_CV)); 264 265 if (mdb_getareg(addr, STACKS_REGS_FP, ®) != 0) { 266 mdb_warn("couldn't read frame pointer for thread 0x%p", addr); 267 return (-1); 268 } 269 270 fsip->fsi_sp = fp = (uintptr_t)reg; 271 272 #if !defined(__i386) 273 if (mdb_getareg(addr, STACKS_REGS_RC, ®) != 0) { 274 mdb_warn("couldn't read program counter for thread 0x%p", addr); 275 return (-1); 276 } 277 278 fsip->fsi_pc = (uintptr_t)reg; 279 #endif 280 281 while (fp != NULL) { 282 if (mdb_vread(&frame, sizeof (frame), fp) == -1) { 283 mdb_warn("couldn't read frame for thread 0x%p at %p", 284 addr, fp); 285 return (-1); 286 } 287 288 if (frame.rw_rtn == NULL) 289 break; 290 291 if (fsip->fsi_depth < fsip->fsi_max_depth) { 292 fsip->fsi_stack[fsip->fsi_depth++] = frame.rw_rtn; 293 } else { 294 fsip->fsi_overflow = 1; 295 break; 296 } 297 298 fp = frame.rw_fp + STACK_BIAS; 299 } 300 301 return (0); 302 } 303 304 void 305 stacks_findstack_cleanup() 306 { 307 avl_tree_t *tree = &stacks_ulwp_byid; 308 void *cookie = NULL; 309 stacks_ulwp_t *sulwp; 310 311 if (!stacks_ulwp_initialized) 312 return; 313 314 while ((sulwp = avl_destroy_nodes(tree, &cookie)) != NULL) 315 mdb_free(sulwp, sizeof (stacks_ulwp_t)); 316 317 bzero(tree, sizeof (*tree)); 318 stacks_ulwp_initialized = B_FALSE; 319 } 320 321 void 322 stacks_help(void) 323 { 324 mdb_printf( 325 "::stacks processes all of the thread stacks in the process, grouping\n" 326 "together threads which have the same:\n" 327 "\n" 328 " * Thread state,\n" 329 " * Sync object type, and\n" 330 " * PCs in their stack trace.\n" 331 "\n" 332 "The default output (no address or options) is just a dump of the thread\n" 333 "groups in the process. For a view of active threads, use \"::stacks -i\",\n" 334 "which filters out threads sleeping on a CV. More general filtering options\n" 335 "are described below, in the \"FILTERS\" section.\n" 336 "\n" 337 "::stacks can be used in a pipeline. The input to ::stacks is one or more\n" 338 "thread IDs. When output into a pipe, ::stacks prints all of the threads \n" 339 "input, filtered by the given filtering options. This means that multiple\n" 340 "::stacks invocations can be piped together to achieve more complicated\n" 341 "filters. For example, to get threads which have both '__door_return' and\n" 342 "'mutex_lock' in their stack trace, you could do:\n" 343 "\n" 344 " ::stacks -c __door_return | ::stacks -c mutex_lock\n" 345 "\n" 346 "To get the full list of threads in each group, use the '-a' flag:\n" 347 "\n" 348 " ::stacks -a\n" 349 "\n"); 350 mdb_dec_indent(2); 351 mdb_printf("%<b>OPTIONS%</b>\n"); 352 mdb_inc_indent(2); 353 mdb_printf("%s", 354 " -a Print all of the grouped threads, instead of just a count.\n" 355 " -f Force a re-run of the thread stack gathering.\n" 356 " -v Be verbose about thread stack gathering.\n" 357 "\n"); 358 mdb_dec_indent(2); 359 mdb_printf("%<b>FILTERS%</b>\n"); 360 mdb_inc_indent(2); 361 mdb_printf("%s", 362 " -i Show active threads; equivalent to '-S CV'.\n" 363 " -c func[+offset]\n" 364 " Only print threads whose stacks contain func/func+offset.\n" 365 " -C func[+offset]\n" 366 " Only print threads whose stacks do not contain func/func+offset.\n" 367 " -m module\n" 368 " Only print threads whose stacks contain functions from module.\n" 369 " -M module\n" 370 " Only print threads whose stacks do not contain functions from\n" 371 " module.\n" 372 " -s {type | ALL}\n" 373 " Only print threads which are on a 'type' synchronization object\n" 374 " (SOBJ).\n" 375 " -S {type | ALL}\n" 376 " Only print threads which are not on a 'type' SOBJ.\n" 377 " -t tstate\n" 378 " Only print threads which are in thread state 'tstate'.\n" 379 " -T tstate\n" 380 " Only print threads which are not in thread state 'tstate'.\n" 381 "\n"); 382 } 383