rctl.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
563N/A/*
563N/A * CDDL HEADER START
563N/A *
563N/A * The contents of this file are subject to the terms of the
563N/A * Common Development and Distribution License, Version 1.0 only
563N/A * (the "License"). You may not use this file except in compliance
563N/A * with the License.
563N/A *
563N/A * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
563N/A * or http://www.opensolaris.org/os/licensing.
563N/A * See the License for the specific language governing permissions
563N/A * and limitations under the License.
563N/A *
563N/A * When distributing Covered Code, include this CDDL HEADER in each
563N/A * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
563N/A * If applicable, add the following below this CDDL HEADER, with the
563N/A * fields enclosed by brackets "[]" replaced with your own identifying
563N/A * information: Portions Copyright [yyyy] [name of copyright owner]
873N/A *
563N/A * CDDL HEADER END
563N/A */
563N/A/*
563N/A * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
563N/A * Use is subject to license terms.
733N/A */
563N/A
563N/A#pragma ident "%Z%%M% %I% %E% SMI"
563N/A
563N/A#include <mdb/mdb_modapi.h>
563N/A#include <sys/rctl.h>
563N/A#include <sys/proc.h>
563N/A#include <sys/task.h>
662N/A#include <sys/project.h>
662N/A#include <sys/zone.h>
563N/A
563N/Astatic int
563N/Aprint_val(uintptr_t addr, rctl_val_t *val, uintptr_t *enforced)
563N/A{
563N/A char *priv;
563N/A static const mdb_bitmask_t val_localflag_bits[] = {
563N/A { "SIGNAL", RCTL_LOCAL_SIGNAL, RCTL_LOCAL_SIGNAL },
563N/A { "DENY", RCTL_LOCAL_DENY, RCTL_LOCAL_DENY },
563N/A { "MAX", RCTL_LOCAL_MAXIMAL, RCTL_LOCAL_MAXIMAL },
563N/A { NULL, 0, 0 }
662N/A };
662N/A
563N/A switch (val->rcv_privilege) {
563N/A case (RCPRIV_BASIC):
623N/A priv = "basic";
623N/A break;
563N/A case (RCPRIV_PRIVILEGED):
563N/A priv = "privileged";
563N/A break;
563N/A case (RCPRIV_SYSTEM):
563N/A priv = "system";
563N/A break;
563N/A default:
563N/A priv = "???";
563N/A break;
563N/A };
563N/A
563N/A mdb_printf("\t%s ", addr == *enforced ? "(cur)": " ");
563N/A
563N/A mdb_printf("%-#18llx %11s\tflags=<%b>\n",
563N/A val->rcv_value, priv, val->rcv_flagaction, val_localflag_bits);
563N/A
563N/A return (WALK_NEXT);
563N/A}
563N/A
563N/Aint
563N/Arctl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
563N/A{
563N/A rctl_t rctl;
563N/A rctl_dict_entry_t dict;
563N/A char name[256];
563N/A rctl_hndl_t hndl;
563N/A
563N/A if (!(flags & DCMD_ADDRSPEC))
563N/A return (DCMD_USAGE);
563N/A
563N/A if (mdb_vread(&rctl, sizeof (rctl_t), addr) == -1) {
563N/A mdb_warn("failed to read rctl_t structure at %p", addr);
563N/A return (DCMD_ERR);
563N/A }
563N/A
563N/A if (argc != 0) {
563N/A const mdb_arg_t *argp = &argv[0];
563N/A
563N/A if (argp->a_type == MDB_TYPE_IMMEDIATE)
563N/A hndl = (rctl_hndl_t)argp->a_un.a_val;
563N/A else
563N/A hndl = (rctl_hndl_t)mdb_strtoull(argp->a_un.a_str);
563N/A
662N/A if (rctl.rc_id != hndl)
662N/A return (DCMD_OK);
662N/A }
662N/A
662N/A if (mdb_vread(&dict, sizeof (rctl_dict_entry_t),
662N/A (uintptr_t)rctl.rc_dict_entry) == -1) {
662N/A mdb_warn("failed to read dict entry for rctl_t %p at %p",
662N/A addr, rctl.rc_dict_entry);
662N/A return (DCMD_ERR);
662N/A }
662N/A
662N/A if (mdb_readstr(name, 256, (uintptr_t)(dict.rcd_name)) == -1) {
662N/A mdb_warn("failed to read name for rctl_t %p", addr);
662N/A return (DCMD_ERR);
662N/A }
662N/A
563N/A mdb_printf("%0?p\t%3d : %s\n", addr, rctl.rc_id, name);
563N/A
563N/A if (mdb_pwalk("rctl_val", (mdb_walk_cb_t)print_val, &(rctl.rc_cursor),
563N/A addr) == -1) {
563N/A mdb_warn("failed to walk all values for rctl_t %p", addr);
563N/A return (DCMD_ERR);
563N/A }
563N/A
563N/A return (DCMD_OK);
563N/A}
563N/A
563N/Aint
563N/Arctl_dict(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
563N/A{
563N/A rctl_dict_entry_t dict;
563N/A char name[256], *type = NULL;
563N/A
563N/A if (!(flags & DCMD_ADDRSPEC)) {
563N/A if (mdb_walk_dcmd("rctl_dict_list", "rctl_dict", argc,
563N/A argv) == -1) {
563N/A mdb_warn("failed to walk 'rctl_dict_list'");
563N/A return (DCMD_ERR);
563N/A }
563N/A return (DCMD_OK);
563N/A }
563N/A
563N/A if (DCMD_HDRSPEC(flags))
580N/A mdb_printf("%<u>%2s %-27s %?s %7s %s%</u>\n",
580N/A "ID", "NAME", "ADDR", "TYPE", "GLOBAL_FLAGS");
563N/A
563N/A if (mdb_vread(&dict, sizeof (dict), addr) == -1) {
563N/A mdb_warn("failed to read rctl_dict at %p", addr);
563N/A return (DCMD_ERR);
563N/A }
563N/A if (mdb_readstr(name, 256, (uintptr_t)(dict.rcd_name)) == -1) {
563N/A mdb_warn("failed to read rctl_dict name for %p", addr);
563N/A return (DCMD_ERR);
563N/A }
563N/A
563N/A switch (dict.rcd_entity) {
563N/A case RCENTITY_PROCESS:
563N/A type = "process";
563N/A break;
563N/A case RCENTITY_TASK:
563N/A type = "task";
563N/A break;
563N/A case RCENTITY_PROJECT:
563N/A type = "project";
563N/A break;
563N/A case RCENTITY_ZONE:
563N/A type = "zone";
563N/A break;
563N/A default:
563N/A type = "unknown";
563N/A break;
563N/A }
563N/A
563N/A mdb_printf("%2d %-27s %0?p %7s 0x%08x", dict.rcd_id, name, addr,
563N/A type, dict.rcd_flagaction);
563N/A
563N/A return (DCMD_OK);
563N/A}
563N/A
563N/Atypedef struct dict_data {
563N/A rctl_hndl_t hndl;
563N/A uintptr_t dict_addr;
563N/A rctl_entity_t type;
563N/A} dict_data_t;
563N/A
563N/Astatic int
662N/Ahndl2dict(uintptr_t addr, rctl_dict_entry_t *entry, dict_data_t *data)
662N/A{
662N/A if (data->hndl == entry->rcd_id) {
662N/A data->dict_addr = addr;
662N/A data->type = entry->rcd_entity;
662N/A return (WALK_DONE);
662N/A }
662N/A
563N/A return (WALK_NEXT);
563N/A}
563N/A
563N/A/*
563N/A * Print out all project, task, and process rctls for a given process.
563N/A * If a handle is specified, print only the rctl matching that handle
563N/A * for the process.
563N/A */
563N/Aint
563N/Arctl_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
563N/A{
563N/A proc_t proc;
563N/A uintptr_t set;
563N/A task_t task;
563N/A kproject_t proj;
563N/A zone_t zone;
623N/A dict_data_t rdict;
623N/A int i;
623N/A
623N/A rdict.dict_addr = NULL;
623N/A
662N/A if (!(flags & DCMD_ADDRSPEC))
662N/A return (DCMD_USAGE);
662N/A
662N/A if (argc == 0)
662N/A rdict.hndl = 0;
662N/A else if (argc == 1) {
662N/A /*
662N/A * User specified a handle. Go find the rctl_dict_entity_t
623N/A * structure so we know what type of rctl to look for.
623N/A */
623N/A const mdb_arg_t *argp = &argv[0];
623N/A
623N/A if (argp->a_type == MDB_TYPE_IMMEDIATE)
623N/A rdict.hndl = (rctl_hndl_t)argp->a_un.a_val;
623N/A else
623N/A rdict.hndl =
623N/A (rctl_hndl_t)mdb_strtoull(argp->a_un.a_str);
623N/A
623N/A if (mdb_walk("rctl_dict_list", (mdb_walk_cb_t)hndl2dict,
623N/A &rdict) == -1) {
623N/A mdb_warn("failed to walk rctl_dict_list");
623N/A return (DCMD_ERR);
623N/A }
623N/A /* Couldn't find a rctl_dict_entry_t for this handle */
563N/A if (rdict.dict_addr == NULL)
563N/A return (DCMD_ERR);
563N/A } else
563N/A return (DCMD_USAGE);
563N/A
563N/A
563N/A if (mdb_vread(&proc, sizeof (proc_t), addr) == -1) {
563N/A mdb_warn("failed to read proc at %p", addr);
563N/A return (DCMD_ERR);
563N/A }
563N/A if (mdb_vread(&zone, sizeof (zone_t), (uintptr_t)proc.p_zone) == -1) {
563N/A mdb_warn("failed to read zone at %p", proc.p_zone);
563N/A return (DCMD_ERR);
563N/A }
563N/A if (mdb_vread(&task, sizeof (task_t), (uintptr_t)proc.p_task) == -1) {
563N/A mdb_warn("failed to read task at %p", proc.p_task);
563N/A return (DCMD_ERR);
563N/A }
563N/A if (mdb_vread(&proj, sizeof (kproject_t),
563N/A (uintptr_t)task.tk_proj) == -1) {
563N/A mdb_warn("failed to read proj at %p", task.tk_proj);
563N/A return (DCMD_ERR);
563N/A }
563N/A
563N/A for (i = 0; i <= RC_MAX_ENTITY; i++) {
563N/A /*
563N/A * If user didn't specify a handle, print rctls for all
563N/A * types. Otherwise, we can walk the rctl_set for only the
563N/A * entity specified by the handle.
563N/A */
563N/A if (rdict.hndl != 0 && rdict.type != i)
563N/A continue;
563N/A
switch (i) {
case (RCENTITY_PROCESS):
set = (uintptr_t)proc.p_rctls;
break;
case (RCENTITY_TASK):
set = (uintptr_t)task.tk_rctls;
break;
case (RCENTITY_PROJECT):
set = (uintptr_t)proj.kpj_rctls;
break;
case (RCENTITY_ZONE):
set = (uintptr_t)zone.zone_rctls;
break;
default:
mdb_warn("Unknown rctl type %d", i);
return (DCMD_ERR);
}
if (mdb_pwalk_dcmd("rctl_set", "rctl", argc, argv, set) == -1) {
mdb_warn("failed to walk rctls in set %p", set);
return (DCMD_ERR);
}
}
return (DCMD_OK);
}
typedef struct dict_walk_data {
int num_dicts;
int num_cur;
rctl_dict_entry_t **curdict;
} dict_walk_data_t;
int
rctl_dict_walk_init(mdb_walk_state_t *wsp)
{
uintptr_t ptr;
int nlists;
GElf_Sym sym;
rctl_dict_entry_t **dicts;
dict_walk_data_t *dwd;
if (mdb_lookup_by_name("rctl_lists", &sym) == -1) {
mdb_warn("failed to find 'rctl_lists'\n");
return (WALK_ERR);
}
nlists = sym.st_size / sizeof (rctl_dict_entry_t *);
ptr = (uintptr_t)sym.st_value;
dicts = mdb_alloc(nlists * sizeof (rctl_dict_entry_t *), UM_SLEEP);
mdb_vread(dicts, sym.st_size, ptr);
dwd = mdb_alloc(sizeof (dict_walk_data_t), UM_SLEEP);
dwd->num_dicts = nlists;
dwd->num_cur = 0;
dwd->curdict = dicts;
wsp->walk_addr = 0;
wsp->walk_data = dwd;
return (WALK_NEXT);
}
int
rctl_dict_walk_step(mdb_walk_state_t *wsp)
{
dict_walk_data_t *dwd = wsp->walk_data;
uintptr_t dp;
rctl_dict_entry_t entry;
int status;
dp = (uintptr_t)((dwd->curdict)[dwd->num_cur]);
while (dp != NULL) {
if (mdb_vread(&entry, sizeof (rctl_dict_entry_t), dp) == -1) {
mdb_warn("failed to read rctl_dict_entry_t structure "
"at %p", dp);
return (WALK_ERR);
}
status = wsp->walk_callback(dp, &entry, wsp->walk_cbdata);
if (status != WALK_NEXT)
return (status);
dp = (uintptr_t)entry.rcd_next;
}
dwd->num_cur++;
if (dwd->num_cur == dwd->num_dicts)
return (WALK_DONE);
return (WALK_NEXT);
}
void
rctl_dict_walk_fini(mdb_walk_state_t *wsp)
{
dict_walk_data_t *wd = wsp->walk_data;
mdb_free(wd->curdict, wd->num_dicts * sizeof (rctl_dict_entry_t *));
mdb_free(wd, sizeof (dict_walk_data_t));
}
typedef struct set_walk_data {
uint_t hashsize;
int hashcur;
void **hashloc;
} set_walk_data_t;
int
rctl_set_walk_init(mdb_walk_state_t *wsp)
{
rctl_set_t rset;
uint_t hashsz;
set_walk_data_t *swd;
rctl_t **rctls;
if (mdb_vread(&rset, sizeof (rctl_set_t), wsp->walk_addr) == -1) {
mdb_warn("failed to read rset at %p", wsp->walk_addr);
return (WALK_ERR);
}
if (mdb_readvar(&hashsz, "rctl_set_size") == -1 || hashsz == 0) {
mdb_warn("rctl_set_size not found or invalid");
return (WALK_ERR);
}
rctls = mdb_alloc(hashsz * sizeof (rctl_t *), UM_SLEEP);
if (mdb_vread(rctls, hashsz * sizeof (rctl_t *),
(uintptr_t)rset.rcs_ctls) == -1) {
mdb_warn("cannot read rctl hash at %p", rset.rcs_ctls);
mdb_free(rctls, hashsz * sizeof (rctl_t *));
return (WALK_ERR);
}
swd = mdb_alloc(sizeof (set_walk_data_t), UM_SLEEP);
swd->hashsize = hashsz;
swd->hashcur = 0;
swd->hashloc = (void **)rctls;
wsp->walk_addr = 0;
wsp->walk_data = swd;
return (WALK_NEXT);
}
int
rctl_set_walk_step(mdb_walk_state_t *wsp)
{
set_walk_data_t *swd = wsp->walk_data;
rctl_t rctl;
void **rhash = swd->hashloc;
int status;
if (swd->hashcur >= swd->hashsize)
return (WALK_DONE);
if (wsp->walk_addr == NULL) {
while (swd->hashcur < swd->hashsize) {
if (rhash[swd->hashcur] != NULL) {
break;
}
swd->hashcur++;
}
if (rhash[swd->hashcur] == NULL ||
swd->hashcur >= swd->hashsize)
return (WALK_DONE);
wsp->walk_addr = (uintptr_t)rhash[swd->hashcur];
swd->hashcur++;
}
if (mdb_vread(&rctl, sizeof (rctl_t), wsp->walk_addr) == -1) {
wsp->walk_addr = NULL;
mdb_warn("unable to read from %#p", wsp->walk_addr);
return (WALK_ERR);
}
status = wsp->walk_callback(wsp->walk_addr, &rctl, wsp->walk_cbdata);
wsp->walk_addr = (uintptr_t)rctl.rc_next;
return (status);
}
void
rctl_set_walk_fini(mdb_walk_state_t *wsp)
{
set_walk_data_t *sd = wsp->walk_data;
mdb_free(sd->hashloc, sd->hashsize * sizeof (rctl_t *));
mdb_free(sd, sizeof (set_walk_data_t));
}
int
rctl_val_walk_init(mdb_walk_state_t *wsp)
{
rctl_t rctl;
if (mdb_vread(&rctl, sizeof (rctl_t), wsp->walk_addr) == -1) {
mdb_warn("failed to read rctl at %p", wsp->walk_addr);
return (WALK_ERR);
}
wsp->walk_addr = (uintptr_t)rctl.rc_values;
wsp->walk_data = rctl.rc_values;
return (WALK_NEXT);
}
int
rctl_val_walk_step(mdb_walk_state_t *wsp)
{
rctl_val_t val;
int status;
if (mdb_vread(&val, sizeof (rctl_val_t), wsp->walk_addr) == -1) {
mdb_warn("failed to read rctl_val at %p", wsp->walk_addr);
return (WALK_DONE);
}
status = wsp->walk_callback(wsp->walk_addr, &val, wsp->walk_cbdata);
if ((wsp->walk_addr = (uintptr_t)val.rcv_next) == NULL)
return (WALK_DONE);
return (status);
}
typedef struct rctl_val_seen {
uintptr_t s_ptr;
rctl_qty_t s_val;
} rctl_val_seen_t;
typedef struct rctl_validate_data {
uintptr_t v_rctl_addr;
rctl_val_t *v_cursor;
uint_t v_flags;
int v_bad_rctl;
int v_cursor_valid;
int v_circularity_detected;
uint_t v_seen_size;
uint_t v_seen_cnt;
rctl_val_seen_t *v_seen;
} rctl_validate_data_t;
#define RCV_VERBOSE 0x1
/*
* rctl_val_validate()
* Do validation on an individual rctl_val_t. This function is called
* as part of the rctl_val walker, and helps perform the checks described
* in the ::rctl_validate dcmd.
*/
static int
rctl_val_validate(uintptr_t addr, rctl_val_t *val, rctl_validate_data_t *data)
{
int i;
data->v_seen[data->v_seen_cnt].s_ptr = addr;
if (addr == (uintptr_t)data->v_cursor)
data->v_cursor_valid++;
data->v_seen[data->v_seen_cnt].s_val = val->rcv_value;
if (val->rcv_prev == (void *)0xbaddcafe ||
val->rcv_next == (void *)0xbaddcafe ||
val->rcv_prev == (void *)0xdeadbeef ||
val->rcv_next == (void *)0xdeadbeef) {
if (data->v_bad_rctl++ == 0)
mdb_printf("%p ", data->v_rctl_addr);
if (data->v_flags & RCV_VERBOSE)
mdb_printf("/ uninitialized or previously "
"freed link at %p ", addr);
}
if (data->v_seen_cnt == 0) {
if (val->rcv_prev != NULL) {
if (data->v_bad_rctl++ == 0)
mdb_printf("%p ", data->v_rctl_addr);
if (data->v_flags & RCV_VERBOSE)
mdb_printf("/ bad prev pointer at "
"head ");
}
} else {
if ((uintptr_t)val->rcv_prev !=
data->v_seen[data->v_seen_cnt - 1].s_ptr) {
if (data->v_bad_rctl++ == 0)
mdb_printf("%p ", data->v_rctl_addr);
if (data->v_flags & RCV_VERBOSE)
mdb_printf("/ bad prev pointer at %p ",
addr);
}
if (data->v_seen[data->v_seen_cnt].s_val <
data->v_seen[data->v_seen_cnt - 1].s_val) {
if (data->v_bad_rctl++ == 0)
mdb_printf("%p ", data->v_rctl_addr);
if (data->v_flags & RCV_VERBOSE)
mdb_printf("/ ordering error at %p ",
addr);
}
}
for (i = data->v_seen_cnt; i >= 0; i--) {
if (data->v_seen[i].s_ptr == (uintptr_t)val->rcv_next) {
if (data->v_bad_rctl++ == 0)
mdb_printf("%p ", data->v_rctl_addr);
if (data->v_flags & RCV_VERBOSE)
mdb_printf("/ circular next pointer "
"at %p ", addr);
data->v_circularity_detected++;
break;
}
}
if (data->v_circularity_detected)
return (WALK_DONE);
data->v_seen_cnt++;
if (data->v_seen_cnt >= data->v_seen_size) {
uint_t new_seen_size = data->v_seen_size * 2;
rctl_val_seen_t *tseen = mdb_zalloc(new_seen_size *
sizeof (rctl_val_seen_t), UM_SLEEP | UM_GC);
bcopy(data->v_seen, tseen, data->v_seen_size *
sizeof (rctl_val_seen_t));
data->v_seen = tseen;
data->v_seen_size = new_seen_size;
}
return (WALK_NEXT);
}
/*
* Validate a rctl pointer by checking:
* - rctl_val_t's for that rctl form an ordered, non-circular list
* - the cursor points to a rctl_val_t within that list
* - there are no more than UINT64_MAX (or # specified by -n)
* rctl_val_t's in the list
*/
int
rctl_validate(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
{
rctl_validate_data_t data;
rctl_t r;
uint64_t long_threshold = UINT64_MAX;
/* Initialize validate data structure */
data.v_rctl_addr = addr;
data.v_flags = 0;
data.v_bad_rctl = 0;
data.v_seen_cnt = 0;
data.v_cursor_valid = 0;
data.v_circularity_detected = 0;
data.v_seen_size = 1;
data.v_seen = mdb_zalloc(data.v_seen_size * sizeof (rctl_val_seen_t),
UM_SLEEP | UM_GC);
if (!(flags & DCMD_ADDRSPEC))
return (DCMD_USAGE);
if (mdb_getopts(argc, argv,
'v', MDB_OPT_SETBITS, RCV_VERBOSE, &data.v_flags,
'n', MDB_OPT_UINT64, &long_threshold,
NULL) != argc)
return (DCMD_USAGE);
if (mdb_vread(&r, sizeof (rctl_t), addr) != sizeof (rctl_t)) {
mdb_warn("failed to read rctl structure at %p", addr);
return (DCMD_ERR);
}
data.v_cursor = r.rc_cursor;
if (data.v_cursor == NULL) {
if (data.v_bad_rctl++ == 0)
mdb_printf("%p ", addr);
if (data.v_flags & RCV_VERBOSE)
mdb_printf("/ NULL cursor seen ");
} else if (data.v_cursor == (rctl_val_t *)0xbaddcafe) {
if (data.v_bad_rctl++ == 0)
mdb_printf("%p ", addr);
if (data.v_flags & RCV_VERBOSE)
mdb_printf("/ uninitialized cursor seen ");
}
/* Walk through each val in this rctl for individual validation. */
if (mdb_pwalk("rctl_val", (mdb_walk_cb_t)rctl_val_validate, &data,
addr) == -1) {
mdb_warn("failed to walk all values for rctl_t %p", addr);
return (DCMD_ERR);
}
if (data.v_seen_cnt >= long_threshold) {
if (data.v_bad_rctl++ == 0)
mdb_printf("%p ", addr);
if (data.v_flags & RCV_VERBOSE)
mdb_printf("/ sequence length = %d ",
data.v_seen_cnt);
}
if (!data.v_cursor_valid) {
if (data.v_bad_rctl++ == 0)
mdb_printf("%p ", addr);
if (data.v_flags & RCV_VERBOSE)
mdb_printf("/ cursor outside sequence");
}
if (data.v_bad_rctl)
mdb_printf("\n");
if (data.v_circularity_detected)
mdb_warn("circular list implies possible memory leak; "
"recommend invoking ::findleaks");
return (DCMD_OK);
}