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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include <mdb/mdb_modapi.h> 30 #include <sys/rctl.h> 31 #include <sys/proc.h> 32 #include <sys/task.h> 33 #include <sys/project.h> 34 #include <sys/zone.h> 35 36 static int 37 print_val(uintptr_t addr, rctl_val_t *val, uintptr_t *enforced) 38 { 39 char *priv; 40 static const mdb_bitmask_t val_localflag_bits[] = { 41 { "SIGNAL", RCTL_LOCAL_SIGNAL, RCTL_LOCAL_SIGNAL }, 42 { "DENY", RCTL_LOCAL_DENY, RCTL_LOCAL_DENY }, 43 { "MAX", RCTL_LOCAL_MAXIMAL, RCTL_LOCAL_MAXIMAL }, 44 { NULL, 0, 0 } 45 }; 46 47 switch (val->rcv_privilege) { 48 case (RCPRIV_BASIC): 49 priv = "basic"; 50 break; 51 case (RCPRIV_PRIVILEGED): 52 priv = "privileged"; 53 break; 54 case (RCPRIV_SYSTEM): 55 priv = "system"; 56 break; 57 default: 58 priv = "???"; 59 break; 60 }; 61 62 mdb_printf("\t%s ", addr == *enforced ? "(cur)": " "); 63 64 mdb_printf("%-#18llx %11s\tflags=<%b>\n", 65 val->rcv_value, priv, val->rcv_flagaction, val_localflag_bits); 66 67 return (WALK_NEXT); 68 } 69 70 int 71 rctl(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 72 { 73 rctl_t rctl; 74 rctl_dict_entry_t dict; 75 char name[256]; 76 rctl_hndl_t hndl; 77 78 if (!(flags & DCMD_ADDRSPEC)) 79 return (DCMD_USAGE); 80 81 if (mdb_vread(&rctl, sizeof (rctl_t), addr) == -1) { 82 mdb_warn("failed to read rctl_t structure at %p", addr); 83 return (DCMD_ERR); 84 } 85 86 if (argc != 0) { 87 const mdb_arg_t *argp = &argv[0]; 88 89 if (argp->a_type == MDB_TYPE_IMMEDIATE) 90 hndl = (rctl_hndl_t)argp->a_un.a_val; 91 else 92 hndl = (rctl_hndl_t)mdb_strtoull(argp->a_un.a_str); 93 94 if (rctl.rc_id != hndl) 95 return (DCMD_OK); 96 } 97 98 if (mdb_vread(&dict, sizeof (rctl_dict_entry_t), 99 (uintptr_t)rctl.rc_dict_entry) == -1) { 100 mdb_warn("failed to read dict entry for rctl_t %p at %p", 101 addr, rctl.rc_dict_entry); 102 return (DCMD_ERR); 103 } 104 105 if (mdb_readstr(name, 256, (uintptr_t)(dict.rcd_name)) == -1) { 106 mdb_warn("failed to read name for rctl_t %p", addr); 107 return (DCMD_ERR); 108 } 109 110 mdb_printf("%0?p\t%3d : %s\n", addr, rctl.rc_id, name); 111 112 if (mdb_pwalk("rctl_val", (mdb_walk_cb_t)print_val, &(rctl.rc_cursor), 113 addr) == -1) { 114 mdb_warn("failed to walk all values for rctl_t %p", addr); 115 return (DCMD_ERR); 116 } 117 118 return (DCMD_OK); 119 } 120 121 int 122 rctl_dict(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 123 { 124 rctl_dict_entry_t dict; 125 char name[256], *type = NULL; 126 127 if (!(flags & DCMD_ADDRSPEC)) { 128 if (mdb_walk_dcmd("rctl_dict_list", "rctl_dict", argc, 129 argv) == -1) { 130 mdb_warn("failed to walk 'rctl_dict_list'"); 131 return (DCMD_ERR); 132 } 133 return (DCMD_OK); 134 } 135 136 if (DCMD_HDRSPEC(flags)) 137 mdb_printf("%<u>%2s %-27s %?s %7s %s%</u>\n", 138 "ID", "NAME", "ADDR", "TYPE", "GLOBAL_FLAGS"); 139 140 if (mdb_vread(&dict, sizeof (dict), addr) == -1) { 141 mdb_warn("failed to read rctl_dict at %p", addr); 142 return (DCMD_ERR); 143 } 144 if (mdb_readstr(name, 256, (uintptr_t)(dict.rcd_name)) == -1) { 145 mdb_warn("failed to read rctl_dict name for %p", addr); 146 return (DCMD_ERR); 147 } 148 149 switch (dict.rcd_entity) { 150 case RCENTITY_PROCESS: 151 type = "process"; 152 break; 153 case RCENTITY_TASK: 154 type = "task"; 155 break; 156 case RCENTITY_PROJECT: 157 type = "project"; 158 break; 159 case RCENTITY_ZONE: 160 type = "zone"; 161 break; 162 default: 163 type = "unknown"; 164 break; 165 } 166 167 mdb_printf("%2d %-27s %0?p %7s 0x%08x", dict.rcd_id, name, addr, 168 type, dict.rcd_flagaction); 169 170 return (DCMD_OK); 171 } 172 173 typedef struct dict_data { 174 rctl_hndl_t hndl; 175 uintptr_t dict_addr; 176 rctl_entity_t type; 177 } dict_data_t; 178 179 static int 180 hndl2dict(uintptr_t addr, rctl_dict_entry_t *entry, dict_data_t *data) 181 { 182 if (data->hndl == entry->rcd_id) { 183 data->dict_addr = addr; 184 data->type = entry->rcd_entity; 185 return (WALK_DONE); 186 } 187 188 return (WALK_NEXT); 189 } 190 191 /* 192 * Print out all project, task, and process rctls for a given process. 193 * If a handle is specified, print only the rctl matching that handle 194 * for the process. 195 */ 196 int 197 rctl_list(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 198 { 199 proc_t proc; 200 uintptr_t set; 201 task_t task; 202 kproject_t proj; 203 zone_t zone; 204 dict_data_t rdict; 205 int i; 206 207 rdict.dict_addr = NULL; 208 209 if (!(flags & DCMD_ADDRSPEC)) 210 return (DCMD_USAGE); 211 212 if (argc == 0) 213 rdict.hndl = 0; 214 else if (argc == 1) { 215 /* 216 * User specified a handle. Go find the rctl_dict_entity_t 217 * structure so we know what type of rctl to look for. 218 */ 219 const mdb_arg_t *argp = &argv[0]; 220 221 if (argp->a_type == MDB_TYPE_IMMEDIATE) 222 rdict.hndl = (rctl_hndl_t)argp->a_un.a_val; 223 else 224 rdict.hndl = 225 (rctl_hndl_t)mdb_strtoull(argp->a_un.a_str); 226 227 if (mdb_walk("rctl_dict_list", (mdb_walk_cb_t)hndl2dict, 228 &rdict) == -1) { 229 mdb_warn("failed to walk rctl_dict_list"); 230 return (DCMD_ERR); 231 } 232 /* Couldn't find a rctl_dict_entry_t for this handle */ 233 if (rdict.dict_addr == NULL) 234 return (DCMD_ERR); 235 } else 236 return (DCMD_USAGE); 237 238 239 if (mdb_vread(&proc, sizeof (proc_t), addr) == -1) { 240 mdb_warn("failed to read proc at %p", addr); 241 return (DCMD_ERR); 242 } 243 if (mdb_vread(&zone, sizeof (zone_t), (uintptr_t)proc.p_zone) == -1) { 244 mdb_warn("failed to read zone at %p", proc.p_zone); 245 return (DCMD_ERR); 246 } 247 if (mdb_vread(&task, sizeof (task_t), (uintptr_t)proc.p_task) == -1) { 248 mdb_warn("failed to read task at %p", proc.p_task); 249 return (DCMD_ERR); 250 } 251 if (mdb_vread(&proj, sizeof (kproject_t), 252 (uintptr_t)task.tk_proj) == -1) { 253 mdb_warn("failed to read proj at %p", task.tk_proj); 254 return (DCMD_ERR); 255 } 256 257 for (i = 0; i <= RC_MAX_ENTITY; i++) { 258 /* 259 * If user didn't specify a handle, print rctls for all 260 * types. Otherwise, we can walk the rctl_set for only the 261 * entity specified by the handle. 262 */ 263 if (rdict.hndl != 0 && rdict.type != i) 264 continue; 265 266 switch (i) { 267 case (RCENTITY_PROCESS): 268 set = (uintptr_t)proc.p_rctls; 269 break; 270 case (RCENTITY_TASK): 271 set = (uintptr_t)task.tk_rctls; 272 break; 273 case (RCENTITY_PROJECT): 274 set = (uintptr_t)proj.kpj_rctls; 275 break; 276 case (RCENTITY_ZONE): 277 set = (uintptr_t)zone.zone_rctls; 278 break; 279 default: 280 mdb_warn("Unknown rctl type %d", i); 281 return (DCMD_ERR); 282 } 283 284 if (mdb_pwalk_dcmd("rctl_set", "rctl", argc, argv, set) == -1) { 285 mdb_warn("failed to walk rctls in set %p", set); 286 return (DCMD_ERR); 287 } 288 } 289 290 return (DCMD_OK); 291 } 292 293 typedef struct dict_walk_data { 294 int num_dicts; 295 int num_cur; 296 rctl_dict_entry_t **curdict; 297 } dict_walk_data_t; 298 299 int 300 rctl_dict_walk_init(mdb_walk_state_t *wsp) 301 { 302 uintptr_t ptr; 303 int nlists; 304 GElf_Sym sym; 305 rctl_dict_entry_t **dicts; 306 dict_walk_data_t *dwd; 307 308 if (mdb_lookup_by_name("rctl_lists", &sym) == -1) { 309 mdb_warn("failed to find 'rctl_lists'\n"); 310 return (WALK_ERR); 311 } 312 313 nlists = sym.st_size / sizeof (rctl_dict_entry_t *); 314 ptr = (uintptr_t)sym.st_value; 315 316 dicts = mdb_alloc(nlists * sizeof (rctl_dict_entry_t *), UM_SLEEP); 317 mdb_vread(dicts, sym.st_size, ptr); 318 319 dwd = mdb_alloc(sizeof (dict_walk_data_t), UM_SLEEP); 320 dwd->num_dicts = nlists; 321 dwd->num_cur = 0; 322 dwd->curdict = dicts; 323 324 wsp->walk_addr = 0; 325 wsp->walk_data = dwd; 326 327 return (WALK_NEXT); 328 } 329 330 int 331 rctl_dict_walk_step(mdb_walk_state_t *wsp) 332 { 333 dict_walk_data_t *dwd = wsp->walk_data; 334 uintptr_t dp; 335 rctl_dict_entry_t entry; 336 int status; 337 338 dp = (uintptr_t)((dwd->curdict)[dwd->num_cur]); 339 340 while (dp != NULL) { 341 if (mdb_vread(&entry, sizeof (rctl_dict_entry_t), dp) == -1) { 342 mdb_warn("failed to read rctl_dict_entry_t structure " 343 "at %p", dp); 344 return (WALK_ERR); 345 } 346 347 status = wsp->walk_callback(dp, &entry, wsp->walk_cbdata); 348 if (status != WALK_NEXT) 349 return (status); 350 351 dp = (uintptr_t)entry.rcd_next; 352 } 353 354 dwd->num_cur++; 355 356 if (dwd->num_cur == dwd->num_dicts) 357 return (WALK_DONE); 358 359 return (WALK_NEXT); 360 } 361 362 void 363 rctl_dict_walk_fini(mdb_walk_state_t *wsp) 364 { 365 dict_walk_data_t *wd = wsp->walk_data; 366 mdb_free(wd->curdict, wd->num_dicts * sizeof (rctl_dict_entry_t *)); 367 mdb_free(wd, sizeof (dict_walk_data_t)); 368 } 369 370 typedef struct set_walk_data { 371 uint_t hashsize; 372 int hashcur; 373 void **hashloc; 374 } set_walk_data_t; 375 376 int 377 rctl_set_walk_init(mdb_walk_state_t *wsp) 378 { 379 rctl_set_t rset; 380 uint_t hashsz; 381 set_walk_data_t *swd; 382 rctl_t **rctls; 383 384 if (mdb_vread(&rset, sizeof (rctl_set_t), wsp->walk_addr) == -1) { 385 mdb_warn("failed to read rset at %p", wsp->walk_addr); 386 return (WALK_ERR); 387 } 388 389 if (mdb_readvar(&hashsz, "rctl_set_size") == -1 || hashsz == 0) { 390 mdb_warn("rctl_set_size not found or invalid"); 391 return (WALK_ERR); 392 } 393 394 rctls = mdb_alloc(hashsz * sizeof (rctl_t *), UM_SLEEP); 395 if (mdb_vread(rctls, hashsz * sizeof (rctl_t *), 396 (uintptr_t)rset.rcs_ctls) == -1) { 397 mdb_warn("cannot read rctl hash at %p", rset.rcs_ctls); 398 mdb_free(rctls, hashsz * sizeof (rctl_t *)); 399 return (WALK_ERR); 400 } 401 402 swd = mdb_alloc(sizeof (set_walk_data_t), UM_SLEEP); 403 swd->hashsize = hashsz; 404 swd->hashcur = 0; 405 swd->hashloc = (void **)rctls; 406 407 wsp->walk_addr = 0; 408 wsp->walk_data = swd; 409 410 return (WALK_NEXT); 411 } 412 413 414 int 415 rctl_set_walk_step(mdb_walk_state_t *wsp) 416 { 417 set_walk_data_t *swd = wsp->walk_data; 418 rctl_t rctl; 419 void **rhash = swd->hashloc; 420 int status; 421 422 if (swd->hashcur >= swd->hashsize) 423 return (WALK_DONE); 424 425 if (wsp->walk_addr == NULL) { 426 while (swd->hashcur < swd->hashsize) { 427 if (rhash[swd->hashcur] != NULL) { 428 break; 429 } 430 swd->hashcur++; 431 } 432 433 if (rhash[swd->hashcur] == NULL || 434 swd->hashcur >= swd->hashsize) 435 return (WALK_DONE); 436 437 wsp->walk_addr = (uintptr_t)rhash[swd->hashcur]; 438 swd->hashcur++; 439 } 440 441 if (mdb_vread(&rctl, sizeof (rctl_t), wsp->walk_addr) == -1) { 442 wsp->walk_addr = NULL; 443 mdb_warn("unable to read from %#p", wsp->walk_addr); 444 return (WALK_ERR); 445 } 446 447 status = wsp->walk_callback(wsp->walk_addr, &rctl, wsp->walk_cbdata); 448 449 wsp->walk_addr = (uintptr_t)rctl.rc_next; 450 451 return (status); 452 } 453 454 void 455 rctl_set_walk_fini(mdb_walk_state_t *wsp) 456 { 457 set_walk_data_t *sd = wsp->walk_data; 458 459 mdb_free(sd->hashloc, sd->hashsize * sizeof (rctl_t *)); 460 mdb_free(sd, sizeof (set_walk_data_t)); 461 } 462 463 int 464 rctl_val_walk_init(mdb_walk_state_t *wsp) 465 { 466 rctl_t rctl; 467 468 if (mdb_vread(&rctl, sizeof (rctl_t), wsp->walk_addr) == -1) { 469 mdb_warn("failed to read rctl at %p", wsp->walk_addr); 470 return (WALK_ERR); 471 } 472 wsp->walk_addr = (uintptr_t)rctl.rc_values; 473 wsp->walk_data = rctl.rc_values; 474 return (WALK_NEXT); 475 } 476 477 int 478 rctl_val_walk_step(mdb_walk_state_t *wsp) 479 { 480 rctl_val_t val; 481 int status; 482 483 if (mdb_vread(&val, sizeof (rctl_val_t), wsp->walk_addr) == -1) { 484 mdb_warn("failed to read rctl_val at %p", wsp->walk_addr); 485 return (WALK_DONE); 486 } 487 488 status = wsp->walk_callback(wsp->walk_addr, &val, wsp->walk_cbdata); 489 490 if ((wsp->walk_addr = (uintptr_t)val.rcv_next) == NULL) 491 return (WALK_DONE); 492 493 return (status); 494 } 495 496 typedef struct rctl_val_seen { 497 uintptr_t s_ptr; 498 rctl_qty_t s_val; 499 } rctl_val_seen_t; 500 501 typedef struct rctl_validate_data { 502 uintptr_t v_rctl_addr; 503 rctl_val_t *v_cursor; 504 uint_t v_flags; 505 int v_bad_rctl; 506 int v_cursor_valid; 507 int v_circularity_detected; 508 uint_t v_seen_size; 509 uint_t v_seen_cnt; 510 rctl_val_seen_t *v_seen; 511 } rctl_validate_data_t; 512 513 #define RCV_VERBOSE 0x1 514 515 /* 516 * rctl_val_validate() 517 * Do validation on an individual rctl_val_t. This function is called 518 * as part of the rctl_val walker, and helps perform the checks described 519 * in the ::rctl_validate dcmd. 520 */ 521 static int 522 rctl_val_validate(uintptr_t addr, rctl_val_t *val, rctl_validate_data_t *data) 523 { 524 int i; 525 526 data->v_seen[data->v_seen_cnt].s_ptr = addr; 527 528 if (addr == (uintptr_t)data->v_cursor) 529 data->v_cursor_valid++; 530 531 data->v_seen[data->v_seen_cnt].s_val = val->rcv_value; 532 533 if (val->rcv_prev == (void *)0xbaddcafe || 534 val->rcv_next == (void *)0xbaddcafe || 535 val->rcv_prev == (void *)0xdeadbeef || 536 val->rcv_next == (void *)0xdeadbeef) { 537 if (data->v_bad_rctl++ == 0) 538 mdb_printf("%p ", data->v_rctl_addr); 539 if (data->v_flags & RCV_VERBOSE) 540 mdb_printf("/ uninitialized or previously " 541 "freed link at %p ", addr); 542 } 543 544 if (data->v_seen_cnt == 0) { 545 if (val->rcv_prev != NULL) { 546 if (data->v_bad_rctl++ == 0) 547 mdb_printf("%p ", data->v_rctl_addr); 548 if (data->v_flags & RCV_VERBOSE) 549 mdb_printf("/ bad prev pointer at " 550 "head "); 551 } 552 } else { 553 if ((uintptr_t)val->rcv_prev != 554 data->v_seen[data->v_seen_cnt - 1].s_ptr) { 555 if (data->v_bad_rctl++ == 0) 556 mdb_printf("%p ", data->v_rctl_addr); 557 if (data->v_flags & RCV_VERBOSE) 558 mdb_printf("/ bad prev pointer at %p ", 559 addr); 560 } 561 562 if (data->v_seen[data->v_seen_cnt].s_val < 563 data->v_seen[data->v_seen_cnt - 1].s_val) { 564 if (data->v_bad_rctl++ == 0) 565 mdb_printf("%p ", data->v_rctl_addr); 566 if (data->v_flags & RCV_VERBOSE) 567 mdb_printf("/ ordering error at %p ", 568 addr); 569 } 570 } 571 572 for (i = data->v_seen_cnt; i >= 0; i--) { 573 if (data->v_seen[i].s_ptr == (uintptr_t)val->rcv_next) { 574 if (data->v_bad_rctl++ == 0) 575 mdb_printf("%p ", data->v_rctl_addr); 576 if (data->v_flags & RCV_VERBOSE) 577 mdb_printf("/ circular next pointer " 578 "at %p ", addr); 579 data->v_circularity_detected++; 580 break; 581 } 582 } 583 584 if (data->v_circularity_detected) 585 return (WALK_DONE); 586 587 data->v_seen_cnt++; 588 if (data->v_seen_cnt >= data->v_seen_size) { 589 uint_t new_seen_size = data->v_seen_size * 2; 590 rctl_val_seen_t *tseen = mdb_zalloc(new_seen_size * 591 sizeof (rctl_val_seen_t), UM_SLEEP | UM_GC); 592 593 bcopy(data->v_seen, tseen, data->v_seen_size * 594 sizeof (rctl_val_seen_t)); 595 596 data->v_seen = tseen; 597 data->v_seen_size = new_seen_size; 598 } 599 600 return (WALK_NEXT); 601 } 602 603 /* 604 * Validate a rctl pointer by checking: 605 * - rctl_val_t's for that rctl form an ordered, non-circular list 606 * - the cursor points to a rctl_val_t within that list 607 * - there are no more than UINT64_MAX (or # specified by -n) 608 * rctl_val_t's in the list 609 */ 610 int 611 rctl_validate(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv) 612 { 613 rctl_validate_data_t data; 614 615 rctl_t r; 616 617 uint64_t long_threshold = UINT64_MAX; 618 619 /* Initialize validate data structure */ 620 data.v_rctl_addr = addr; 621 data.v_flags = 0; 622 data.v_bad_rctl = 0; 623 data.v_seen_cnt = 0; 624 data.v_cursor_valid = 0; 625 data.v_circularity_detected = 0; 626 data.v_seen_size = 1; 627 data.v_seen = mdb_zalloc(data.v_seen_size * sizeof (rctl_val_seen_t), 628 UM_SLEEP | UM_GC); 629 630 if (!(flags & DCMD_ADDRSPEC)) 631 return (DCMD_USAGE); 632 633 if (mdb_getopts(argc, argv, 634 'v', MDB_OPT_SETBITS, RCV_VERBOSE, &data.v_flags, 635 'n', MDB_OPT_UINT64, &long_threshold, 636 NULL) != argc) 637 return (DCMD_USAGE); 638 639 if (mdb_vread(&r, sizeof (rctl_t), addr) != sizeof (rctl_t)) { 640 mdb_warn("failed to read rctl structure at %p", addr); 641 return (DCMD_ERR); 642 } 643 644 data.v_cursor = r.rc_cursor; 645 646 if (data.v_cursor == NULL) { 647 if (data.v_bad_rctl++ == 0) 648 mdb_printf("%p ", addr); 649 if (data.v_flags & RCV_VERBOSE) 650 mdb_printf("/ NULL cursor seen "); 651 } else if (data.v_cursor == (rctl_val_t *)0xbaddcafe) { 652 if (data.v_bad_rctl++ == 0) 653 mdb_printf("%p ", addr); 654 if (data.v_flags & RCV_VERBOSE) 655 mdb_printf("/ uninitialized cursor seen "); 656 } 657 658 /* Walk through each val in this rctl for individual validation. */ 659 if (mdb_pwalk("rctl_val", (mdb_walk_cb_t)rctl_val_validate, &data, 660 addr) == -1) { 661 mdb_warn("failed to walk all values for rctl_t %p", addr); 662 return (DCMD_ERR); 663 } 664 665 if (data.v_seen_cnt >= long_threshold) { 666 if (data.v_bad_rctl++ == 0) 667 mdb_printf("%p ", addr); 668 if (data.v_flags & RCV_VERBOSE) 669 mdb_printf("/ sequence length = %d ", 670 data.v_seen_cnt); 671 } 672 673 if (!data.v_cursor_valid) { 674 if (data.v_bad_rctl++ == 0) 675 mdb_printf("%p ", addr); 676 if (data.v_flags & RCV_VERBOSE) 677 mdb_printf("/ cursor outside sequence"); 678 } 679 680 if (data.v_bad_rctl) 681 mdb_printf("\n"); 682 683 if (data.v_circularity_detected) 684 mdb_warn("circular list implies possible memory leak; " 685 "recommend invoking ::findleaks"); 686 687 return (DCMD_OK); 688 } 689