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