c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill/*
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * CDDL HEADER START
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill *
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * The contents of this file are subject to the terms of the
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * Common Development and Distribution License (the "License").
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * You may not use this file except in compliance with the License.
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill *
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * or http://www.opensolaris.org/os/licensing.
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * See the License for the specific language governing permissions
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * and limitations under the License.
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill *
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * When distributing Covered Code, include this CDDL HEADER in each
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * If applicable, add the following below this CDDL HEADER, with the
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * fields enclosed by brackets "[]" replaced with your own identifying
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * information: Portions Copyright [yyyy] [name of copyright owner]
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill *
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * CDDL HEADER END
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill */
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill/*
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill */
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#include <mdb/mdb_modapi.h>
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#include <fcntl.h>
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#include <stdio.h>
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#include <stdlib.h>
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#include <sys/avl.h>
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#include <sys/lwp.h>
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#include <thr_uberdata.h>
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#include <stddef.h>
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#include "findstack.h"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#if defined(__i386) || defined(__amd64)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillstruct rwindow {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill uintptr_t rw_fp;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill uintptr_t rw_rtn;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill};
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#endif
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#ifndef STACK_BIAS
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#define STACK_BIAS 0
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#endif
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#ifdef __amd64
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#define STACKS_REGS_FP "rbp"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#define STACKS_REGS_RC "rip"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#else
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#ifdef __i386
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#define STACKS_REGS_FP "ebp"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#define STACKS_REGS_RC "eip"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#else
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#define STACKS_REGS_FP "fp"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#define STACKS_REGS_RC "pc"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#endif
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#endif
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#define STACKS_SOBJ_MX (uintptr_t)"MX"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#define STACKS_SOBJ_CV (uintptr_t)"CV"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillint
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillthread_text_to_state(const char *state, uint_t *out)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill{
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (strcmp(state, "PARKED") == 0) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill *out = B_TRUE;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill } else if (strcmp(state, "UNPARKED") == 0) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill *out = B_FALSE;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill } else if (strcmp(state, "FREE") == 0) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill /*
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * When run with "-i", ::stacks filters out "FREE" threads.
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * We therefore need to recognize "FREE", and set it to a
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * value that will never match fsi_tstate.
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill */
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill *out = UINT_MAX;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill } else {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (-1);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (0);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill}
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillvoid
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillthread_state_to_text(uint_t state, char *out, size_t out_sz)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill{
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill (void) snprintf(out, out_sz, state ? "PARKED" : "UNPARKED");
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill}
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillint
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillsobj_text_to_ops(const char *name, uintptr_t *sobj_ops_out)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill{
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (strcmp(name, "MX") == 0) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill *sobj_ops_out = STACKS_SOBJ_MX;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill } else if (strcmp(name, "CV") == 0) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill *sobj_ops_out = STACKS_SOBJ_CV;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill } else {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_warn("sobj \"%s\" not recognized\n", name);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (-1);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (0);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill}
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillvoid
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillsobj_ops_to_text(uintptr_t addr, char *out, size_t sz)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill{
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill (void) snprintf(out, sz, "%s", addr == NULL ? "<none>" : (char *)addr);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill}
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillstatic int
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillstacks_module_callback(mdb_object_t *obj, void *arg)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill{
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill stacks_module_t *smp = arg;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill boolean_t match = (strcmp(obj->obj_name, smp->sm_name) == 0);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill char *suffix = ".so";
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill const char *s, *next;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill size_t len;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (smp->sm_size != 0)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (0);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill /*
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * It doesn't match the name, but -- for convenience -- we want to
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * allow matches before ".so.[suffix]". An aside: why doesn't
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * strrstr() exist? (Don't google that. I'm serious, don't do it.
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * If you do, and you read the thread of "why doesn't strrstr() exist?"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * circa 2005 you will see things that you will NEVER be able to unsee!)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill */
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (!match && (s = strstr(obj->obj_name, suffix)) != NULL) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill while ((next = strstr(s + 1, suffix)) != NULL) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill s = next;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill continue;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill len = s - obj->obj_name;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill match = (strncmp(smp->sm_name, obj->obj_name, len) == 0 &&
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill smp->sm_name[len] == '\0');
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill /*
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * If we have a library that has the libc directory in the path, we
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * want to match against anything that would match libc.so.1. (This
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * is necessary to be able to easily deal with libc implementations
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill * that have alternate hardware capabilities.)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill */
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (!match && strstr(obj->obj_fullname, "/libc/") != NULL) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_object_t libc = *obj;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill libc.obj_name = "libc.so.1";
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill libc.obj_fullname = "";
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (stacks_module_callback(&libc, arg));
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (match) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill smp->sm_text = obj->obj_base;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill smp->sm_size = obj->obj_size;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (0);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill}
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillint
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillstacks_module(stacks_module_t *smp)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill{
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (mdb_object_iter(stacks_module_callback, smp) != 0)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (-1);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (0);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill}
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrilltypedef struct stacks_ulwp {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill avl_node_t sulwp_node;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill lwpid_t sulwp_id;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill uintptr_t sulwp_addr;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill} stacks_ulwp_t;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillboolean_t stacks_ulwp_initialized;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillavl_tree_t stacks_ulwp_byid;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill/*ARGSUSED*/
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillint
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillstacks_ulwp_walk(uintptr_t addr, ulwp_t *ulwp, void *ignored)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill{
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill stacks_ulwp_t *sulwp = mdb_alloc(sizeof (stacks_ulwp_t), UM_SLEEP);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill sulwp->sulwp_id = ulwp->ul_lwpid;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill sulwp->sulwp_addr = addr;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (avl_find(&stacks_ulwp_byid, sulwp, NULL) != NULL) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_warn("found multiple LWPs with ID %d!", ulwp->ul_lwpid);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (WALK_ERR);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill avl_add(&stacks_ulwp_byid, sulwp);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (WALK_NEXT);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill}
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillstatic int
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillstacks_ulwp_compare(const void *l, const void *r)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill{
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill const stacks_ulwp_t *lhs = l;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill const stacks_ulwp_t *rhs = r;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (lhs->sulwp_id > rhs->sulwp_id)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (1);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (lhs->sulwp_id < rhs->sulwp_id)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (-1);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (0);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill}
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill/*ARGSUSED*/
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillint
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillstacks_findstack(uintptr_t addr, findstack_info_t *fsip, uint_t print_warnings)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill{
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_reg_t reg;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill uintptr_t fp;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill struct rwindow frame;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill avl_tree_t *tree = &stacks_ulwp_byid;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill stacks_ulwp_t *sulwp, cmp;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill ulwp_t ulwp;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fsip->fsi_failed = 0;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fsip->fsi_pc = 0;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fsip->fsi_sp = 0;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fsip->fsi_depth = 0;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fsip->fsi_overflow = 0;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (!stacks_ulwp_initialized) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill avl_create(tree, stacks_ulwp_compare, sizeof (stacks_ulwp_t),
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill offsetof(stacks_ulwp_t, sulwp_node));
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (mdb_walk("ulwp",
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill (mdb_walk_cb_t)stacks_ulwp_walk, NULL) != 0) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_warn("couldn't walk 'ulwp'");
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (-1);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill stacks_ulwp_initialized = B_TRUE;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill bzero(&cmp, sizeof (cmp));
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill cmp.sulwp_id = (lwpid_t)addr;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if ((sulwp = avl_find(tree, &cmp, NULL)) == NULL) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_warn("couldn't find ulwp_t for tid %d\n", cmp.sulwp_id);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (-1);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (mdb_vread(&ulwp, sizeof (ulwp), sulwp->sulwp_addr) == -1) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_warn("couldn't read ulwp_t for tid %d at %p",
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill cmp.sulwp_id, sulwp->sulwp_addr);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (-1);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fsip->fsi_tstate = ulwp.ul_sleepq != NULL;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fsip->fsi_sobj_ops = (uintptr_t)(ulwp.ul_sleepq == NULL ? NULL :
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill (ulwp.ul_qtype == MX ? STACKS_SOBJ_MX : STACKS_SOBJ_CV));
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (mdb_getareg(addr, STACKS_REGS_FP, &reg) != 0) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_warn("couldn't read frame pointer for thread 0x%p", addr);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (-1);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fsip->fsi_sp = fp = (uintptr_t)reg;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#if !defined(__i386)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (mdb_getareg(addr, STACKS_REGS_RC, &reg) != 0) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_warn("couldn't read program counter for thread 0x%p", addr);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (-1);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fsip->fsi_pc = (uintptr_t)reg;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill#endif
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill while (fp != NULL) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (mdb_vread(&frame, sizeof (frame), fp) == -1) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_warn("couldn't read frame for thread 0x%p at %p",
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill addr, fp);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (-1);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (frame.rw_rtn == NULL)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill break;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (fsip->fsi_depth < fsip->fsi_max_depth) {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fsip->fsi_stack[fsip->fsi_depth++] = frame.rw_rtn;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill } else {
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fsip->fsi_overflow = 1;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill break;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill fp = frame.rw_fp + STACK_BIAS;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill }
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return (0);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill}
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillvoid
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillstacks_findstack_cleanup()
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill{
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill avl_tree_t *tree = &stacks_ulwp_byid;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill void *cookie = NULL;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill stacks_ulwp_t *sulwp;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill if (!stacks_ulwp_initialized)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill return;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill while ((sulwp = avl_destroy_nodes(tree, &cookie)) != NULL)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_free(sulwp, sizeof (stacks_ulwp_t));
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill bzero(tree, sizeof (*tree));
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill stacks_ulwp_initialized = B_FALSE;
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill}
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillvoid
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrillstacks_help(void)
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill{
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_printf(
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"::stacks processes all of the thread stacks in the process, grouping\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"together threads which have the same:\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" * Thread state,\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" * Sync object type, and\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" * PCs in their stack trace.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"The default output (no address or options) is just a dump of the thread\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"groups in the process. For a view of active threads, use \"::stacks -i\",\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"which filters out threads sleeping on a CV. More general filtering options\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"are described below, in the \"FILTERS\" section.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"::stacks can be used in a pipeline. The input to ::stacks is one or more\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"thread IDs. When output into a pipe, ::stacks prints all of the threads \n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"input, filtered by the given filtering options. This means that multiple\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"::stacks invocations can be piped together to achieve more complicated\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"filters. For example, to get threads which have both '__door_return' and\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"'mutex_lock' in their stack trace, you could do:\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" ::stacks -c __door_return | ::stacks -c mutex_lock\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"To get the full list of threads in each group, use the '-a' flag:\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" ::stacks -a\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"\n");
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_dec_indent(2);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_printf("%<b>OPTIONS%</b>\n");
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_inc_indent(2);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_printf("%s",
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -a Print all of the grouped threads, instead of just a count.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -f Force a re-run of the thread stack gathering.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -v Be verbose about thread stack gathering.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"\n");
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_dec_indent(2);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_printf("%<b>FILTERS%</b>\n");
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_inc_indent(2);
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill mdb_printf("%s",
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -i Show active threads; equivalent to '-S CV'.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -c func[+offset]\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" Only print threads whose stacks contain func/func+offset.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -C func[+offset]\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" Only print threads whose stacks do not contain func/func+offset.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -m module\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" Only print threads whose stacks contain functions from module.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -M module\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" Only print threads whose stacks do not contain functions from\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" module.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -s {type | ALL}\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" Only print threads which are on a 'type' synchronization object\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" (SOBJ).\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -S {type | ALL}\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" Only print threads which are not on a 'type' SOBJ.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -t tstate\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" Only print threads which are in thread state 'tstate'.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" -T tstate\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill" Only print threads which are not in thread state 'tstate'.\n"
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill"\n");
c9a6ea2e938727c95af7108c5e00eee4c890c7aeBryan Cantrill}