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 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 #pragma ident "%Z%%M% %I% %E% SMI" 27 28 #include <mdb/mdb_modapi.h> 29 #include <mdb/mdb_module.h> 30 #include <mdb/mdb_string.h> 31 #include <mdb/mdb_debug.h> 32 #include <mdb/mdb_callb.h> 33 #include <mdb/mdb_dump.h> 34 #include <mdb/mdb_err.h> 35 #include <mdb/mdb_io.h> 36 #include <mdb/mdb_lex.h> 37 #include <mdb/mdb_frame.h> 38 #include <mdb/mdb.h> 39 #ifndef _KMDB 40 #include <math.h> 41 #endif 42 43 /* 44 * Private callback structure for implementing mdb_walk_dcmd, below. 45 */ 46 typedef struct { 47 mdb_idcmd_t *dw_dcmd; 48 mdb_argvec_t dw_argv; 49 uint_t dw_flags; 50 } dcmd_walk_arg_t; 51 52 /* 53 * Global properties which modules are allowed to look at. These are 54 * re-initialized by the target activation callbacks. 55 */ 56 int mdb_prop_postmortem = FALSE; /* Are we examining a dump? */ 57 int mdb_prop_kernel = FALSE; /* Are we examining a kernel? */ 58 int mdb_prop_datamodel = 0; /* Data model (see mdb_target_impl.h) */ 59 60 ssize_t 61 mdb_vread(void *buf, size_t nbytes, uintptr_t addr) 62 { 63 ssize_t rbytes = mdb_tgt_vread(mdb.m_target, buf, nbytes, addr); 64 65 if (rbytes > 0 && rbytes < nbytes) 66 return (set_errbytes(rbytes, nbytes)); 67 68 return (rbytes); 69 } 70 71 ssize_t 72 mdb_vwrite(const void *buf, size_t nbytes, uintptr_t addr) 73 { 74 return (mdb_tgt_vwrite(mdb.m_target, buf, nbytes, addr)); 75 } 76 77 ssize_t 78 mdb_fread(void *buf, size_t nbytes, uintptr_t addr) 79 { 80 ssize_t rbytes = mdb_tgt_fread(mdb.m_target, buf, nbytes, addr); 81 82 if (rbytes > 0 && rbytes < nbytes) 83 return (set_errbytes(rbytes, nbytes)); 84 85 return (rbytes); 86 } 87 88 ssize_t 89 mdb_fwrite(const void *buf, size_t nbytes, uintptr_t addr) 90 { 91 return (mdb_tgt_fwrite(mdb.m_target, buf, nbytes, addr)); 92 } 93 94 ssize_t 95 mdb_pread(void *buf, size_t nbytes, physaddr_t addr) 96 { 97 ssize_t rbytes = mdb_tgt_pread(mdb.m_target, buf, nbytes, addr); 98 99 if (rbytes > 0 && rbytes < nbytes) 100 return (set_errbytes(rbytes, nbytes)); 101 102 return (rbytes); 103 } 104 105 ssize_t 106 mdb_pwrite(const void *buf, size_t nbytes, physaddr_t addr) 107 { 108 return (mdb_tgt_pwrite(mdb.m_target, buf, nbytes, addr)); 109 } 110 111 ssize_t 112 mdb_readstr(char *buf, size_t nbytes, uintptr_t addr) 113 { 114 return (mdb_tgt_readstr(mdb.m_target, MDB_TGT_AS_VIRT, 115 buf, nbytes, addr)); 116 } 117 118 ssize_t 119 mdb_writestr(const char *buf, uintptr_t addr) 120 { 121 return (mdb_tgt_writestr(mdb.m_target, MDB_TGT_AS_VIRT, buf, addr)); 122 } 123 124 ssize_t 125 mdb_readsym(void *buf, size_t nbytes, const char *name) 126 { 127 ssize_t rbytes = mdb_tgt_readsym(mdb.m_target, MDB_TGT_AS_VIRT, 128 buf, nbytes, MDB_TGT_OBJ_EXEC, name); 129 130 if (rbytes > 0 && rbytes < nbytes) 131 return (set_errbytes(rbytes, nbytes)); 132 133 return (rbytes); 134 } 135 136 ssize_t 137 mdb_writesym(const void *buf, size_t nbytes, const char *name) 138 { 139 return (mdb_tgt_writesym(mdb.m_target, MDB_TGT_AS_VIRT, 140 buf, nbytes, MDB_TGT_OBJ_EXEC, name)); 141 } 142 143 ssize_t 144 mdb_readvar(void *buf, const char *name) 145 { 146 GElf_Sym sym; 147 148 if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EXEC, 149 name, &sym, NULL)) 150 return (-1); 151 152 if (mdb_tgt_vread(mdb.m_target, buf, sym.st_size, 153 (uintptr_t)sym.st_value) == sym.st_size) 154 return ((ssize_t)sym.st_size); 155 156 return (-1); 157 } 158 159 ssize_t 160 mdb_writevar(const void *buf, const char *name) 161 { 162 GElf_Sym sym; 163 164 if (mdb_tgt_lookup_by_name(mdb.m_target, MDB_TGT_OBJ_EXEC, 165 name, &sym, NULL)) 166 return (-1); 167 168 if (mdb_tgt_vwrite(mdb.m_target, buf, sym.st_size, 169 (uintptr_t)sym.st_value) == sym.st_size) 170 return ((ssize_t)sym.st_size); 171 172 return (-1); 173 } 174 175 int 176 mdb_lookup_by_name(const char *name, GElf_Sym *sym) 177 { 178 return (mdb_lookup_by_obj(MDB_TGT_OBJ_EXEC, name, sym)); 179 } 180 181 int 182 mdb_lookup_by_obj(const char *obj, const char *name, GElf_Sym *sym) 183 { 184 return (mdb_tgt_lookup_by_name(mdb.m_target, obj, name, sym, NULL)); 185 } 186 187 int 188 mdb_lookup_by_addr(uintptr_t addr, uint_t flags, char *buf, 189 size_t nbytes, GElf_Sym *sym) 190 { 191 return (mdb_tgt_lookup_by_addr(mdb.m_target, addr, flags, 192 buf, nbytes, sym, NULL)); 193 } 194 195 u_longlong_t 196 mdb_strtoull(const char *s) 197 { 198 int radix = mdb.m_radix; 199 200 if (s[0] == '0') { 201 switch (s[1]) { 202 case 'I': 203 case 'i': 204 radix = 2; 205 s += 2; 206 break; 207 case 'O': 208 case 'o': 209 radix = 8; 210 s += 2; 211 break; 212 case 'T': 213 case 't': 214 radix = 10; 215 s += 2; 216 break; 217 case 'X': 218 case 'x': 219 radix = 16; 220 s += 2; 221 break; 222 } 223 } 224 225 return (strtonum(s, radix)); 226 } 227 228 size_t 229 mdb_snprintf(char *buf, size_t nbytes, const char *format, ...) 230 { 231 va_list alist; 232 233 va_start(alist, format); 234 nbytes = mdb_iob_vsnprintf(buf, nbytes, format, alist); 235 va_end(alist); 236 237 return (nbytes); 238 } 239 240 void 241 mdb_printf(const char *format, ...) 242 { 243 va_list alist; 244 245 va_start(alist, format); 246 mdb_iob_vprintf(mdb.m_out, format, alist); 247 va_end(alist); 248 } 249 250 void 251 mdb_warn(const char *format, ...) 252 { 253 va_list alist; 254 255 va_start(alist, format); 256 vwarn(format, alist); 257 va_end(alist); 258 } 259 260 void 261 mdb_flush(void) 262 { 263 mdb_iob_flush(mdb.m_out); 264 } 265 266 /* 267 * Convert an object of len bytes pointed to by srcraw between 268 * network-order and host-order and store in dstraw. The length len must 269 * be the actual length of the objects pointed to by srcraw and dstraw (or 270 * zero) or the results are undefined. srcraw and dstraw may be the same, 271 * in which case the object is converted in-place. Note that this routine 272 * will convert from host-order to network-order or network-order to 273 * host-order, since the conversion is the same in either case. 274 */ 275 /* ARGSUSED */ 276 void 277 mdb_nhconvert(void *dstraw, const void *srcraw, size_t len) 278 { 279 #ifdef _LITTLE_ENDIAN 280 uint8_t b1, b2; 281 uint8_t *dst, *src; 282 size_t i; 283 284 dst = (uint8_t *)dstraw; 285 src = (uint8_t *)srcraw; 286 for (i = 0; i < len / 2; i++) { 287 b1 = src[i]; 288 b2 = src[len - i - 1]; 289 dst[i] = b2; 290 dst[len - i - 1] = b1; 291 } 292 #else 293 if (dstraw != srcraw) 294 bcopy(srcraw, dstraw, len); 295 #endif 296 } 297 298 299 /* 300 * Bit formatting functions: Note the interesting use of UM_GC here to 301 * allocate a buffer for the caller which will be automatically freed 302 * when the dcmd completes or is forcibly aborted. 303 */ 304 305 #define NBNB (NBBY / 2) /* number of bits per nibble */ 306 #define SETBIT(buf, j, c) { \ 307 if (((j) + 1) % (NBNB + 1) == 0) \ 308 (buf)[(j)++] = ' '; \ 309 (buf)[(j)++] = (c); \ 310 } 311 312 const char * 313 mdb_one_bit(int width, int bit, int on) 314 { 315 int i, j = 0; 316 char *buf; 317 318 buf = mdb_zalloc(width + (width / NBNB) + 2, UM_GC | UM_SLEEP); 319 320 for (i = --width; i > bit; i--) 321 SETBIT(buf, j, '.'); 322 323 SETBIT(buf, j, on ? '1' : '0'); 324 325 for (i = bit - 1; i >= 0; i--) 326 SETBIT(buf, j, '.'); 327 328 return (buf); 329 } 330 331 const char * 332 mdb_inval_bits(int width, int start, int stop) 333 { 334 int i, j = 0; 335 char *buf; 336 337 buf = mdb_zalloc(width + (width / NBNB) + 2, UM_GC | UM_SLEEP); 338 339 for (i = --width; i > stop; i--) 340 SETBIT(buf, j, '.'); 341 342 for (i = stop; i >= start; i--) 343 SETBIT(buf, j, 'x'); 344 345 for (; i >= 0; i--) 346 SETBIT(buf, j, '.'); 347 348 return (buf); 349 } 350 351 ulong_t 352 mdb_inc_indent(ulong_t i) 353 { 354 if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT) { 355 ulong_t margin = mdb_iob_getmargin(mdb.m_out); 356 mdb_iob_margin(mdb.m_out, margin + i); 357 return (margin); 358 } 359 360 mdb_iob_margin(mdb.m_out, i); 361 mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT); 362 return (0); 363 } 364 365 ulong_t 366 mdb_dec_indent(ulong_t i) 367 { 368 if (mdb_iob_getflags(mdb.m_out) & MDB_IOB_INDENT) { 369 ulong_t margin = mdb_iob_getmargin(mdb.m_out); 370 371 if (margin < i || margin - i == 0) { 372 mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT); 373 mdb_iob_margin(mdb.m_out, MDB_IOB_DEFMARGIN); 374 } else 375 mdb_iob_margin(mdb.m_out, margin - i); 376 377 return (margin); 378 } 379 380 return (0); 381 } 382 383 int 384 mdb_eval(const char *s) 385 { 386 mdb_frame_t *ofp = mdb.m_fmark; 387 mdb_frame_t *fp = mdb.m_frame; 388 int err; 389 390 if (s == NULL) 391 return (set_errno(EINVAL)); 392 393 /* 394 * Push m_in down onto the input stack, then set m_in to point to the 395 * i/o buffer for our command string, and reset the frame marker. 396 * The mdb_run() function returns when the new m_in iob reaches EOF. 397 */ 398 mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno); 399 mdb.m_in = mdb_iob_create(mdb_strio_create(s), MDB_IOB_RDONLY); 400 401 mdb.m_fmark = NULL; 402 err = mdb_run(); 403 mdb.m_fmark = ofp; 404 405 /* 406 * Now pop the old standard input stream and restore mdb.m_in and 407 * the parser's saved current line number. 408 */ 409 mdb.m_in = mdb_iob_stack_pop(&fp->f_istk); 410 yylineno = mdb_iob_lineno(mdb.m_in); 411 412 /* 413 * If mdb_run() returned an error, propagate this backward 414 * up the stack of debugger environment frames. 415 */ 416 if (MDB_ERR_IS_FATAL(err)) 417 longjmp(fp->f_pcb, err); 418 419 if (err == MDB_ERR_PAGER || err == MDB_ERR_SIGINT) 420 return (set_errno(EMDB_CANCEL)); 421 422 if (err != 0) 423 return (set_errno(EMDB_EVAL)); 424 425 return (0); 426 } 427 428 void 429 mdb_set_dot(uintmax_t addr) 430 { 431 mdb_nv_set_value(mdb.m_dot, addr); 432 mdb.m_incr = 0; 433 } 434 435 uintmax_t 436 mdb_get_dot(void) 437 { 438 return (mdb_nv_get_value(mdb.m_dot)); 439 } 440 441 static int 442 walk_step(mdb_wcb_t *wcb) 443 { 444 mdb_wcb_t *nwcb = wcb->w_lyr_head; 445 int status; 446 447 /* 448 * If the control block has no layers, we just invoke the walker's 449 * step function and return status indicating whether to continue 450 * or stop. If the control block has layers, we need to invoke 451 * ourself recursively for the next layer, until eventually we 452 * percolate down to an unlayered walk. 453 */ 454 if (nwcb == NULL) 455 return (wcb->w_walker->iwlk_step(&wcb->w_state)); 456 457 if ((status = walk_step(nwcb)) != WALK_NEXT) { 458 wcb->w_lyr_head = nwcb->w_lyr_link; 459 nwcb->w_lyr_link = NULL; 460 mdb_wcb_destroy(nwcb); 461 } 462 463 if (status == WALK_DONE && wcb->w_lyr_head != NULL) 464 return (WALK_NEXT); 465 466 return (status); 467 } 468 469 static int 470 walk_common(mdb_wcb_t *wcb) 471 { 472 int status, rval = 0; 473 mdb_frame_t *pfp; 474 475 /* 476 * Enter the control block in the active list so that mdb can clean 477 * up after it in case we abort out of the current command. 478 */ 479 if ((pfp = mdb_list_prev(mdb.m_frame)) != NULL && pfp->f_pcmd != NULL) 480 mdb_wcb_insert(wcb, pfp); 481 else 482 mdb_wcb_insert(wcb, mdb.m_frame); 483 484 /* 485 * The per-walk constructor performs private buffer initialization 486 * and locates whatever symbols are necessary. 487 */ 488 if ((status = wcb->w_walker->iwlk_init(&wcb->w_state)) != WALK_NEXT) { 489 if (status != WALK_DONE) 490 rval = set_errno(EMDB_WALKINIT); 491 goto done; 492 } 493 494 /* 495 * Mark wcb to indicate that walk_init has been called (which means 496 * we can call walk_fini if the walk is aborted at this point). 497 */ 498 wcb->w_inited = TRUE; 499 500 while (walk_step(wcb) == WALK_NEXT) 501 continue; 502 done: 503 if ((pfp = mdb_list_prev(mdb.m_frame)) != NULL && pfp->f_pcmd != NULL) 504 mdb_wcb_delete(wcb, pfp); 505 else 506 mdb_wcb_delete(wcb, mdb.m_frame); 507 508 mdb_wcb_destroy(wcb); 509 return (rval); 510 } 511 512 int 513 mdb_pwalk(const char *name, mdb_walk_cb_t func, void *data, uintptr_t addr) 514 { 515 mdb_iwalker_t *iwp = mdb_walker_lookup(name); 516 517 if (func == NULL) 518 return (set_errno(EINVAL)); 519 520 if (iwp != NULL) 521 return (walk_common(mdb_wcb_create(iwp, func, data, addr))); 522 523 return (-1); /* errno is set for us */ 524 } 525 526 int 527 mdb_walk(const char *name, mdb_walk_cb_t func, void *data) 528 { 529 return (mdb_pwalk(name, func, data, NULL)); 530 } 531 532 /*ARGSUSED*/ 533 static int 534 walk_dcmd(uintptr_t addr, const void *ignored, dcmd_walk_arg_t *dwp) 535 { 536 int status = mdb_call_idcmd(dwp->dw_dcmd, addr, 1, dwp->dw_flags, 537 &dwp->dw_argv, NULL, NULL); 538 539 if (status == DCMD_USAGE || status == DCMD_ABORT) 540 return (WALK_ERR); 541 542 dwp->dw_flags &= ~DCMD_LOOPFIRST; 543 return (WALK_NEXT); 544 } 545 546 int 547 mdb_pwalk_dcmd(const char *wname, const char *dcname, 548 int argc, const mdb_arg_t *argv, uintptr_t addr) 549 { 550 mdb_argvec_t args; 551 dcmd_walk_arg_t dw; 552 mdb_iwalker_t *iwp; 553 mdb_wcb_t *wcb; 554 int status; 555 556 if (wname == NULL || dcname == NULL) 557 return (set_errno(EINVAL)); 558 559 if ((dw.dw_dcmd = mdb_dcmd_lookup(dcname)) == NULL) 560 return (-1); /* errno is set for us */ 561 562 if ((iwp = mdb_walker_lookup(wname)) == NULL) 563 return (-1); /* errno is set for us */ 564 565 args.a_data = (mdb_arg_t *)argv; 566 args.a_nelems = args.a_size = argc; 567 568 mdb_argvec_create(&dw.dw_argv); 569 mdb_argvec_copy(&dw.dw_argv, &args); 570 dw.dw_flags = DCMD_LOOP | DCMD_LOOPFIRST | DCMD_ADDRSPEC; 571 572 wcb = mdb_wcb_create(iwp, (mdb_walk_cb_t)walk_dcmd, &dw, addr); 573 status = walk_common(wcb); 574 575 mdb_argvec_zero(&dw.dw_argv); 576 mdb_argvec_destroy(&dw.dw_argv); 577 578 return (status); 579 } 580 581 int 582 mdb_walk_dcmd(const char *wname, const char *dcname, 583 int argc, const mdb_arg_t *argv) 584 { 585 return (mdb_pwalk_dcmd(wname, dcname, argc, argv, NULL)); 586 } 587 588 /*ARGSUSED*/ 589 static int 590 layered_walk_step(uintptr_t addr, const void *data, mdb_wcb_t *wcb) 591 { 592 /* 593 * Prior to calling the top-level walker's step function, reset its 594 * mdb_walk_state_t walk_addr and walk_layer members to refer to the 595 * target virtual address and data buffer of the underlying object. 596 */ 597 wcb->w_state.walk_addr = addr; 598 wcb->w_state.walk_layer = data; 599 600 return (wcb->w_walker->iwlk_step(&wcb->w_state)); 601 } 602 603 int 604 mdb_layered_walk(const char *wname, mdb_walk_state_t *wsp) 605 { 606 mdb_wcb_t *cwcb, *wcb; 607 mdb_iwalker_t *iwp; 608 609 if (wname == NULL || wsp == NULL) 610 return (set_errno(EINVAL)); 611 612 if ((iwp = mdb_walker_lookup(wname)) == NULL) 613 return (-1); /* errno is set for us */ 614 615 if ((cwcb = mdb_wcb_from_state(wsp)) == NULL) 616 return (set_errno(EMDB_BADWCB)); 617 618 if (cwcb->w_walker == iwp) 619 return (set_errno(EMDB_WALKLOOP)); 620 621 wcb = mdb_wcb_create(iwp, (mdb_walk_cb_t)layered_walk_step, 622 cwcb, wsp->walk_addr); 623 624 if (iwp->iwlk_init(&wcb->w_state) != WALK_NEXT) { 625 mdb_wcb_destroy(wcb); 626 return (set_errno(EMDB_WALKINIT)); 627 } 628 629 wcb->w_inited = TRUE; 630 631 mdb_dprintf(MDB_DBG_WALK, "added %s`%s as %s`%s layer\n", 632 iwp->iwlk_modp->mod_name, iwp->iwlk_name, 633 cwcb->w_walker->iwlk_modp->mod_name, cwcb->w_walker->iwlk_name); 634 635 if (cwcb->w_lyr_head != NULL) { 636 for (cwcb = cwcb->w_lyr_head; cwcb->w_lyr_link != NULL; ) 637 cwcb = cwcb->w_lyr_link; 638 cwcb->w_lyr_link = wcb; 639 } else 640 cwcb->w_lyr_head = wcb; 641 642 return (0); 643 } 644 645 int 646 mdb_call_dcmd(const char *name, uintptr_t dot, uint_t flags, 647 int argc, const mdb_arg_t *argv) 648 { 649 mdb_idcmd_t *idcp; 650 mdb_argvec_t args; 651 int status; 652 653 if (name == NULL || argc < 0) 654 return (set_errno(EINVAL)); 655 656 if ((idcp = mdb_dcmd_lookup(name)) == NULL) 657 return (-1); /* errno is set for us */ 658 659 args.a_data = (mdb_arg_t *)argv; 660 args.a_nelems = args.a_size = argc; 661 status = mdb_call_idcmd(idcp, dot, 1, flags, &args, NULL, NULL); 662 663 if (status == DCMD_ERR || status == DCMD_ABORT) 664 return (set_errno(EMDB_DCFAIL)); 665 666 if (status == DCMD_USAGE) 667 return (set_errno(EMDB_DCUSAGE)); 668 669 return (0); 670 } 671 672 int 673 mdb_add_walker(const mdb_walker_t *wp) 674 { 675 mdb_module_t *mp; 676 677 if (mdb.m_lmod == NULL) { 678 mdb_cmd_t *cp = mdb.m_frame->f_cp; 679 mp = cp->c_dcmd->idc_modp; 680 } else 681 mp = mdb.m_lmod; 682 683 return (mdb_module_add_walker(mp, wp, 0)); 684 } 685 686 int 687 mdb_remove_walker(const char *name) 688 { 689 mdb_module_t *mp; 690 691 if (mdb.m_lmod == NULL) { 692 mdb_cmd_t *cp = mdb.m_frame->f_cp; 693 mp = cp->c_dcmd->idc_modp; 694 } else 695 mp = mdb.m_lmod; 696 697 return (mdb_module_remove_walker(mp, name)); 698 } 699 700 void 701 mdb_get_pipe(mdb_pipe_t *p) 702 { 703 mdb_cmd_t *cp = mdb.m_frame->f_cp; 704 mdb_addrvec_t *adp = &cp->c_addrv; 705 706 if (p == NULL) { 707 warn("dcmd failure: mdb_get_pipe invoked with NULL pointer\n"); 708 longjmp(mdb.m_frame->f_pcb, MDB_ERR_API); 709 } 710 711 if (adp->ad_nelems != 0) { 712 ASSERT(adp->ad_ndx != 0); 713 p->pipe_data = &adp->ad_data[adp->ad_ndx - 1]; 714 p->pipe_len = adp->ad_nelems - adp->ad_ndx + 1; 715 adp->ad_ndx = adp->ad_nelems; 716 } else { 717 p->pipe_data = NULL; 718 p->pipe_len = 0; 719 } 720 } 721 722 void 723 mdb_set_pipe(const mdb_pipe_t *p) 724 { 725 mdb_cmd_t *cp = mdb.m_frame->f_pcmd; 726 727 if (p == NULL) { 728 warn("dcmd failure: mdb_set_pipe invoked with NULL pointer\n"); 729 longjmp(mdb.m_frame->f_pcb, MDB_ERR_API); 730 } 731 732 if (cp != NULL) { 733 size_t nbytes = sizeof (uintptr_t) * p->pipe_len; 734 735 mdb_cmd_reset(cp); 736 cp->c_addrv.ad_data = mdb_alloc(nbytes, UM_SLEEP); 737 bcopy(p->pipe_data, cp->c_addrv.ad_data, nbytes); 738 cp->c_addrv.ad_nelems = p->pipe_len; 739 cp->c_addrv.ad_size = p->pipe_len; 740 } 741 } 742 743 ssize_t 744 mdb_get_xdata(const char *name, void *buf, size_t nbytes) 745 { 746 return (mdb_tgt_getxdata(mdb.m_target, name, buf, nbytes)); 747 } 748 749 /* 750 * Private structure and function for implementing mdb_dumpptr on top 751 * of mdb_dump_internal 752 */ 753 typedef struct dptrdat { 754 mdb_dumpptr_cb_t func; 755 void *arg; 756 } dptrdat_t; 757 758 static ssize_t 759 mdb_dump_aux_ptr(void *buf, size_t nbyte, uint64_t offset, void *arg) 760 { 761 dptrdat_t *dat = arg; 762 763 return (dat->func(buf, nbyte, offset, dat->arg)); 764 } 765 766 /* 767 * Private structure and function for handling callbacks which return 768 * EMDB_PARTIAL 769 */ 770 typedef struct d64dat { 771 mdb_dump64_cb_t func; 772 void *arg; 773 } d64dat_t; 774 775 static ssize_t 776 mdb_dump_aux_partial(void *buf, size_t nbyte, uint64_t offset, void *arg) 777 { 778 d64dat_t *dat = arg; 779 int result; 780 int count; 781 782 result = dat->func(buf, nbyte, offset, dat->arg); 783 if (result == -1 && errno == EMDB_PARTIAL) { 784 count = 0; 785 do { 786 result = dat->func((char *)buf + count, 1, 787 offset + count, dat->arg); 788 if (result == 1) 789 count++; 790 } while (count < nbyte && result == 1); 791 if (count) 792 result = count; 793 } 794 795 return (result); 796 } 797 798 int 799 mdb_dumpptr(uintptr_t addr, size_t len, uint_t flags, mdb_dumpptr_cb_t fp, 800 void *arg) 801 { 802 dptrdat_t dat; 803 d64dat_t dat64; 804 805 dat.func = fp; 806 dat.arg = arg; 807 dat64.func = mdb_dump_aux_ptr; 808 dat64.arg = &dat; 809 return (mdb_dump_internal(addr, len, flags, mdb_dump_aux_partial, 810 &dat64, sizeof (uintptr_t))); 811 } 812 813 int 814 mdb_dump64(uint64_t addr, uint64_t len, uint_t flags, mdb_dump64_cb_t fp, 815 void *arg) 816 { 817 d64dat_t dat64; 818 819 dat64.func = fp; 820 dat64.arg = arg; 821 return (mdb_dump_internal(addr, len, flags, mdb_dump_aux_partial, 822 &dat64, sizeof (uint64_t))); 823 } 824 825 int 826 mdb_get_state(void) 827 { 828 mdb_tgt_status_t ts; 829 830 (void) mdb_tgt_status(mdb.m_target, &ts); 831 832 return (ts.st_state); 833 } 834 835 void * 836 mdb_callback_add(int class, mdb_callback_f fp, void *arg) 837 { 838 mdb_module_t *m; 839 840 if (class != MDB_CALLBACK_STCHG && class != MDB_CALLBACK_PROMPT) { 841 (void) set_errno(EINVAL); 842 return (NULL); 843 } 844 845 if (mdb.m_lmod != NULL) 846 m = mdb.m_lmod; 847 else 848 m = mdb.m_frame->f_cp->c_dcmd->idc_modp; 849 850 return (mdb_callb_add(m, class, fp, arg)); 851 } 852 853 void 854 mdb_callback_remove(void *hdl) 855 { 856 mdb_callb_remove(hdl); 857 } 858 859 /* 860 * Divides the given range (inclusive at both endpoints) evenly into the given 861 * number of buckets, adding one bucket at the end that is one past the end of 862 * the range. The returned buckets will be automatically freed when the dcmd 863 * completes or is forcibly aborted. 864 */ 865 const int * 866 mdb_dist_linear(int buckets, int beg, int end) 867 { 868 int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC); 869 int pos; 870 int dist = end - beg + 1; 871 872 for (pos = 0; pos < buckets; pos++) 873 out[pos] = beg + (pos * dist)/buckets; 874 out[buckets] = end + 1; 875 876 return (out); 877 } 878 879 /* 880 * We want the bins to be a constant ratio: 881 * 882 * b_0 = beg; 883 * b_idx = b_{idx-1} * r; 884 * b_buckets = end + 1; 885 * 886 * That is: 887 * 888 * buckets 889 * beg * r = end 890 * 891 * Which reduces to: 892 * 893 * buckets ___________________ 894 * r = -------/ ((end + 1) / beg) 895 * 896 * log ((end + 1) / beg) 897 * log r = --------------------- 898 * buckets 899 * 900 * (log ((end + 1) / beg)) / buckets 901 * r = e 902 */ 903 /* ARGSUSED */ 904 const int * 905 mdb_dist_geometric(int buckets, int beg, int end, int minbucketsize) 906 { 907 #ifdef _KMDB 908 return (mdb_dist_linear(buckets, beg, end)); 909 #else 910 int *out = mdb_alloc((buckets + 1) * sizeof (*out), UM_SLEEP | UM_GC); 911 912 extern double log(double); 913 extern double exp(double); 914 915 double r; 916 double b; 917 int idx = 0; 918 int last; 919 int begzero; 920 921 if (minbucketsize == 0) 922 minbucketsize = 1; 923 924 if (buckets == 1) { 925 out[0] = beg; 926 out[1] = end + 1; 927 return (out); 928 } 929 930 begzero = (beg == 0); 931 if (begzero) 932 beg = 1; 933 934 r = exp(log((double)(end + 1) / beg) / buckets); 935 936 /* 937 * We've now computed r, using the previously derived formula. We 938 * now need to generate the array of bucket bounds. There are 939 * two major variables: 940 * 941 * b holds b_idx, the current index, as a double. 942 * last holds the integer which goes into out[idx] 943 * 944 * Our job is to transform the smooth function b_idx, defined 945 * above, into integer-sized buckets, with a specified minimum 946 * bucket size. Since b_idx is an exponentially growing function, 947 * any inadequate buckets must be at the beginning. To deal 948 * with this, we make buckets of minimum size until b catches up 949 * with last. 950 * 951 * A final wrinkle is that beg *can* be zero. We compute r and b 952 * as if beg was 1, then start last as 0. This can lead to a bit 953 * of oddness around the 0 bucket, but it's mostly reasonable. 954 */ 955 956 b = last = beg; 957 if (begzero) 958 last = 0; 959 960 for (idx = 0; idx < buckets; idx++) { 961 int next; 962 963 out[idx] = last; 964 965 b *= r; 966 next = (int)b; 967 968 if (next > last + minbucketsize - 1) 969 last = next; 970 else 971 last += minbucketsize; 972 } 973 out[buckets] = end + 1; 974 975 return (out); 976 #endif 977 } 978 979 #define NCHARS 50 980 /* 981 * Print the distribution header with the given bucket label. The header is 982 * printed on a single line, and the label is assumed to fit within the given 983 * width (number of characters). The default label width when unspecified (0) 984 * is eleven characters. Optionally, a label other than "count" may be specified 985 * for the bucket counts. 986 */ 987 void 988 mdb_dist_print_header(const char *label, int width, const char *count) 989 { 990 int n; 991 const char *dist = " Distribution "; 992 char dashes[NCHARS + 1]; 993 994 if (width == 0) 995 width = 11; 996 997 if (count == NULL) 998 count = "count"; 999 1000 n = (NCHARS - strlen(dist)) / 2; 1001 (void) memset(dashes, '-', n); 1002 dashes[n] = '\0'; 1003 1004 mdb_printf("%*s %s%s%s %s\n", width, label, dashes, dist, dashes, 1005 count); 1006 } 1007 1008 /* 1009 * Print one distribution bucket whose range is from distarray[i] inclusive to 1010 * distarray[i + 1] exclusive by totalling counts in that index range. The 1011 * given total is assumed to be the sum of all elements in the counts array. 1012 * Each bucket is labeled by its range in the form "first-last" (omit "-last" if 1013 * the range is a single value) where first and last are integers, and last is 1014 * one less than the first value of the next bucket range. The bucket label is 1015 * assumed to fit within the given width (number of characters), which should 1016 * match the width value passed to mdb_dist_print_header(). The default width 1017 * when unspecified (0) is eleven characters. 1018 */ 1019 void 1020 mdb_dist_print_bucket(const int *distarray, int i, uint_t counts[], 1021 uint64_t total, int width) 1022 { 1023 int b; /* bucket range index */ 1024 int bb = distarray[i]; /* bucket begin */ 1025 int be = distarray[i + 1] - 1; /* bucket end */ 1026 uint64_t count = 0; /* bucket value */ 1027 1028 int nats; 1029 char ats[NCHARS + 1], spaces[NCHARS + 1]; 1030 char range[40]; 1031 1032 if (width == 0) 1033 width = 11; 1034 1035 if (total == 0) 1036 total = 1; /* avoid divide-by-zero */ 1037 1038 for (b = bb; b <= be; b++) 1039 count += counts[b]; 1040 1041 nats = (NCHARS * count) / total; 1042 (void) memset(ats, '@', nats); 1043 ats[nats] = 0; 1044 (void) memset(spaces, ' ', NCHARS - nats); 1045 spaces[NCHARS - nats] = 0; 1046 1047 if (bb == be) 1048 (void) mdb_snprintf(range, sizeof (range), "%d", bb); 1049 else 1050 (void) mdb_snprintf(range, sizeof (range), "%d-%d", bb, be); 1051 mdb_printf("%*s |%s%s %lld\n", width, range, ats, spaces, count); 1052 } 1053 #undef NCHARS 1054