Print this page
9210 remove KMDB branch debugging support
9211 ::crregs could do with cr2/cr3 support
9209 ::ttrace should be able to filter by thread
Reviewed by: Patrick Mooney <patrick.mooney@joyent.com>
Reviewed by: Yuri Pankov <yuripv@yuripv.net>
Split |
Close |
Expand all |
Collapse all |
--- old/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
+++ new/usr/src/cmd/mdb/common/mdb/mdb_cmds.c
1 1 /*
2 2 * CDDL HEADER START
3 3 *
4 4 * The contents of this file are subject to the terms of the
5 5 * Common Development and Distribution License (the "License").
6 6 * You may not use this file except in compliance with the License.
7 7 *
8 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 9 * or http://www.opensolaris.org/os/licensing.
10 10 * See the License for the specific language governing permissions
11 11 * and limitations under the License.
12 12 *
13 13 * When distributing Covered Code, include this CDDL HEADER in each
14 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 15 * If applicable, add the following below this CDDL HEADER, with the
16 16 * fields enclosed by brackets "[]" replaced with your own identifying
17 17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 18 *
↓ open down ↓ |
18 lines elided |
↑ open up ↑ |
19 19 * CDDL HEADER END
20 20 */
21 21
22 22 /*
23 23 * Copyright 2009 Sun Microsystems, Inc. All rights reserved.
24 24 * Use is subject to license terms.
25 25 */
26 26
27 27 /*
28 28 * Copyright (c) 2012 by Delphix. All rights reserved.
29 - * Copyright (c) 2015 Joyent, Inc. All rights reserved.
29 + * Copyright (c) 2018 Joyent, Inc. All rights reserved.
30 30 * Copyright (c) 2013 Josef 'Jeff' Sipek <jeffpc@josefsipek.net>
31 31 * Copyright (c) 2015, 2017 by Delphix. All rights reserved.
32 32 */
33 33
34 34 #include <sys/elf.h>
35 35 #include <sys/elf_SPARC.h>
36 36
37 37 #include <libproc.h>
38 38 #include <stdlib.h>
39 39 #include <string.h>
40 40 #include <fcntl.h>
41 41 #include <errno.h>
42 42 #include <alloca.h>
43 43 #include <libctf.h>
44 44 #include <ctype.h>
45 45
46 46 #include <mdb/mdb_string.h>
47 47 #include <mdb/mdb_argvec.h>
48 48 #include <mdb/mdb_nv.h>
49 49 #include <mdb/mdb_fmt.h>
50 50 #include <mdb/mdb_target.h>
51 51 #include <mdb/mdb_err.h>
52 52 #include <mdb/mdb_debug.h>
53 53 #include <mdb/mdb_conf.h>
54 54 #include <mdb/mdb_module.h>
55 55 #include <mdb/mdb_modapi.h>
56 56 #include <mdb/mdb_stdlib.h>
57 57 #include <mdb/mdb_lex.h>
58 58 #include <mdb/mdb_io_impl.h>
59 59 #include <mdb/mdb_help.h>
60 60 #include <mdb/mdb_disasm.h>
61 61 #include <mdb/mdb_frame.h>
62 62 #include <mdb/mdb_evset.h>
63 63 #include <mdb/mdb_print.h>
64 64 #include <mdb/mdb_nm.h>
65 65 #include <mdb/mdb_set.h>
66 66 #include <mdb/mdb_demangle.h>
67 67 #include <mdb/mdb_ctf.h>
68 68 #include <mdb/mdb_whatis.h>
69 69 #include <mdb/mdb_whatis_impl.h>
70 70 #include <mdb/mdb_macalias.h>
71 71 #include <mdb/mdb_tab.h>
72 72 #include <mdb/mdb_typedef.h>
73 73 #ifdef _KMDB
74 74 #include <kmdb/kmdb_kdi.h>
75 75 #endif
76 76 #include <mdb/mdb.h>
77 77
78 78 #ifdef __sparc
79 79 #define SETHI_MASK 0xc1c00000
80 80 #define SETHI_VALUE 0x01000000
81 81
82 82 #define IS_SETHI(machcode) (((machcode) & SETHI_MASK) == SETHI_VALUE)
83 83
84 84 #define OP(machcode) ((machcode) >> 30)
85 85 #define OP3(machcode) (((machcode) >> 19) & 0x3f)
86 86 #define RD(machcode) (((machcode) >> 25) & 0x1f)
87 87 #define RS1(machcode) (((machcode) >> 14) & 0x1f)
88 88 #define I(machcode) (((machcode) >> 13) & 0x01)
89 89
90 90 #define IMM13(machcode) ((machcode) & 0x1fff)
91 91 #define IMM22(machcode) ((machcode) & 0x3fffff)
92 92
93 93 #define OP_ARITH_MEM_MASK 0x2
94 94 #define OP_ARITH 0x2
95 95 #define OP_MEM 0x3
96 96
97 97 #define OP3_CC_MASK 0x10
98 98 #define OP3_COMPLEX_MASK 0x20
99 99
100 100 #define OP3_ADD 0x00
101 101 #define OP3_OR 0x02
102 102 #define OP3_XOR 0x03
103 103
104 104 #ifndef R_O7
105 105 #define R_O7 0xf
106 106 #endif
107 107 #endif /* __sparc */
108 108
109 109 static mdb_tgt_addr_t
110 110 write_uint8(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
111 111 {
112 112 uint8_t o, n = (uint8_t)ull;
113 113
114 114 if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
115 115 addr) == -1)
116 116 return (addr);
117 117
118 118 if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
119 119 return (addr);
120 120
121 121 if (rdback) {
122 122 if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
123 123 return (addr);
124 124
125 125 mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8x=%8T0x%x\n",
126 126 mdb_iob_getmargin(mdb.m_out), addr, o, n);
127 127 }
128 128
129 129 return (addr + sizeof (n));
130 130 }
131 131
132 132 static mdb_tgt_addr_t
133 133 write_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
134 134 {
135 135 uint16_t o, n = (uint16_t)ull;
136 136
137 137 if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
138 138 addr) == -1)
139 139 return (addr);
140 140
141 141 if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
142 142 return (addr);
143 143
144 144 if (rdback) {
145 145 if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
146 146 return (addr);
147 147
148 148 mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#8hx=%8T0x%hx\n",
149 149 mdb_iob_getmargin(mdb.m_out), addr, o, n);
150 150 }
151 151
152 152 return (addr + sizeof (n));
153 153 }
154 154
155 155 static mdb_tgt_addr_t
156 156 write_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t ull, uint_t rdback)
157 157 {
158 158 uint32_t o, n = (uint32_t)ull;
159 159
160 160 if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
161 161 addr) == -1)
162 162 return (addr);
163 163
164 164 if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
165 165 return (addr);
166 166
167 167 if (rdback) {
168 168 if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
169 169 return (addr);
170 170
171 171 mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#16x=%8T0x%x\n",
172 172 mdb_iob_getmargin(mdb.m_out), addr, o, n);
173 173 }
174 174
175 175 return (addr + sizeof (n));
176 176 }
177 177
178 178 static mdb_tgt_addr_t
179 179 write_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
180 180 {
181 181 uint64_t o;
182 182
183 183 if (rdback && mdb_tgt_aread(mdb.m_target, as, &o, sizeof (o),
184 184 addr) == -1)
185 185 return (addr);
186 186
187 187 if (mdb_tgt_awrite(mdb.m_target, as, &n, sizeof (n), addr) == -1)
188 188 return (addr);
189 189
190 190 if (rdback) {
191 191 if (mdb_tgt_aread(mdb.m_target, as, &n, sizeof (n), addr) == -1)
192 192 return (addr);
193 193
194 194 mdb_iob_printf(mdb.m_out, "%-#*lla%16T%-#24llx=%8T0x%llx\n",
195 195 mdb_iob_getmargin(mdb.m_out), addr, o, n);
196 196 }
197 197
198 198 return (addr + sizeof (n));
199 199 }
200 200
201 201 /*
202 202 * Writes to objects of size 1, 2, 4, or 8 bytes. The function
203 203 * doesn't care if the object is a number or not (e.g. it could
204 204 * be a byte array, or a struct) as long as the size of the write
205 205 * is one of the aforementioned ones.
206 206 */
207 207 static mdb_tgt_addr_t
208 208 write_var_uint(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, size_t size,
209 209 uint_t rdback)
210 210 {
211 211 if (size < sizeof (uint64_t)) {
212 212 uint64_t max_num = 1ULL << (size * NBBY);
213 213
214 214 if (val >= max_num) {
215 215 uint64_t write_len = 0;
216 216
217 217 /* count bytes needed for val */
218 218 while (val != 0) {
219 219 write_len++;
220 220 val >>= NBBY;
221 221 }
222 222
223 223 mdb_warn("value too big for the length of the write: "
224 224 "supplied %llu bytes but maximum is %llu bytes\n",
225 225 (u_longlong_t)write_len, (u_longlong_t)size);
226 226 return (addr);
227 227 }
228 228 }
229 229
230 230 switch (size) {
231 231 case 1:
232 232 return (write_uint8(as, addr, val, rdback));
233 233 case 2:
234 234 return (write_uint16(as, addr, val, rdback));
235 235 case 4:
236 236 return (write_uint32(as, addr, val, rdback));
237 237 case 8:
238 238 return (write_uint64(as, addr, val, rdback));
239 239 default:
240 240 mdb_warn("writes of size %u are not supported\n ", size);
241 241 return (addr);
242 242 }
243 243 }
244 244
245 245 static mdb_tgt_addr_t
246 246 write_ctf_uint(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t n, uint_t rdback)
247 247 {
248 248 mdb_ctf_id_t mid;
249 249 size_t size;
250 250 ssize_t type_size;
251 251 int kind;
252 252
253 253 if (mdb_ctf_lookup_by_addr(addr, &mid) != 0) {
254 254 mdb_warn("no CTF data found at this address\n");
255 255 return (addr);
256 256 }
257 257
258 258 kind = mdb_ctf_type_kind(mid);
259 259 if (kind == CTF_ERR) {
260 260 mdb_warn("CTF data found but type kind could not be read");
261 261 return (addr);
262 262 }
263 263
264 264 if (kind == CTF_K_TYPEDEF) {
265 265 mdb_ctf_id_t temp_id;
266 266 if (mdb_ctf_type_resolve(mid, &temp_id) != 0) {
267 267 mdb_warn("failed to resolve type");
268 268 return (addr);
269 269 }
270 270 kind = mdb_ctf_type_kind(temp_id);
271 271 }
272 272
273 273 if (kind != CTF_K_INTEGER && kind != CTF_K_POINTER &&
274 274 kind != CTF_K_ENUM) {
275 275 mdb_warn("CTF type should be integer, pointer, or enum\n");
276 276 return (addr);
277 277 }
278 278
279 279 type_size = mdb_ctf_type_size(mid);
280 280 if (type_size < 0) {
281 281 mdb_warn("CTF data found but size could not be read");
282 282 return (addr);
283 283 }
284 284 size = type_size;
285 285
286 286 return (write_var_uint(as, addr, n, size, rdback));
287 287 }
288 288
289 289 static int
290 290 write_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr,
291 291 int argc, const mdb_arg_t *argv)
292 292 {
293 293 mdb_tgt_addr_t (*write_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
294 294 uint64_t, uint_t);
295 295 mdb_tgt_addr_t naddr;
296 296 uintmax_t value;
297 297 int rdback = mdb.m_flags & MDB_FL_READBACK;
298 298 size_t i;
299 299
300 300 if (argc == 1) {
301 301 mdb_warn("expected value to write following %c\n",
302 302 argv->a_un.a_char);
303 303 return (DCMD_ERR);
304 304 }
305 305
306 306 switch (argv->a_un.a_char) {
307 307 case 'v':
308 308 write_value = write_uint8;
309 309 break;
310 310 case 'w':
311 311 write_value = write_uint16;
312 312 break;
313 313 case 'z':
314 314 write_value = write_ctf_uint;
315 315 break;
316 316 case 'W':
317 317 write_value = write_uint32;
318 318 break;
319 319 case 'Z':
320 320 write_value = write_uint64;
321 321 break;
322 322 }
323 323
324 324 for (argv++, i = 1; i < argc; i++, argv++) {
325 325 if (argv->a_type == MDB_TYPE_CHAR) {
326 326 mdb_warn("expected immediate value instead of '%c'\n",
327 327 argv->a_un.a_char);
328 328 return (DCMD_ERR);
329 329 }
330 330
331 331 if (argv->a_type == MDB_TYPE_STRING) {
332 332 if (mdb_eval(argv->a_un.a_str) == -1) {
333 333 mdb_warn("failed to write \"%s\"",
334 334 argv->a_un.a_str);
335 335 return (DCMD_ERR);
336 336 }
337 337 value = mdb_nv_get_value(mdb.m_dot);
338 338 } else
339 339 value = argv->a_un.a_val;
340 340
341 341 mdb_nv_set_value(mdb.m_dot, addr);
342 342
343 343 if ((naddr = write_value(as, addr, value, rdback)) == addr) {
344 344 mdb_warn("failed to write %llr at address 0x%llx",
345 345 value, addr);
346 346 mdb.m_incr = 0;
347 347 return (DCMD_ERR);
348 348 }
349 349
350 350 mdb.m_incr = naddr - addr;
351 351 addr = naddr;
352 352 }
353 353
354 354 return (DCMD_OK);
355 355 }
356 356
357 357 static mdb_tgt_addr_t
358 358 match_uint16(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
359 359 {
360 360 uint16_t x, val = (uint16_t)v64, mask = (uint16_t)m64;
361 361
362 362 for (; mdb_tgt_aread(mdb.m_target, as, &x,
363 363 sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
364 364
365 365 if ((x & mask) == val) {
366 366 mdb_iob_printf(mdb.m_out, "%lla\n", addr);
367 367 break;
368 368 }
369 369 }
370 370 return (addr);
371 371 }
372 372
373 373 static mdb_tgt_addr_t
374 374 match_uint32(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t v64, uint64_t m64)
375 375 {
376 376 uint32_t x, val = (uint32_t)v64, mask = (uint32_t)m64;
377 377
378 378 for (; mdb_tgt_aread(mdb.m_target, as, &x,
379 379 sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
380 380
381 381 if ((x & mask) == val) {
382 382 mdb_iob_printf(mdb.m_out, "%lla\n", addr);
383 383 break;
384 384 }
385 385 }
386 386 return (addr);
387 387 }
388 388
389 389 static mdb_tgt_addr_t
390 390 match_uint64(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint64_t val, uint64_t mask)
391 391 {
392 392 uint64_t x;
393 393
394 394 for (; mdb_tgt_aread(mdb.m_target, as, &x,
395 395 sizeof (x), addr) == sizeof (x); addr += sizeof (x)) {
396 396
397 397 if ((x & mask) == val) {
398 398 mdb_iob_printf(mdb.m_out, "%lla\n", addr);
399 399 break;
400 400 }
401 401 }
402 402 return (addr);
403 403 }
404 404
405 405 static int
406 406 match_arglist(mdb_tgt_as_t as, uint_t flags, mdb_tgt_addr_t addr,
407 407 int argc, const mdb_arg_t *argv)
408 408 {
409 409 mdb_tgt_addr_t (*match_value)(mdb_tgt_as_t, mdb_tgt_addr_t,
410 410 uint64_t, uint64_t);
411 411
412 412 uint64_t args[2] = { 0, -1ULL }; /* [ value, mask ] */
413 413 size_t i;
414 414
415 415 if (argc < 2) {
416 416 mdb_warn("expected value following %c\n", argv->a_un.a_char);
417 417 return (DCMD_ERR);
418 418 }
419 419
420 420 if (argc > 3) {
421 421 mdb_warn("only value and mask may follow %c\n",
422 422 argv->a_un.a_char);
423 423 return (DCMD_ERR);
424 424 }
425 425
426 426 switch (argv->a_un.a_char) {
427 427 case 'l':
428 428 match_value = match_uint16;
429 429 break;
430 430 case 'L':
431 431 match_value = match_uint32;
432 432 break;
433 433 case 'M':
434 434 match_value = match_uint64;
435 435 break;
436 436 }
437 437
438 438 for (argv++, i = 1; i < argc; i++, argv++) {
439 439 if (argv->a_type == MDB_TYPE_CHAR) {
440 440 mdb_warn("expected immediate value instead of '%c'\n",
441 441 argv->a_un.a_char);
442 442 return (DCMD_ERR);
443 443 }
444 444
445 445 if (argv->a_type == MDB_TYPE_STRING) {
446 446 if (mdb_eval(argv->a_un.a_str) == -1) {
447 447 mdb_warn("failed to evaluate \"%s\"",
448 448 argv->a_un.a_str);
449 449 return (DCMD_ERR);
450 450 }
451 451 args[i - 1] = mdb_nv_get_value(mdb.m_dot);
452 452 } else
453 453 args[i - 1] = argv->a_un.a_val;
454 454 }
455 455
456 456 addr = match_value(as, addr, args[0], args[1]);
457 457 mdb_nv_set_value(mdb.m_dot, addr);
458 458
459 459 /*
460 460 * In adb(1), the match operators ignore any repeat count that has
461 461 * been applied to them. We emulate this undocumented property
462 462 * by returning DCMD_ABORT if our input is not a pipeline.
463 463 */
464 464 return ((flags & DCMD_PIPE) ? DCMD_OK : DCMD_ABORT);
465 465 }
466 466
467 467 static int
468 468 argncmp(int argc, const mdb_arg_t *argv, const char *s)
469 469 {
470 470 for (; *s != '\0'; s++, argc--, argv++) {
471 471 if (argc == 0 || argv->a_type != MDB_TYPE_CHAR)
472 472 return (FALSE);
473 473 if (argv->a_un.a_char != *s)
474 474 return (FALSE);
475 475 }
476 476 return (TRUE);
477 477 }
478 478
479 479 static int
480 480 print_arglist(mdb_tgt_as_t as, mdb_tgt_addr_t addr, uint_t flags,
481 481 int argc, const mdb_arg_t *argv)
482 482 {
483 483 char buf[MDB_TGT_SYM_NAMLEN];
484 484 mdb_tgt_addr_t oaddr = addr;
485 485 mdb_tgt_addr_t naddr;
486 486 GElf_Sym sym;
487 487 size_t i, n;
488 488
489 489 if (DCMD_HDRSPEC(flags) && (flags & DCMD_PIPE_OUT) == 0) {
490 490 const char *fmt;
491 491 int is_dis;
492 492 /*
493 493 * This is nasty, but necessary for precise adb compatibility.
494 494 * Detect disassembly format by looking for "ai" or "ia":
495 495 */
496 496 if (argncmp(argc, argv, "ai")) {
497 497 fmt = "%-#*lla\n";
498 498 is_dis = TRUE;
499 499 } else if (argncmp(argc, argv, "ia")) {
500 500 fmt = "%-#*lla";
501 501 is_dis = TRUE;
502 502 } else {
503 503 fmt = "%-#*lla%16T";
504 504 is_dis = FALSE;
505 505 }
506 506
507 507 /*
508 508 * If symbolic decoding is on, disassembly is off, and the
509 509 * address exactly matches a symbol, print the symbol name:
510 510 */
511 511 if ((mdb.m_flags & MDB_FL_PSYM) && !is_dis &&
512 512 (as == MDB_TGT_AS_VIRT || as == MDB_TGT_AS_FILE) &&
513 513 mdb_tgt_lookup_by_addr(mdb.m_target, (uintptr_t)addr,
514 514 MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0)
515 515 mdb_iob_printf(mdb.m_out, "%s:\n", buf);
516 516
517 517 /*
518 518 * If this is a virtual address, cast it so that it reflects
519 519 * only the valid component of the address.
520 520 */
521 521 if (as == MDB_TGT_AS_VIRT)
522 522 addr = (uintptr_t)addr;
523 523
524 524 mdb_iob_printf(mdb.m_out, fmt,
525 525 (uint_t)mdb_iob_getmargin(mdb.m_out), addr);
526 526 }
527 527
528 528 if (argc == 0) {
529 529 /*
530 530 * Yes, for you trivia buffs: if you use a format verb and give
531 531 * no format string, you get: X^"= "i ... note that in adb the
532 532 * the '=' verb once had 'z' as its default, but then 'z' was
533 533 * deleted (it was once an alias for 'i') and so =\n now calls
534 534 * scanform("z") and produces a 'bad modifier' message.
535 535 */
536 536 static const mdb_arg_t def_argv[] = {
537 537 { MDB_TYPE_CHAR, MDB_INIT_CHAR('X') },
538 538 { MDB_TYPE_CHAR, MDB_INIT_CHAR('^') },
539 539 { MDB_TYPE_STRING, MDB_INIT_STRING("= ") },
540 540 { MDB_TYPE_CHAR, MDB_INIT_CHAR('i') }
541 541 };
542 542
543 543 argc = sizeof (def_argv) / sizeof (mdb_arg_t);
544 544 argv = def_argv;
545 545 }
546 546
547 547 mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
548 548
549 549 for (i = 0, n = 1; i < argc; i++, argv++) {
550 550 switch (argv->a_type) {
551 551 case MDB_TYPE_CHAR:
552 552 naddr = mdb_fmt_print(mdb.m_target, as, addr, n,
553 553 argv->a_un.a_char);
554 554 mdb.m_incr = naddr - addr;
555 555 addr = naddr;
556 556 n = 1;
557 557 break;
558 558
559 559 case MDB_TYPE_IMMEDIATE:
560 560 n = argv->a_un.a_val;
561 561 break;
562 562
563 563 case MDB_TYPE_STRING:
564 564 mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
565 565 n = 1;
566 566 break;
567 567 }
568 568 }
569 569
570 570 mdb.m_incr = addr - oaddr;
571 571 mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
572 572 return (DCMD_OK);
573 573 }
574 574
575 575 static int
576 576 print_common(mdb_tgt_as_t as, uint_t flags, int argc, const mdb_arg_t *argv)
577 577 {
578 578 mdb_tgt_addr_t addr = mdb_nv_get_value(mdb.m_dot);
579 579
580 580 if (argc != 0 && argv->a_type == MDB_TYPE_CHAR) {
581 581 if (strchr("vwzWZ", argv->a_un.a_char))
582 582 return (write_arglist(as, addr, argc, argv));
583 583 if (strchr("lLM", argv->a_un.a_char))
584 584 return (match_arglist(as, flags, addr, argc, argv));
585 585 }
586 586
587 587 return (print_arglist(as, addr, flags, argc, argv));
588 588 }
589 589
590 590 /*ARGSUSED*/
591 591 static int
592 592 cmd_print_core(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
593 593 {
594 594 return (print_common(MDB_TGT_AS_VIRT, flags, argc, argv));
595 595 }
596 596
597 597 #ifndef _KMDB
598 598 /*ARGSUSED*/
599 599 static int
600 600 cmd_print_object(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
601 601 {
602 602 return (print_common(MDB_TGT_AS_FILE, flags, argc, argv));
603 603 }
604 604 #endif
605 605
606 606 /*ARGSUSED*/
607 607 static int
608 608 cmd_print_phys(uintptr_t x, uint_t flags, int argc, const mdb_arg_t *argv)
609 609 {
610 610 return (print_common(MDB_TGT_AS_PHYS, flags, argc, argv));
611 611 }
612 612
613 613 /*ARGSUSED*/
614 614 static int
615 615 cmd_print_value(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
616 616 {
617 617 uintmax_t ndot, dot = mdb_get_dot();
618 618 const char *tgt_argv[1];
619 619 mdb_tgt_t *t;
620 620 size_t i, n;
621 621
622 622 if (argc == 0) {
623 623 mdb_warn("expected one or more format characters "
624 624 "following '='\n");
625 625 return (DCMD_ERR);
626 626 }
627 627
628 628 tgt_argv[0] = (const char *)˙
629 629 t = mdb_tgt_create(mdb_value_tgt_create, 0, 1, tgt_argv);
630 630 mdb_iob_setflags(mdb.m_out, MDB_IOB_INDENT);
631 631
632 632 for (i = 0, n = 1; i < argc; i++, argv++) {
633 633 switch (argv->a_type) {
634 634 case MDB_TYPE_CHAR:
635 635 ndot = mdb_fmt_print(t, MDB_TGT_AS_VIRT,
636 636 dot, n, argv->a_un.a_char);
637 637 if (argv->a_un.a_char == '+' ||
638 638 argv->a_un.a_char == '-')
639 639 dot = ndot;
640 640 n = 1;
641 641 break;
642 642
643 643 case MDB_TYPE_IMMEDIATE:
644 644 n = argv->a_un.a_val;
645 645 break;
646 646
647 647 case MDB_TYPE_STRING:
648 648 mdb_iob_puts(mdb.m_out, argv->a_un.a_str);
649 649 n = 1;
650 650 break;
651 651 }
652 652 }
653 653
654 654 mdb_iob_clrflags(mdb.m_out, MDB_IOB_INDENT);
655 655 mdb_nv_set_value(mdb.m_dot, dot);
656 656 mdb.m_incr = 0;
657 657
658 658 mdb_tgt_destroy(t);
659 659 return (DCMD_OK);
660 660 }
661 661
662 662 /*ARGSUSED*/
663 663 static int
664 664 cmd_assign_variable(uintptr_t addr, uint_t flags,
665 665 int argc, const mdb_arg_t *argv)
666 666 {
667 667 uintmax_t dot = mdb_nv_get_value(mdb.m_dot);
668 668 const char *p;
669 669 mdb_var_t *v;
670 670
671 671 if (argc == 2) {
672 672 if (argv->a_type != MDB_TYPE_CHAR) {
673 673 mdb_warn("improper arguments following '>' operator\n");
674 674 return (DCMD_ERR);
675 675 }
676 676
677 677 switch (argv->a_un.a_char) {
678 678 case 'c':
679 679 addr = *((uchar_t *)&addr);
680 680 break;
681 681 case 's':
682 682 addr = *((ushort_t *)&addr);
683 683 break;
684 684 case 'i':
685 685 addr = *((uint_t *)&addr);
686 686 break;
687 687 case 'l':
688 688 addr = *((ulong_t *)&addr);
689 689 break;
690 690 default:
691 691 mdb_warn("%c is not a valid // modifier\n",
692 692 argv->a_un.a_char);
693 693 return (DCMD_ERR);
694 694 }
695 695
696 696 dot = addr;
697 697 argv++;
698 698 argc--;
699 699 }
700 700
701 701 if (argc != 1 || argv->a_type != MDB_TYPE_STRING) {
702 702 mdb_warn("expected single variable name following '>'\n");
703 703 return (DCMD_ERR);
704 704 }
705 705
706 706 if (strlen(argv->a_un.a_str) >= (size_t)MDB_NV_NAMELEN) {
707 707 mdb_warn("variable names may not exceed %d characters\n",
708 708 MDB_NV_NAMELEN - 1);
709 709 return (DCMD_ERR);
710 710 }
711 711
712 712 if ((p = strbadid(argv->a_un.a_str)) != NULL) {
713 713 mdb_warn("'%c' may not be used in a variable name\n", *p);
714 714 return (DCMD_ERR);
715 715 }
716 716
717 717 if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
718 718 (void) mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str, NULL, dot, 0);
719 719 else
720 720 mdb_nv_set_value(v, dot);
721 721
722 722 mdb.m_incr = 0;
723 723 return (DCMD_OK);
724 724 }
725 725
726 726 static int
727 727 print_soutype(const char *sou, uintptr_t addr, uint_t flags)
728 728 {
729 729 static const char *prefixes[] = { "struct ", "union " };
730 730 size_t namesz = 7 + strlen(sou) + 1;
731 731 char *name = mdb_alloc(namesz, UM_SLEEP | UM_GC);
732 732 mdb_ctf_id_t id;
733 733 int i;
734 734
735 735 for (i = 0; i < 2; i++) {
736 736 (void) mdb_snprintf(name, namesz, "%s%s", prefixes[i], sou);
737 737
738 738 if (mdb_ctf_lookup_by_name(name, &id) == 0) {
739 739 mdb_arg_t v;
740 740 int rv;
741 741
742 742 v.a_type = MDB_TYPE_STRING;
743 743 v.a_un.a_str = name;
744 744
745 745 rv = mdb_call_dcmd("print", addr, flags, 1, &v);
746 746 return (rv);
747 747 }
748 748 }
749 749
750 750 return (DCMD_ERR);
751 751 }
752 752
753 753 static int
754 754 print_type(const char *name, uintptr_t addr, uint_t flags)
755 755 {
756 756 mdb_ctf_id_t id;
757 757 char *sname;
758 758 size_t snamesz;
759 759 int rv;
760 760
761 761 if (!(flags & DCMD_ADDRSPEC)) {
762 762 addr = mdb_get_dot();
763 763 flags |= DCMD_ADDRSPEC;
764 764 }
765 765
766 766 if ((rv = print_soutype(name, addr, flags)) != DCMD_ERR)
767 767 return (rv);
768 768
769 769 snamesz = strlen(name) + 3;
770 770 sname = mdb_zalloc(snamesz, UM_SLEEP | UM_GC);
771 771 (void) mdb_snprintf(sname, snamesz, "%s_t", name);
772 772
773 773 if (mdb_ctf_lookup_by_name(sname, &id) == 0) {
774 774 mdb_arg_t v;
775 775 int rv;
776 776
777 777 v.a_type = MDB_TYPE_STRING;
778 778 v.a_un.a_str = sname;
779 779
780 780 rv = mdb_call_dcmd("print", addr, flags, 1, &v);
781 781 return (rv);
782 782 }
783 783
784 784 sname[snamesz - 2] = 's';
785 785 rv = print_soutype(sname, addr, flags);
786 786 return (rv);
787 787 }
788 788
789 789 static int
790 790 exec_alias(const char *fname, uintptr_t addr, uint_t flags)
791 791 {
792 792 const char *alias;
793 793 int rv;
794 794
795 795 if ((alias = mdb_macalias_lookup(fname)) == NULL)
796 796 return (DCMD_ERR);
797 797
798 798 if (flags & DCMD_ADDRSPEC) {
799 799 size_t sz = sizeof (uintptr_t) * 2 + strlen(alias) + 1;
800 800 char *addralias = mdb_alloc(sz, UM_SLEEP | UM_GC);
801 801 (void) mdb_snprintf(addralias, sz, "%p%s", addr, alias);
802 802 rv = mdb_eval(addralias);
803 803 } else {
804 804 rv = mdb_eval(alias);
805 805 }
806 806
807 807 return (rv == -1 ? DCMD_ABORT : DCMD_OK);
808 808 }
809 809
810 810 /*ARGSUSED*/
811 811 static int
812 812 cmd_src_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
813 813 {
814 814 const char *fname;
815 815 mdb_io_t *fio;
816 816 int rv;
817 817
818 818 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
819 819 return (DCMD_USAGE);
820 820
821 821 fname = argv->a_un.a_str;
822 822
823 823 if (flags & DCMD_PIPE_OUT) {
824 824 mdb_warn("macro files cannot be used as input to a pipeline\n");
825 825 return (DCMD_ABORT);
826 826 }
827 827
828 828 if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
829 829 O_RDONLY, 0)) != NULL) {
830 830 mdb_frame_t *fp = mdb.m_frame;
831 831 int err;
832 832
833 833 mdb_iob_stack_push(&fp->f_istk, mdb.m_in, yylineno);
834 834 mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
835 835 err = mdb_run();
836 836
837 837 ASSERT(fp == mdb.m_frame);
838 838 mdb.m_in = mdb_iob_stack_pop(&fp->f_istk);
839 839 yylineno = mdb_iob_lineno(mdb.m_in);
840 840
841 841 if (err == MDB_ERR_PAGER && mdb.m_fmark != fp)
842 842 longjmp(fp->f_pcb, err);
843 843
844 844 if (err == MDB_ERR_QUIT || err == MDB_ERR_ABORT ||
845 845 err == MDB_ERR_SIGINT || err == MDB_ERR_OUTPUT)
846 846 longjmp(fp->f_pcb, err);
847 847
848 848 return (DCMD_OK);
849 849 }
850 850
851 851 if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
852 852 (rv = print_type(fname, addr, flags)) != DCMD_ERR)
853 853 return (rv);
854 854
855 855 mdb_warn("failed to open %s (see ::help '$<')\n", fname);
856 856 return (DCMD_ABORT);
857 857 }
858 858
859 859 static int
860 860 cmd_exec_file(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
861 861 {
862 862 const char *fname;
863 863 mdb_io_t *fio;
864 864 int rv;
865 865
866 866 /*
867 867 * The syntax [expr[,count]]$< with no trailing macro file name is
868 868 * magic in that if count is zero, this command won't be called and
869 869 * the expression is thus a no-op. If count is non-zero, we get
870 870 * invoked with argc == 0, and this means abort the current macro.
871 871 * If our debugger stack depth is greater than one, we may be using
872 872 * $< from within a previous $<<, so in that case we set m_in to
873 873 * NULL to force this entire frame to be popped.
874 874 */
875 875 if (argc == 0) {
876 876 if (mdb_iob_stack_size(&mdb.m_frame->f_istk) != 0) {
877 877 mdb_iob_destroy(mdb.m_in);
878 878 mdb.m_in = mdb_iob_stack_pop(&mdb.m_frame->f_istk);
879 879 } else if (mdb.m_depth > 1) {
880 880 mdb_iob_destroy(mdb.m_in);
881 881 mdb.m_in = NULL;
882 882 } else
883 883 mdb_warn("input stack is empty\n");
884 884 return (DCMD_OK);
885 885 }
886 886
887 887 if ((flags & (DCMD_PIPE | DCMD_PIPE_OUT)) || mdb.m_depth == 1)
888 888 return (cmd_src_file(addr, flags, argc, argv));
889 889
890 890 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
891 891 return (DCMD_USAGE);
892 892
893 893 fname = argv->a_un.a_str;
894 894
895 895 if ((fio = mdb_fdio_create_path(mdb.m_ipath, fname,
896 896 O_RDONLY, 0)) != NULL) {
897 897 mdb_iob_destroy(mdb.m_in);
898 898 mdb.m_in = mdb_iob_create(fio, MDB_IOB_RDONLY);
899 899 return (DCMD_OK);
900 900 }
901 901
902 902 if ((rv = exec_alias(fname, addr, flags)) != DCMD_ERR ||
903 903 (rv = print_type(fname, addr, flags)) != DCMD_ERR)
904 904 return (rv);
905 905
906 906 mdb_warn("failed to open %s (see ::help '$<')\n", fname);
907 907 return (DCMD_ABORT);
908 908 }
909 909
910 910 #ifndef _KMDB
911 911 /*ARGSUSED*/
912 912 static int
913 913 cmd_cat(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
914 914 {
915 915 int status = DCMD_OK;
916 916 char buf[BUFSIZ];
917 917 mdb_iob_t *iob;
918 918 mdb_io_t *fio;
919 919
920 920 if (flags & DCMD_ADDRSPEC)
921 921 return (DCMD_USAGE);
922 922
923 923 for (; argc-- != 0; argv++) {
924 924 if (argv->a_type != MDB_TYPE_STRING) {
925 925 mdb_warn("expected string argument\n");
926 926 status = DCMD_ERR;
927 927 continue;
928 928 }
929 929
930 930 if ((fio = mdb_fdio_create_path(NULL,
931 931 argv->a_un.a_str, O_RDONLY, 0)) == NULL) {
932 932 mdb_warn("failed to open %s", argv->a_un.a_str);
933 933 status = DCMD_ERR;
934 934 continue;
935 935 }
936 936
937 937 iob = mdb_iob_create(fio, MDB_IOB_RDONLY);
938 938
939 939 while (!(mdb_iob_getflags(iob) & (MDB_IOB_EOF | MDB_IOB_ERR))) {
940 940 ssize_t len = mdb_iob_read(iob, buf, sizeof (buf));
941 941 if (len > 0) {
942 942 if (mdb_iob_write(mdb.m_out, buf, len) < 0) {
943 943 if (errno != EPIPE)
944 944 mdb_warn("write failed");
945 945 status = DCMD_ERR;
946 946 break;
947 947 }
948 948 }
949 949 }
950 950
951 951 if (mdb_iob_err(iob))
952 952 mdb_warn("error while reading %s", mdb_iob_name(iob));
953 953
954 954 mdb_iob_destroy(iob);
955 955 }
956 956
957 957 return (status);
958 958 }
959 959 #endif
960 960
961 961 /*ARGSUSED*/
962 962 static int
963 963 cmd_grep(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
964 964 {
965 965 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
966 966 return (DCMD_USAGE);
967 967
968 968 if (mdb_eval(argv->a_un.a_str) == -1)
969 969 return (DCMD_ABORT);
970 970
971 971 if (mdb_get_dot() != 0)
972 972 mdb_printf("%lr\n", addr);
973 973
974 974 return (DCMD_OK);
975 975 }
976 976
977 977 /*ARGSUSED*/
978 978 static int
979 979 cmd_map(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
980 980 {
981 981 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
982 982 return (DCMD_USAGE);
983 983
984 984 if (mdb_eval(argv->a_un.a_str) == -1)
985 985 return (DCMD_ABORT);
986 986
987 987 mdb_printf("%llr\n", mdb_get_dot());
988 988 return (DCMD_OK);
989 989 }
990 990
991 991 /*ARGSUSED*/
992 992 static int
993 993 cmd_notsup(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
994 994 {
995 995 mdb_warn("command is not supported by current target\n");
996 996 return (DCMD_ERR);
997 997 }
998 998
999 999 /*ARGSUSED*/
1000 1000 static int
1001 1001 cmd_quit(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1002 1002 {
1003 1003 #ifdef _KMDB
1004 1004 uint_t opt_u = FALSE;
1005 1005
1006 1006 if (mdb_getopts(argc, argv,
1007 1007 'u', MDB_OPT_SETBITS, TRUE, &opt_u, NULL) != argc)
1008 1008 return (DCMD_USAGE);
1009 1009
1010 1010 if (opt_u) {
1011 1011 if (mdb.m_flags & MDB_FL_NOUNLOAD) {
1012 1012 warn("%s\n", mdb_strerror(EMDB_KNOUNLOAD));
1013 1013 return (DCMD_ERR);
1014 1014 }
1015 1015
1016 1016 kmdb_kdi_set_unload_request();
1017 1017 }
1018 1018 #endif
1019 1019
1020 1020 longjmp(mdb.m_frame->f_pcb, MDB_ERR_QUIT);
1021 1021 /*NOTREACHED*/
1022 1022 return (DCMD_ERR);
1023 1023 }
1024 1024
1025 1025 #ifdef _KMDB
1026 1026 static void
1027 1027 quit_help(void)
1028 1028 {
1029 1029 mdb_printf(
1030 1030 "-u unload the debugger (if not loaded at boot)\n");
1031 1031 }
1032 1032 #endif
1033 1033
1034 1034 /*ARGSUSED*/
1035 1035 static int
1036 1036 cmd_vars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1037 1037 {
1038 1038 uint_t opt_nz = FALSE, opt_tag = FALSE, opt_prt = FALSE;
1039 1039 mdb_var_t *v;
1040 1040
1041 1041 if (mdb_getopts(argc, argv,
1042 1042 'n', MDB_OPT_SETBITS, TRUE, &opt_nz,
1043 1043 'p', MDB_OPT_SETBITS, TRUE, &opt_prt,
1044 1044 't', MDB_OPT_SETBITS, TRUE, &opt_tag, NULL) != argc)
1045 1045 return (DCMD_USAGE);
1046 1046
1047 1047 mdb_nv_rewind(&mdb.m_nv);
1048 1048
1049 1049 while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
1050 1050 if ((opt_tag == FALSE || (v->v_flags & MDB_NV_TAGGED)) &&
1051 1051 (opt_nz == FALSE || mdb_nv_get_value(v) != 0)) {
1052 1052 if (opt_prt) {
1053 1053 mdb_printf("%#llr>%s\n",
1054 1054 mdb_nv_get_value(v), mdb_nv_get_name(v));
1055 1055 } else {
1056 1056 mdb_printf("%s = %llr\n",
1057 1057 mdb_nv_get_name(v), mdb_nv_get_value(v));
1058 1058 }
1059 1059 }
1060 1060 }
1061 1061
1062 1062 return (DCMD_OK);
1063 1063 }
1064 1064
1065 1065 /*ARGSUSED*/
1066 1066 static int
1067 1067 cmd_nzvars(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1068 1068 {
1069 1069 uintmax_t value;
1070 1070 mdb_var_t *v;
1071 1071
1072 1072 if (argc != 0)
1073 1073 return (DCMD_USAGE);
1074 1074
1075 1075 mdb_nv_rewind(&mdb.m_nv);
1076 1076
1077 1077 while ((v = mdb_nv_advance(&mdb.m_nv)) != NULL) {
1078 1078 if ((value = mdb_nv_get_value(v)) != 0)
1079 1079 mdb_printf("%s = %llr\n", mdb_nv_get_name(v), value);
1080 1080 }
1081 1081
1082 1082 return (DCMD_OK);
1083 1083 }
1084 1084
1085 1085 /*ARGSUSED*/
1086 1086 static int
1087 1087 cmd_radix(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1088 1088 {
1089 1089 if (argc != 0)
1090 1090 return (DCMD_USAGE);
1091 1091
1092 1092 if (flags & DCMD_ADDRSPEC) {
1093 1093 if (addr < 2 || addr > 16) {
1094 1094 mdb_warn("expected radix from 2 to 16\n");
1095 1095 return (DCMD_ERR);
1096 1096 }
1097 1097 mdb.m_radix = (int)addr;
1098 1098 }
1099 1099
1100 1100 mdb_iob_printf(mdb.m_out, "radix = %d base ten\n", mdb.m_radix);
1101 1101 return (DCMD_OK);
1102 1102 }
1103 1103
1104 1104 /*ARGSUSED*/
1105 1105 static int
1106 1106 cmd_symdist(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1107 1107 {
1108 1108 if (argc != 0)
1109 1109 return (DCMD_USAGE);
1110 1110
1111 1111 if (flags & DCMD_ADDRSPEC)
1112 1112 mdb.m_symdist = addr;
1113 1113
1114 1114 mdb_printf("symbol matching distance = %lr (%s)\n",
1115 1115 mdb.m_symdist, mdb.m_symdist ? "absolute mode" : "smart mode");
1116 1116
1117 1117 return (DCMD_OK);
1118 1118 }
1119 1119
1120 1120 /*ARGSUSED*/
1121 1121 static int
1122 1122 cmd_pgwidth(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1123 1123 {
1124 1124 if (argc != 0)
1125 1125 return (DCMD_USAGE);
1126 1126
1127 1127 if (flags & DCMD_ADDRSPEC)
1128 1128 mdb_iob_resize(mdb.m_out, mdb.m_out->iob_rows, addr);
1129 1129
1130 1130 mdb_printf("output page width = %lu\n", mdb.m_out->iob_cols);
1131 1131 return (DCMD_OK);
1132 1132 }
1133 1133
1134 1134 /*ARGSUSED*/
1135 1135 static int
1136 1136 cmd_reopen(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1137 1137 {
1138 1138 if (argc != 0)
1139 1139 return (DCMD_USAGE);
1140 1140
1141 1141 if (mdb_tgt_setflags(mdb.m_target, MDB_TGT_F_RDWR) == -1) {
1142 1142 mdb_warn("failed to re-open target for writing");
1143 1143 return (DCMD_ERR);
1144 1144 }
1145 1145
1146 1146 return (DCMD_OK);
1147 1147 }
1148 1148
1149 1149 /*ARGSUSED*/
1150 1150 static int
1151 1151 print_xdata(void *ignored, const char *name, const char *desc, size_t nbytes)
1152 1152 {
1153 1153 mdb_printf("%-24s - %s (%lu bytes)\n", name, desc, (ulong_t)nbytes);
1154 1154 return (0);
1155 1155 }
1156 1156
1157 1157 /*ARGSUSED*/
1158 1158 static int
1159 1159 cmd_xdata(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1160 1160 {
1161 1161 if (argc != 0 || (flags & DCMD_ADDRSPEC))
1162 1162 return (DCMD_USAGE);
1163 1163
1164 1164 (void) mdb_tgt_xdata_iter(mdb.m_target, print_xdata, NULL);
1165 1165 return (DCMD_OK);
1166 1166 }
1167 1167
1168 1168 /*ARGSUSED*/
1169 1169 static int
1170 1170 cmd_unset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1171 1171 {
1172 1172 mdb_var_t *v;
1173 1173 size_t i;
1174 1174
1175 1175 for (i = 0; i < argc; i++) {
1176 1176 if (argv[i].a_type != MDB_TYPE_STRING) {
1177 1177 mdb_warn("bad option: arg %lu is not a string\n",
1178 1178 (ulong_t)i + 1);
1179 1179 return (DCMD_USAGE);
1180 1180 }
1181 1181 }
1182 1182
1183 1183 for (i = 0; i < argc; i++, argv++) {
1184 1184 if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL)
1185 1185 mdb_warn("variable '%s' not defined\n",
1186 1186 argv->a_un.a_str);
1187 1187 else
1188 1188 mdb_nv_remove(&mdb.m_nv, v);
1189 1189 }
1190 1190
1191 1191 return (DCMD_OK);
1192 1192 }
1193 1193
1194 1194 #ifndef _KMDB
1195 1195 /*ARGSUSED*/
1196 1196 static int
1197 1197 cmd_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1198 1198 {
1199 1199 uint_t opt_e = FALSE, opt_d = FALSE;
1200 1200 const char *filename = NULL;
1201 1201 int i;
1202 1202
1203 1203 i = mdb_getopts(argc, argv,
1204 1204 'd', MDB_OPT_SETBITS, TRUE, &opt_d,
1205 1205 'e', MDB_OPT_SETBITS, TRUE, &opt_e, NULL);
1206 1206
1207 1207 if ((i != argc && i != argc - 1) || (opt_d && opt_e) ||
1208 1208 (i != argc && argv[i].a_type != MDB_TYPE_STRING) ||
1209 1209 (i != argc && opt_d == TRUE) || (flags & DCMD_ADDRSPEC))
1210 1210 return (DCMD_USAGE);
1211 1211
1212 1212 if (mdb.m_depth != 1) {
1213 1213 mdb_warn("log may not be manipulated in this context\n");
1214 1214 return (DCMD_ABORT);
1215 1215 }
1216 1216
1217 1217 if (i != argc)
1218 1218 filename = argv[i].a_un.a_str;
1219 1219
1220 1220 /*
1221 1221 * If no arguments were specified, print the log file name (if any)
1222 1222 * and report whether the log is enabled or disabled.
1223 1223 */
1224 1224 if (argc == 0) {
1225 1225 if (mdb.m_log) {
1226 1226 mdb_printf("%s: logging to \"%s\" is currently %s\n",
1227 1227 mdb.m_pname, IOP_NAME(mdb.m_log),
1228 1228 mdb.m_flags & MDB_FL_LOG ? "enabled" : "disabled");
1229 1229 } else
1230 1230 mdb_printf("%s: no log is active\n", mdb.m_pname);
1231 1231 return (DCMD_OK);
1232 1232 }
1233 1233
1234 1234 /*
1235 1235 * If the -d option was specified, pop the log i/o object off the
1236 1236 * i/o stack of stdin, stdout, and stderr.
1237 1237 */
1238 1238 if (opt_d) {
1239 1239 if (mdb.m_flags & MDB_FL_LOG) {
1240 1240 (void) mdb_iob_pop_io(mdb.m_in);
1241 1241 (void) mdb_iob_pop_io(mdb.m_out);
1242 1242 (void) mdb_iob_pop_io(mdb.m_err);
1243 1243 mdb.m_flags &= ~MDB_FL_LOG;
1244 1244 } else
1245 1245 mdb_warn("logging is already disabled\n");
1246 1246 return (DCMD_OK);
1247 1247 }
1248 1248
1249 1249 /*
1250 1250 * The -e option is the default: (re-)enable logging by pushing
1251 1251 * the log i/o object on to stdin, stdout, and stderr. If we have
1252 1252 * a previous log file, we need to pop it and close it. If we have
1253 1253 * no new log file, push the previous one back on.
1254 1254 */
1255 1255 if (filename != NULL) {
1256 1256 if (mdb.m_log != NULL) {
1257 1257 if (mdb.m_flags & MDB_FL_LOG) {
1258 1258 (void) mdb_iob_pop_io(mdb.m_in);
1259 1259 (void) mdb_iob_pop_io(mdb.m_out);
1260 1260 (void) mdb_iob_pop_io(mdb.m_err);
1261 1261 mdb.m_flags &= ~MDB_FL_LOG;
1262 1262 }
1263 1263 mdb_io_rele(mdb.m_log);
1264 1264 }
1265 1265
1266 1266 mdb.m_log = mdb_fdio_create_path(NULL, filename,
1267 1267 O_CREAT | O_APPEND | O_WRONLY, 0666);
1268 1268
1269 1269 if (mdb.m_log == NULL) {
1270 1270 mdb_warn("failed to open %s", filename);
1271 1271 return (DCMD_ERR);
1272 1272 }
1273 1273 }
1274 1274
1275 1275 if (mdb.m_log != NULL) {
1276 1276 mdb_iob_push_io(mdb.m_in, mdb_logio_create(mdb.m_log));
1277 1277 mdb_iob_push_io(mdb.m_out, mdb_logio_create(mdb.m_log));
1278 1278 mdb_iob_push_io(mdb.m_err, mdb_logio_create(mdb.m_log));
1279 1279
1280 1280 mdb_printf("%s: logging to \"%s\"\n", mdb.m_pname, filename);
1281 1281 mdb.m_log = mdb_io_hold(mdb.m_log);
1282 1282 mdb.m_flags |= MDB_FL_LOG;
1283 1283
1284 1284 return (DCMD_OK);
1285 1285 }
1286 1286
1287 1287 mdb_warn("no log file has been selected\n");
1288 1288 return (DCMD_ERR);
1289 1289 }
1290 1290
1291 1291 static int
1292 1292 cmd_old_log(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1293 1293 {
1294 1294 if (argc == 0) {
1295 1295 mdb_arg_t arg = { MDB_TYPE_STRING, MDB_INIT_STRING("-d") };
1296 1296 return (cmd_log(addr, flags, 1, &arg));
1297 1297 }
1298 1298
1299 1299 return (cmd_log(addr, flags, argc, argv));
1300 1300 }
1301 1301 #endif
1302 1302
1303 1303 /*ARGSUSED*/
1304 1304 static int
1305 1305 cmd_load(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1306 1306 {
1307 1307 int i, mode = MDB_MOD_LOCAL;
1308 1308
1309 1309 i = mdb_getopts(argc, argv,
1310 1310 #ifdef _KMDB
1311 1311 'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
1312 1312 #endif
1313 1313 'f', MDB_OPT_SETBITS, MDB_MOD_FORCE, &mode,
1314 1314 'g', MDB_OPT_SETBITS, MDB_MOD_GLOBAL, &mode,
1315 1315 's', MDB_OPT_SETBITS, MDB_MOD_SILENT, &mode,
1316 1316 NULL);
1317 1317
1318 1318 argc -= i;
1319 1319 argv += i;
1320 1320
1321 1321 if ((flags & DCMD_ADDRSPEC) || argc != 1 ||
1322 1322 argv->a_type != MDB_TYPE_STRING ||
1323 1323 strchr("+-", argv->a_un.a_str[0]) != NULL)
1324 1324 return (DCMD_USAGE);
1325 1325
1326 1326 if (mdb_module_load(argv->a_un.a_str, mode) < 0)
1327 1327 return (DCMD_ERR);
1328 1328
1329 1329 return (DCMD_OK);
1330 1330 }
1331 1331
1332 1332 static void
1333 1333 load_help(void)
1334 1334 {
1335 1335 mdb_printf(
1336 1336 #ifdef _KMDB
1337 1337 "-d defer load until next continue\n"
1338 1338 #endif
1339 1339 "-s load module silently\n");
1340 1340 }
1341 1341
1342 1342 /*ARGSUSED*/
1343 1343 static int
1344 1344 cmd_unload(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1345 1345 {
1346 1346 int mode = 0;
1347 1347 int i;
1348 1348
1349 1349 i = mdb_getopts(argc, argv,
1350 1350 #ifdef _KMDB
1351 1351 'd', MDB_OPT_SETBITS, MDB_MOD_DEFER, &mode,
1352 1352 #endif
1353 1353 NULL);
1354 1354
1355 1355 argc -= i;
1356 1356 argv += i;
1357 1357
1358 1358 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
1359 1359 return (DCMD_USAGE);
1360 1360
1361 1361 if (mdb_module_unload(argv->a_un.a_str, mode) == -1) {
1362 1362 mdb_warn("failed to unload %s", argv->a_un.a_str);
1363 1363 return (DCMD_ERR);
1364 1364 }
1365 1365
1366 1366 return (DCMD_OK);
1367 1367 }
1368 1368
1369 1369 #ifdef _KMDB
1370 1370 static void
1371 1371 unload_help(void)
1372 1372 {
1373 1373 mdb_printf(
1374 1374 "-d defer unload until next continue\n");
1375 1375 }
1376 1376 #endif
1377 1377
1378 1378 static int
1379 1379 cmd_dbmode(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1380 1380 {
1381 1381 if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
1382 1382 return (DCMD_USAGE);
1383 1383
1384 1384 if (argc != 0) {
1385 1385 if (argv->a_type != MDB_TYPE_STRING)
1386 1386 return (DCMD_USAGE);
1387 1387 if ((addr = mdb_dstr2mode(argv->a_un.a_str)) != MDB_DBG_HELP)
1388 1388 mdb_dmode(addr);
1389 1389 } else if (flags & DCMD_ADDRSPEC)
1390 1390 mdb_dmode(addr);
1391 1391
1392 1392 mdb_printf("debugging mode = 0x%04x\n", mdb.m_debug);
1393 1393 return (DCMD_OK);
1394 1394 }
1395 1395
1396 1396 /*ARGSUSED*/
1397 1397 static int
1398 1398 cmd_version(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1399 1399 {
1400 1400 #ifdef DEBUG
1401 1401 mdb_printf("\r%s (DEBUG)\n", mdb_conf_version());
1402 1402 #else
1403 1403 mdb_printf("\r%s\n", mdb_conf_version());
1404 1404 #endif
1405 1405 return (DCMD_OK);
1406 1406 }
1407 1407
1408 1408 /*ARGSUSED*/
1409 1409 static int
1410 1410 cmd_algol(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1411 1411 {
1412 1412 if (mdb.m_flags & MDB_FL_ADB)
1413 1413 mdb_printf("No algol 68 here\n");
1414 1414 else
1415 1415 mdb_printf("No adb here\n");
1416 1416 return (DCMD_OK);
1417 1417 }
1418 1418
1419 1419 /*ARGSUSED*/
1420 1420 static int
1421 1421 cmd_obey(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1422 1422 {
1423 1423 if (mdb.m_flags & MDB_FL_ADB)
1424 1424 mdb_printf("CHAPTER 1\n");
1425 1425 else
1426 1426 mdb_printf("No Language H here\n");
1427 1427 return (DCMD_OK);
1428 1428 }
1429 1429
1430 1430 /*ARGSUSED*/
1431 1431 static int
1432 1432 print_global(void *data, const GElf_Sym *sym, const char *name,
1433 1433 const mdb_syminfo_t *sip, const char *obj)
1434 1434 {
1435 1435 uintptr_t value;
1436 1436
1437 1437 if (mdb_tgt_vread((mdb_tgt_t *)data, &value, sizeof (value),
1438 1438 (uintptr_t)sym->st_value) == sizeof (value))
1439 1439 mdb_printf("%s(%llr):\t%lr\n", name, sym->st_value, value);
1440 1440 else
1441 1441 mdb_printf("%s(%llr):\t?\n", name, sym->st_value);
1442 1442
1443 1443 return (0);
1444 1444 }
1445 1445
1446 1446 /*ARGSUSED*/
1447 1447 static int
1448 1448 cmd_globals(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1449 1449 {
1450 1450 if (argc != 0)
1451 1451 return (DCMD_USAGE);
1452 1452
1453 1453 (void) mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
1454 1454 MDB_TGT_SYMTAB, MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_OBJECT |
1455 1455 MDB_TGT_TYPE_FUNC, print_global, mdb.m_target);
1456 1456
1457 1457 return (0);
1458 1458 }
1459 1459
1460 1460 /*ARGSUSED*/
1461 1461 static int
1462 1462 cmd_eval(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1463 1463 {
1464 1464 if (argc != 1 || argv->a_type != MDB_TYPE_STRING)
1465 1465 return (DCMD_USAGE);
1466 1466
1467 1467 if (mdb_eval(argv->a_un.a_str) == -1)
1468 1468 return (DCMD_ABORT);
1469 1469
1470 1470 return (DCMD_OK);
1471 1471 }
1472 1472
1473 1473 /*ARGSUSED*/
1474 1474 static int
1475 1475 print_file(void *data, const GElf_Sym *sym, const char *name,
1476 1476 const mdb_syminfo_t *sip, const char *obj)
1477 1477 {
1478 1478 int i = *((int *)data);
1479 1479
1480 1480 mdb_printf("%d\t%s\n", i++, name);
1481 1481 *((int *)data) = i;
1482 1482 return (0);
1483 1483 }
1484 1484
1485 1485 /*ARGSUSED*/
1486 1486 static int
1487 1487 cmd_files(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1488 1488 {
1489 1489 int i = 1;
1490 1490 const char *obj = MDB_TGT_OBJ_EVERY;
1491 1491
1492 1492 if ((flags & DCMD_ADDRSPEC) || argc > 1)
1493 1493 return (DCMD_USAGE);
1494 1494
1495 1495 if (argc == 1) {
1496 1496 if (argv->a_type != MDB_TYPE_STRING)
1497 1497 return (DCMD_USAGE);
1498 1498
1499 1499 obj = argv->a_un.a_str;
1500 1500 }
1501 1501
1502 1502 (void) mdb_tgt_symbol_iter(mdb.m_target, obj, MDB_TGT_SYMTAB,
1503 1503 MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FILE, print_file, &i);
1504 1504
1505 1505 return (DCMD_OK);
1506 1506 }
1507 1507
1508 1508 static const char *
1509 1509 map_name(const mdb_map_t *map, const char *name)
1510 1510 {
1511 1511 if (map->map_flags & MDB_TGT_MAP_HEAP)
1512 1512 return ("[ heap ]");
1513 1513 if (name != NULL && name[0] != 0)
1514 1514 return (name);
1515 1515
1516 1516 if (map->map_flags & MDB_TGT_MAP_SHMEM)
1517 1517 return ("[ shmem ]");
1518 1518 if (map->map_flags & MDB_TGT_MAP_STACK)
1519 1519 return ("[ stack ]");
1520 1520 if (map->map_flags & MDB_TGT_MAP_ANON)
1521 1521 return ("[ anon ]");
1522 1522 if (map->map_name != NULL)
1523 1523 return (map->map_name);
1524 1524 return ("[ unknown ]");
1525 1525 }
1526 1526
1527 1527 /*ARGSUSED*/
1528 1528 static int
1529 1529 print_map(void *ignored, const mdb_map_t *map, const char *name)
1530 1530 {
1531 1531 name = map_name(map, name);
1532 1532
1533 1533 mdb_printf("%?p %?p %?lx %s\n", map->map_base,
1534 1534 map->map_base + map->map_size, map->map_size, name);
1535 1535 return (0);
1536 1536 }
1537 1537
1538 1538 static int
1539 1539 cmd_mappings(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1540 1540 {
1541 1541 const mdb_map_t *m;
1542 1542
1543 1543 if (argc > 1 || (argc != 0 && (flags & DCMD_ADDRSPEC)))
1544 1544 return (DCMD_USAGE);
1545 1545
1546 1546 mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
1547 1547 "BASE", "LIMIT", "SIZE", "NAME");
1548 1548
1549 1549 if (flags & DCMD_ADDRSPEC) {
1550 1550 if ((m = mdb_tgt_addr_to_map(mdb.m_target, addr)) == NULL)
1551 1551 mdb_warn("failed to obtain mapping");
1552 1552 else
1553 1553 (void) print_map(NULL, m, NULL);
1554 1554
1555 1555 } else if (argc != 0) {
1556 1556 if (argv->a_type == MDB_TYPE_STRING)
1557 1557 m = mdb_tgt_name_to_map(mdb.m_target, argv->a_un.a_str);
1558 1558 else
1559 1559 m = mdb_tgt_addr_to_map(mdb.m_target, argv->a_un.a_val);
1560 1560
1561 1561 if (m == NULL)
1562 1562 mdb_warn("failed to obtain mapping");
1563 1563 else
1564 1564 (void) print_map(NULL, m, NULL);
1565 1565
1566 1566 } else if (mdb_tgt_mapping_iter(mdb.m_target, print_map, NULL) == -1)
1567 1567 mdb_warn("failed to iterate over mappings");
1568 1568
1569 1569 return (DCMD_OK);
1570 1570 }
1571 1571
1572 1572 static int
1573 1573 whatis_map_callback(void *wp, const mdb_map_t *map, const char *name)
1574 1574 {
1575 1575 mdb_whatis_t *w = wp;
1576 1576 uintptr_t cur;
1577 1577
1578 1578 name = map_name(map, name);
1579 1579
1580 1580 while (mdb_whatis_match(w, map->map_base, map->map_size, &cur))
1581 1581 mdb_whatis_report_address(w, cur, "in %s [%p,%p)\n",
1582 1582 name, map->map_base, map->map_base + map->map_size);
1583 1583
1584 1584 return (0);
1585 1585 }
1586 1586
1587 1587 /*ARGSUSED*/
1588 1588 int
1589 1589 whatis_run_mappings(mdb_whatis_t *w, void *ignored)
1590 1590 {
1591 1591 (void) mdb_tgt_mapping_iter(mdb.m_target, whatis_map_callback, w);
1592 1592 return (0);
1593 1593 }
1594 1594
1595 1595 /*ARGSUSED*/
1596 1596 static int
1597 1597 objects_printversion(void *ignored, const mdb_map_t *map, const char *name)
1598 1598 {
1599 1599 ctf_file_t *ctfp;
1600 1600 const char *version;
1601 1601
1602 1602 ctfp = mdb_tgt_name_to_ctf(mdb.m_target, name);
1603 1603 if (ctfp == NULL || (version = ctf_label_topmost(ctfp)) == NULL)
1604 1604 version = "Unknown";
1605 1605
1606 1606 mdb_printf("%-28s %s\n", name, version);
1607 1607 return (0);
1608 1608 }
1609 1609
1610 1610 /*ARGSUSED*/
1611 1611 static int
1612 1612 cmd_objects(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1613 1613 {
1614 1614 uint_t opt_v = FALSE;
1615 1615 mdb_tgt_map_f *cb;
1616 1616
1617 1617 if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
1618 1618 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
1619 1619 return (DCMD_USAGE);
1620 1620
1621 1621 if (opt_v) {
1622 1622 cb = objects_printversion;
1623 1623 mdb_printf("%<u>%-28s %s%</u>\n", "NAME", "VERSION");
1624 1624 } else {
1625 1625 cb = print_map;
1626 1626 mdb_printf("%<u>%?s %?s %?s %s%</u>\n",
1627 1627 "BASE", "LIMIT", "SIZE", "NAME");
1628 1628 }
1629 1629
1630 1630 if (mdb_tgt_object_iter(mdb.m_target, cb, NULL) == -1) {
1631 1631 mdb_warn("failed to iterate over objects");
1632 1632 return (DCMD_ERR);
1633 1633 }
1634 1634
1635 1635 return (DCMD_OK);
1636 1636 }
1637 1637
1638 1638 /*ARGSUSED*/
1639 1639 static int
1640 1640 showrev_addversion(void *vers_nv, const mdb_map_t *ignored, const char *object)
1641 1641 {
1642 1642 ctf_file_t *ctfp;
1643 1643 const char *version = NULL;
1644 1644 char *objname;
1645 1645
1646 1646 objname = mdb_alloc(strlen(object) + 1, UM_SLEEP | UM_GC);
1647 1647 (void) strcpy(objname, object);
1648 1648
1649 1649 if ((ctfp = mdb_tgt_name_to_ctf(mdb.m_target, objname)) != NULL)
1650 1650 version = ctf_label_topmost(ctfp);
1651 1651
1652 1652 /*
1653 1653 * Not all objects have CTF and label data, so set version to "Unknown".
1654 1654 */
1655 1655 if (version == NULL)
1656 1656 version = "Unknown";
1657 1657
1658 1658 (void) mdb_nv_insert(vers_nv, version, NULL, (uintptr_t)objname,
1659 1659 MDB_NV_OVERLOAD);
1660 1660
1661 1661 return (0);
1662 1662 }
1663 1663
1664 1664 static int
1665 1665 showrev_ispatch(const char *s)
1666 1666 {
1667 1667 if (s == NULL)
1668 1668 return (0);
1669 1669
1670 1670 if (*s == 'T')
1671 1671 s++; /* skip T for T-patch */
1672 1672
1673 1673 for (; *s != '\0'; s++) {
1674 1674 if ((*s < '0' || *s > '9') && *s != '-')
1675 1675 return (0);
1676 1676 }
1677 1677
1678 1678 return (1);
1679 1679 }
1680 1680
1681 1681 /*ARGSUSED*/
1682 1682 static int
1683 1683 showrev_printobject(mdb_var_t *v, void *ignored)
1684 1684 {
1685 1685 mdb_printf("%s ", MDB_NV_COOKIE(v));
1686 1686 return (0);
1687 1687 }
1688 1688
1689 1689 static int
1690 1690 showrev_printversion(mdb_var_t *v, void *showall)
1691 1691 {
1692 1692 const char *version = mdb_nv_get_name(v);
1693 1693 int patch;
1694 1694
1695 1695 patch = showrev_ispatch(version);
1696 1696 if (patch || (uintptr_t)showall) {
1697 1697 mdb_printf("%s: %s Objects: ",
1698 1698 (patch ? "Patch" : "Version"), version);
1699 1699 (void) mdb_inc_indent(2);
1700 1700
1701 1701 mdb_nv_defn_iter(v, showrev_printobject, NULL);
1702 1702
1703 1703 (void) mdb_dec_indent(2);
1704 1704 mdb_printf("\n");
1705 1705 }
1706 1706
1707 1707 return (0);
1708 1708 }
1709 1709
1710 1710 /*
1711 1711 * Display version information for each object in the system.
1712 1712 * Print information about patches only, unless showall is TRUE.
1713 1713 */
1714 1714 static int
1715 1715 showrev_objectversions(int showall)
1716 1716 {
1717 1717 mdb_nv_t vers_nv;
1718 1718
1719 1719 (void) mdb_nv_create(&vers_nv, UM_SLEEP | UM_GC);
1720 1720 if (mdb_tgt_object_iter(mdb.m_target, showrev_addversion,
1721 1721 &vers_nv) == -1) {
1722 1722 mdb_warn("failed to iterate over objects");
1723 1723 return (DCMD_ERR);
1724 1724 }
1725 1725
1726 1726 mdb_nv_sort_iter(&vers_nv, showrev_printversion,
1727 1727 (void *)(uintptr_t)showall, UM_SLEEP | UM_GC);
1728 1728 return (DCMD_OK);
1729 1729 }
1730 1730
1731 1731 /*
1732 1732 * Display information similar to what showrev(1M) displays when invoked
1733 1733 * with no arguments.
1734 1734 */
1735 1735 static int
1736 1736 showrev_sysinfo(void)
1737 1737 {
1738 1738 const char *s;
1739 1739 int rc;
1740 1740 struct utsname u;
1741 1741
1742 1742 if ((rc = mdb_tgt_uname(mdb.m_target, &u)) != -1) {
1743 1743 mdb_printf("Hostname: %s\n", u.nodename);
1744 1744 mdb_printf("Release: %s\n", u.release);
1745 1745 mdb_printf("Kernel architecture: %s\n", u.machine);
1746 1746 }
1747 1747
1748 1748 /*
1749 1749 * Match the order of the showrev(1M) output and put "Application
1750 1750 * architecture" before "Kernel version"
1751 1751 */
1752 1752 if ((s = mdb_tgt_isa(mdb.m_target)) != NULL)
1753 1753 mdb_printf("Application architecture: %s\n", s);
1754 1754
1755 1755 if (rc != -1)
1756 1756 mdb_printf("Kernel version: %s %s %s %s\n",
1757 1757 u.sysname, u.release, u.machine, u.version);
1758 1758
1759 1759 if ((s = mdb_tgt_platform(mdb.m_target)) != NULL)
1760 1760 mdb_printf("Platform: %s\n", s);
1761 1761
1762 1762 return (DCMD_OK);
1763 1763 }
1764 1764
1765 1765 /*ARGSUSED*/
1766 1766 static int
1767 1767 cmd_showrev(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1768 1768 {
1769 1769 uint_t opt_p = FALSE, opt_v = FALSE;
1770 1770
1771 1771 if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
1772 1772 'p', MDB_OPT_SETBITS, TRUE, &opt_p,
1773 1773 'v', MDB_OPT_SETBITS, TRUE, &opt_v, NULL) != argc)
1774 1774 return (DCMD_USAGE);
1775 1775
1776 1776 if (opt_p || opt_v)
1777 1777 return (showrev_objectversions(opt_v));
1778 1778 else
1779 1779 return (showrev_sysinfo());
1780 1780 }
1781 1781
1782 1782 #ifdef __sparc
1783 1783 static void
1784 1784 findsym_output(uintptr_t *symlist, uintptr_t value, uintptr_t location)
1785 1785 {
1786 1786 uintptr_t *symbolp;
1787 1787
1788 1788 for (symbolp = symlist; *symbolp; symbolp++)
1789 1789 if (value == *symbolp)
1790 1790 mdb_printf("found %a at %a\n", value, location);
1791 1791 }
1792 1792
1793 1793 /*ARGSUSED*/
1794 1794 static int
1795 1795 findsym_cb(void *data, const GElf_Sym *sym, const char *name,
1796 1796 const mdb_syminfo_t *sip, const char *obj)
1797 1797 {
1798 1798 uint32_t *text;
1799 1799 int len;
1800 1800 int i;
1801 1801 int j;
1802 1802 uint8_t rd;
1803 1803 uintptr_t value;
1804 1804 int32_t imm13;
1805 1805 uint8_t op;
1806 1806 uint8_t op3;
1807 1807 uintptr_t *symlist = data;
1808 1808 size_t size = sym->st_size;
1809 1809
1810 1810 /*
1811 1811 * if the size of the symbol is 0, then this symbol must be for an
1812 1812 * alternate entry point or just some global label. We will,
1813 1813 * therefore, get back to the text that follows this symbol in
1814 1814 * some other symbol
1815 1815 */
1816 1816 if (size == 0)
1817 1817 return (0);
1818 1818
1819 1819 if (sym->st_shndx == SHN_UNDEF)
1820 1820 return (0);
1821 1821
1822 1822 text = alloca(size);
1823 1823
1824 1824 if (mdb_vread(text, size, sym->st_value) == -1) {
1825 1825 mdb_warn("failed to read text for %s", name);
1826 1826 return (0);
1827 1827 }
1828 1828
1829 1829 len = size / 4;
1830 1830 for (i = 0; i < len; i++) {
1831 1831 if (!IS_SETHI(text[i]))
1832 1832 continue;
1833 1833
1834 1834 rd = RD(text[i]);
1835 1835 value = IMM22(text[i]) << 10;
1836 1836
1837 1837 /*
1838 1838 * see if we already have a match with just the sethi
1839 1839 */
1840 1840 findsym_output(symlist, value, sym->st_value + i * 4);
1841 1841
1842 1842 /*
1843 1843 * search from the sethi on until we hit a relevant instr
1844 1844 */
1845 1845 for (j = i + 1; j < len; j++) {
1846 1846 if ((op = OP(text[j])) & OP_ARITH_MEM_MASK) {
1847 1847 op3 = OP3(text[j]);
1848 1848
1849 1849 if (RS1(text[j]) != rd)
1850 1850 goto instr_end;
1851 1851
1852 1852 /*
1853 1853 * This is a simple tool; we only deal
1854 1854 * with operations which take immediates
1855 1855 */
1856 1856 if (I(text[j]) == 0)
1857 1857 goto instr_end;
1858 1858
1859 1859 /*
1860 1860 * sign extend the immediate value
1861 1861 */
1862 1862 imm13 = IMM13(text[j]);
1863 1863 imm13 <<= 19;
1864 1864 imm13 >>= 19;
1865 1865
1866 1866 if (op == OP_ARITH) {
1867 1867 /* arithmetic operations */
1868 1868 if (op3 & OP3_COMPLEX_MASK)
1869 1869 goto instr_end;
1870 1870
1871 1871 switch (op3 & ~OP3_CC_MASK) {
1872 1872 case OP3_OR:
1873 1873 value |= imm13;
1874 1874 break;
1875 1875 case OP3_ADD:
1876 1876 value += imm13;
1877 1877 break;
1878 1878 case OP3_XOR:
1879 1879 value ^= imm13;
1880 1880 break;
1881 1881 default:
1882 1882 goto instr_end;
1883 1883 }
1884 1884 } else {
1885 1885 /* loads and stores */
1886 1886 /* op3 == OP_MEM */
1887 1887
1888 1888 value += imm13;
1889 1889 }
1890 1890
1891 1891 findsym_output(symlist, value,
1892 1892 sym->st_value + j * 4);
1893 1893 instr_end:
1894 1894 /*
1895 1895 * if we're clobbering rd, break
1896 1896 */
1897 1897 if (RD(text[j]) == rd)
1898 1898 break;
1899 1899 } else if (IS_SETHI(text[j])) {
1900 1900 if (RD(text[j]) == rd)
1901 1901 break;
1902 1902 } else if (OP(text[j]) == 1) {
1903 1903 /*
1904 1904 * see if a call clobbers an %o or %g
1905 1905 */
1906 1906 if (rd <= R_O7)
1907 1907 break;
1908 1908 }
1909 1909 }
1910 1910 }
1911 1911
1912 1912 return (0);
1913 1913 }
1914 1914
1915 1915 static int
1916 1916 cmd_findsym(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
1917 1917 {
1918 1918 uintptr_t *symlist;
1919 1919 uint_t optg = FALSE;
1920 1920 uint_t type;
1921 1921 int len, i;
1922 1922
1923 1923 i = mdb_getopts(argc, argv, 'g', MDB_OPT_SETBITS, TRUE, &optg, NULL);
1924 1924
1925 1925 argc -= i;
1926 1926 argv += i;
1927 1927
1928 1928 len = argc + ((flags & DCMD_ADDRSPEC) ? 1 : 0) + 1;
1929 1929
1930 1930 if (len <= 1)
1931 1931 return (DCMD_USAGE);
1932 1932
1933 1933 /*
1934 1934 * Set up a NULL-terminated symbol list, and then iterate over the
1935 1935 * symbol table, scanning each function for references to these symbols.
1936 1936 */
1937 1937 symlist = mdb_alloc(len * sizeof (uintptr_t), UM_SLEEP | UM_GC);
1938 1938 len = 0;
1939 1939
1940 1940 for (i = 0; i < argc; i++, argv++) {
1941 1941 const char *str = argv->a_un.a_str;
1942 1942 uintptr_t value;
1943 1943 GElf_Sym sym;
1944 1944
1945 1945 if (argv->a_type == MDB_TYPE_STRING) {
1946 1946 if (strchr("+-", str[0]) != NULL)
1947 1947 return (DCMD_USAGE);
1948 1948 else if (str[0] >= '0' && str[0] <= '9')
1949 1949 value = mdb_strtoull(str);
1950 1950 else if (mdb_lookup_by_name(str, &sym) != 0) {
1951 1951 mdb_warn("symbol '%s' not found", str);
1952 1952 return (DCMD_USAGE);
1953 1953 } else
1954 1954 value = sym.st_value;
1955 1955 } else
1956 1956 value = argv[i].a_un.a_val;
1957 1957
1958 1958 if (value != NULL)
1959 1959 symlist[len++] = value;
1960 1960 }
1961 1961
1962 1962 if (flags & DCMD_ADDRSPEC)
1963 1963 symlist[len++] = addr;
1964 1964
1965 1965 symlist[len] = NULL;
1966 1966
1967 1967 if (optg)
1968 1968 type = MDB_TGT_BIND_GLOBAL | MDB_TGT_TYPE_FUNC;
1969 1969 else
1970 1970 type = MDB_TGT_BIND_ANY | MDB_TGT_TYPE_FUNC;
1971 1971
1972 1972 if (mdb_tgt_symbol_iter(mdb.m_target, MDB_TGT_OBJ_EVERY,
1973 1973 MDB_TGT_SYMTAB, type, findsym_cb, symlist) == -1) {
1974 1974 mdb_warn("failed to iterate over symbol table");
1975 1975 return (DCMD_ERR);
1976 1976 }
1977 1977
1978 1978 return (DCMD_OK);
1979 1979 }
1980 1980 #endif /* __sparc */
1981 1981
1982 1982 static int
1983 1983 dis_str2addr(const char *s, uintptr_t *addr)
1984 1984 {
1985 1985 GElf_Sym sym;
1986 1986
1987 1987 if (s[0] >= '0' && s[0] <= '9') {
1988 1988 *addr = (uintptr_t)mdb_strtoull(s);
1989 1989 return (0);
1990 1990 }
1991 1991
1992 1992 if (mdb_tgt_lookup_by_name(mdb.m_target,
1993 1993 MDB_TGT_OBJ_EVERY, s, &sym, NULL) == -1) {
1994 1994 mdb_warn("symbol '%s' not found\n", s);
1995 1995 return (-1);
1996 1996 }
1997 1997
1998 1998 *addr = (uintptr_t)sym.st_value;
1999 1999 return (0);
2000 2000 }
2001 2001
2002 2002 static int
2003 2003 cmd_dis(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2004 2004 {
2005 2005 mdb_tgt_t *tgt = mdb.m_target;
2006 2006 mdb_disasm_t *dis = mdb.m_disasm;
2007 2007
2008 2008 uintptr_t oaddr, naddr;
2009 2009 mdb_tgt_as_t as;
2010 2010 mdb_tgt_status_t st;
2011 2011 char buf[BUFSIZ];
2012 2012 GElf_Sym sym;
2013 2013 int i;
2014 2014
2015 2015 uint_t opt_f = FALSE; /* File-mode off by default */
2016 2016 uint_t opt_w = FALSE; /* Window mode off by default */
2017 2017 uint_t opt_a = FALSE; /* Raw-address mode off by default */
2018 2018 uint_t opt_b = FALSE; /* Address & symbols off by default */
2019 2019 uintptr_t n = -1UL; /* Length of window in instructions */
2020 2020 uintptr_t eaddr = 0; /* Ending address; 0 if limited by n */
2021 2021
2022 2022 i = mdb_getopts(argc, argv,
2023 2023 'f', MDB_OPT_SETBITS, TRUE, &opt_f,
2024 2024 'w', MDB_OPT_SETBITS, TRUE, &opt_w,
2025 2025 'a', MDB_OPT_SETBITS, TRUE, &opt_a,
2026 2026 'b', MDB_OPT_SETBITS, TRUE, &opt_b,
2027 2027 'n', MDB_OPT_UINTPTR, &n, NULL);
2028 2028
2029 2029 /*
2030 2030 * Disgusting argument post-processing ... basically the idea is to get
2031 2031 * the target address into addr, which we do by using the specified
2032 2032 * expression value, looking up a string as a symbol name, or by
2033 2033 * using the address specified as dot.
2034 2034 */
2035 2035 if (i != argc) {
2036 2036 if (argc != 0 && (argc - i) == 1) {
2037 2037 if (argv[i].a_type == MDB_TYPE_STRING) {
2038 2038 if (argv[i].a_un.a_str[0] == '-')
2039 2039 return (DCMD_USAGE);
2040 2040
2041 2041 if (dis_str2addr(argv[i].a_un.a_str, &addr))
2042 2042 return (DCMD_ERR);
2043 2043 } else
2044 2044 addr = argv[i].a_un.a_val;
2045 2045 } else
2046 2046 return (DCMD_USAGE);
2047 2047 }
2048 2048
2049 2049 /*
2050 2050 * If we're not in window mode yet, and some type of arguments were
2051 2051 * specified, see if the address corresponds nicely to a function.
2052 2052 * If not, turn on window mode; otherwise disassemble the function.
2053 2053 */
2054 2054 if (opt_w == FALSE && (argc != i || (flags & DCMD_ADDRSPEC))) {
2055 2055 if (mdb_tgt_lookup_by_addr(tgt, addr,
2056 2056 MDB_TGT_SYM_EXACT, buf, sizeof (buf), &sym, NULL) == 0 &&
2057 2057 GELF_ST_TYPE(sym.st_info) == STT_FUNC) {
2058 2058 /*
2059 2059 * If the symbol has a size then set our end address to
2060 2060 * be the end of the function symbol we just located.
2061 2061 */
2062 2062 if (sym.st_size != 0)
2063 2063 eaddr = addr + (uintptr_t)sym.st_size;
2064 2064 } else
2065 2065 opt_w = TRUE;
2066 2066 }
2067 2067
2068 2068 /*
2069 2069 * Window-mode doesn't make sense in a loop.
2070 2070 */
2071 2071 if (flags & DCMD_LOOP)
2072 2072 opt_w = FALSE;
2073 2073
2074 2074 /*
2075 2075 * If -n was explicit, limit output to n instructions;
2076 2076 * otherwise set n to some reasonable default
2077 2077 */
2078 2078 if (n != -1UL)
2079 2079 eaddr = 0;
2080 2080 else
2081 2081 n = 10;
2082 2082
2083 2083 /*
2084 2084 * If the state is IDLE (i.e. no address space), turn on -f.
2085 2085 */
2086 2086 if (mdb_tgt_status(tgt, &st) == 0 && st.st_state == MDB_TGT_IDLE)
2087 2087 opt_f = TRUE;
2088 2088
2089 2089 if (opt_f)
2090 2090 as = MDB_TGT_AS_FILE;
2091 2091 else
2092 2092 as = MDB_TGT_AS_VIRT;
2093 2093
2094 2094 if (opt_w == FALSE) {
2095 2095 n++;
2096 2096 while ((eaddr == 0 && n-- != 0) || (addr < eaddr)) {
2097 2097 naddr = mdb_dis_ins2str(dis, tgt, as,
2098 2098 buf, sizeof (buf), addr);
2099 2099 if (naddr == addr)
2100 2100 return (DCMD_ERR);
2101 2101 if (opt_a)
2102 2102 mdb_printf("%-#32p%8T%s\n", addr, buf);
2103 2103 else if (opt_b)
2104 2104 mdb_printf("%-#?p %-#32a%8T%s\n",
2105 2105 addr, addr, buf);
2106 2106 else
2107 2107 mdb_printf("%-#32a%8T%s\n", addr, buf);
2108 2108 addr = naddr;
2109 2109 }
2110 2110
2111 2111 } else {
2112 2112 #ifdef __sparc
2113 2113 if (addr & 0x3) {
2114 2114 mdb_warn("address is not properly aligned\n");
2115 2115 return (DCMD_ERR);
2116 2116 }
2117 2117 #endif
2118 2118
2119 2119 for (oaddr = mdb_dis_previns(dis, tgt, as, addr, n);
2120 2120 oaddr < addr; oaddr = naddr) {
2121 2121 naddr = mdb_dis_ins2str(dis, tgt, as,
2122 2122 buf, sizeof (buf), oaddr);
2123 2123 if (naddr == oaddr)
2124 2124 return (DCMD_ERR);
2125 2125 if (opt_a)
2126 2126 mdb_printf("%-#32p%8T%s\n", oaddr, buf);
2127 2127 else if (opt_b)
2128 2128 mdb_printf("%-#?p %-#32a%8T%s\n",
2129 2129 oaddr, oaddr, buf);
2130 2130 else
2131 2131 mdb_printf("%-#32a%8T%s\n", oaddr, buf);
2132 2132 }
2133 2133
2134 2134 if ((naddr = mdb_dis_ins2str(dis, tgt, as,
2135 2135 buf, sizeof (buf), addr)) == addr)
2136 2136 return (DCMD_ERR);
2137 2137
2138 2138 mdb_printf("%<b>");
2139 2139 mdb_flush();
2140 2140 if (opt_a)
2141 2141 mdb_printf("%-#32p%8T%s%", addr, buf);
2142 2142 else if (opt_b)
2143 2143 mdb_printf("%-#?p %-#32a%8T%s", addr, addr, buf);
2144 2144 else
2145 2145 mdb_printf("%-#32a%8T%s%", addr, buf);
2146 2146 mdb_printf("%</b>\n");
2147 2147
2148 2148 for (addr = naddr; n-- != 0; addr = naddr) {
2149 2149 naddr = mdb_dis_ins2str(dis, tgt, as,
2150 2150 buf, sizeof (buf), addr);
2151 2151 if (naddr == addr)
2152 2152 return (DCMD_ERR);
2153 2153 if (opt_a)
2154 2154 mdb_printf("%-#32p%8T%s\n", addr, buf);
2155 2155 else if (opt_b)
2156 2156 mdb_printf("%-#?p %-#32a%8T%s\n",
2157 2157 addr, addr, buf);
2158 2158 else
2159 2159 mdb_printf("%-#32a%8T%s\n", addr, buf);
2160 2160 }
2161 2161 }
2162 2162
2163 2163 mdb_set_dot(addr);
2164 2164 return (DCMD_OK);
2165 2165 }
2166 2166
2167 2167 /*ARGSUSED*/
2168 2168 static int
2169 2169 walk_step(uintptr_t addr, const void *data, void *private)
2170 2170 {
2171 2171 mdb_printf("%#lr\n", addr);
2172 2172 return (WALK_NEXT);
2173 2173 }
2174 2174
2175 2175 static int
2176 2176 cmd_walk(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2177 2177 {
2178 2178 int status;
2179 2179
2180 2180 if (argc < 1 || argc > 2 || argv[0].a_type != MDB_TYPE_STRING ||
2181 2181 argv[argc - 1].a_type != MDB_TYPE_STRING)
2182 2182 return (DCMD_USAGE);
2183 2183
2184 2184 if (argc > 1) {
2185 2185 const char *name = argv[1].a_un.a_str;
2186 2186 mdb_var_t *v = mdb_nv_lookup(&mdb.m_nv, name);
2187 2187 const char *p;
2188 2188
2189 2189 if (v != NULL && (v->v_flags & MDB_NV_RDONLY) != 0) {
2190 2190 mdb_warn("variable %s is read-only\n", name);
2191 2191 return (DCMD_ABORT);
2192 2192 }
2193 2193
2194 2194 if (v == NULL && (p = strbadid(name)) != NULL) {
2195 2195 mdb_warn("'%c' may not be used in a variable "
2196 2196 "name\n", *p);
2197 2197 return (DCMD_ABORT);
2198 2198 }
2199 2199
2200 2200 if (v == NULL && (v = mdb_nv_insert(&mdb.m_nv,
2201 2201 name, NULL, 0, 0)) == NULL)
2202 2202 return (DCMD_ERR);
2203 2203
2204 2204 /*
2205 2205 * If there already exists a vcb for this variable, we may be
2206 2206 * calling ::walk in a loop. We only create a vcb for this
2207 2207 * variable on the first invocation.
2208 2208 */
2209 2209 if (mdb_vcb_find(v, mdb.m_frame) == NULL)
2210 2210 mdb_vcb_insert(mdb_vcb_create(v), mdb.m_frame);
2211 2211 }
2212 2212
2213 2213 if (flags & DCMD_ADDRSPEC)
2214 2214 status = mdb_pwalk(argv->a_un.a_str, walk_step, NULL, addr);
2215 2215 else
2216 2216 status = mdb_walk(argv->a_un.a_str, walk_step, NULL);
2217 2217
2218 2218 if (status == -1) {
2219 2219 mdb_warn("failed to perform walk");
2220 2220 return (DCMD_ERR);
2221 2221 }
2222 2222
2223 2223 return (DCMD_OK);
2224 2224 }
2225 2225
2226 2226 static int
2227 2227 cmd_walk_tab(mdb_tab_cookie_t *mcp, uint_t flags, int argc,
2228 2228 const mdb_arg_t *argv)
2229 2229 {
2230 2230 if (argc > 1)
2231 2231 return (1);
2232 2232
2233 2233 if (argc == 1) {
2234 2234 ASSERT(argv[0].a_type == MDB_TYPE_STRING);
2235 2235 return (mdb_tab_complete_walker(mcp, argv[0].a_un.a_str));
2236 2236 }
2237 2237
2238 2238 if (argc == 0 && flags & DCMD_TAB_SPACE)
2239 2239 return (mdb_tab_complete_walker(mcp, NULL));
2240 2240
2241 2241 return (1);
2242 2242 }
2243 2243
2244 2244 static ssize_t
2245 2245 mdb_partial_xread(void *buf, size_t nbytes, uintptr_t addr, void *arg)
2246 2246 {
2247 2247 ssize_t (*fp)(mdb_tgt_t *, const void *, size_t, uintptr_t) =
2248 2248 (ssize_t (*)(mdb_tgt_t *, const void *, size_t, uintptr_t))arg;
2249 2249
2250 2250 return (fp(mdb.m_target, buf, nbytes, addr));
2251 2251 }
2252 2252
2253 2253 /* ARGSUSED3 */
2254 2254 static ssize_t
2255 2255 mdb_partial_pread(void *buf, size_t nbytes, physaddr_t addr, void *arg)
2256 2256 {
2257 2257 return (mdb_tgt_pread(mdb.m_target, buf, nbytes, addr));
2258 2258 }
2259 2259
2260 2260
2261 2261 static int
2262 2262 cmd_dump(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2263 2263 {
2264 2264 uint_t dflags =
2265 2265 MDB_DUMP_ALIGN | MDB_DUMP_NEWDOT | MDB_DUMP_ASCII | MDB_DUMP_HEADER;
2266 2266 uint_t phys = FALSE;
2267 2267 uint_t file = FALSE;
2268 2268 uintptr_t group = 4;
2269 2269 uintptr_t length = 0;
2270 2270 uintptr_t width = 1;
2271 2271 mdb_tgt_status_t st;
2272 2272 int error;
2273 2273
2274 2274 if (mdb_getopts(argc, argv,
2275 2275 'e', MDB_OPT_SETBITS, MDB_DUMP_ENDIAN, &dflags,
2276 2276 'f', MDB_OPT_SETBITS, TRUE, &file,
2277 2277 'g', MDB_OPT_UINTPTR, &group,
2278 2278 'l', MDB_OPT_UINTPTR, &length,
2279 2279 'p', MDB_OPT_SETBITS, TRUE, &phys,
2280 2280 'q', MDB_OPT_CLRBITS, MDB_DUMP_ASCII, &dflags,
2281 2281 'r', MDB_OPT_SETBITS, MDB_DUMP_RELATIVE, &dflags,
2282 2282 's', MDB_OPT_SETBITS, MDB_DUMP_SQUISH, &dflags,
2283 2283 't', MDB_OPT_SETBITS, MDB_DUMP_TRIM, &dflags,
2284 2284 'u', MDB_OPT_CLRBITS, MDB_DUMP_ALIGN, &dflags,
2285 2285 'v', MDB_OPT_SETBITS, MDB_DUMP_PEDANT, &dflags,
2286 2286 'w', MDB_OPT_UINTPTR, &width, NULL) != argc)
2287 2287 return (DCMD_USAGE);
2288 2288
2289 2289 if ((phys && file) ||
2290 2290 (width == 0) || (width > 0x10) ||
2291 2291 (group == 0) || (group > 0x100) ||
2292 2292 (mdb.m_dcount > 1 && length > 0))
2293 2293 return (DCMD_USAGE);
2294 2294 if (length == 0)
2295 2295 length = mdb.m_dcount;
2296 2296
2297 2297 /*
2298 2298 * If neither -f nor -p were specified and the state is IDLE (i.e. no
2299 2299 * address space), turn on -p. This is so we can read large files.
2300 2300 */
2301 2301 if (phys == FALSE && file == FALSE && mdb_tgt_status(mdb.m_target,
2302 2302 &st) == 0 && st.st_state == MDB_TGT_IDLE)
2303 2303 phys = TRUE;
2304 2304
2305 2305 dflags |= MDB_DUMP_GROUP(group) | MDB_DUMP_WIDTH(width);
2306 2306 if (phys)
2307 2307 error = mdb_dump64(mdb_get_dot(), length, dflags,
2308 2308 mdb_partial_pread, NULL);
2309 2309 else if (file)
2310 2310 error = mdb_dumpptr(addr, length, dflags,
2311 2311 mdb_partial_xread, (void *)mdb_tgt_fread);
2312 2312 else
2313 2313 error = mdb_dumpptr(addr, length, dflags,
2314 2314 mdb_partial_xread, (void *)mdb_tgt_vread);
2315 2315
2316 2316 return (((flags & DCMD_LOOP) || (error == -1)) ? DCMD_ABORT : DCMD_OK);
2317 2317 }
2318 2318
2319 2319 /*ARGSUSED*/
2320 2320 static int
2321 2321 cmd_echo(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2322 2322 {
2323 2323 if (flags & DCMD_ADDRSPEC)
2324 2324 return (DCMD_USAGE);
2325 2325
2326 2326 for (; argc-- != 0; argv++) {
2327 2327 if (argv->a_type == MDB_TYPE_STRING)
2328 2328 mdb_printf("%s ", argv->a_un.a_str);
2329 2329 else
2330 2330 mdb_printf("%llr ", argv->a_un.a_val);
2331 2331 }
2332 2332
2333 2333 mdb_printf("\n");
2334 2334 return (DCMD_OK);
2335 2335 }
2336 2336
2337 2337 /*ARGSUSED*/
2338 2338 static int
2339 2339 cmd_head(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2340 2340 {
2341 2341 uint64_t cnt = 10;
2342 2342 const char *c;
2343 2343 mdb_pipe_t p;
2344 2344
2345 2345 if (!flags & DCMD_PIPE)
2346 2346 return (DCMD_USAGE);
2347 2347
2348 2348 if (argc == 1 || argc == 2) {
2349 2349 const char *num;
2350 2350
2351 2351 if (argc == 1) {
2352 2352 if (argv[0].a_type != MDB_TYPE_STRING ||
2353 2353 *argv[0].a_un.a_str != '-')
2354 2354 return (DCMD_USAGE);
2355 2355
2356 2356 num = argv[0].a_un.a_str + 1;
2357 2357
2358 2358 } else {
2359 2359 if (argv[0].a_type != MDB_TYPE_STRING ||
2360 2360 strcmp(argv[0].a_un.a_str, "-n") != 0)
2361 2361 return (DCMD_USAGE);
2362 2362
2363 2363 num = argv[1].a_un.a_str;
2364 2364 }
2365 2365
2366 2366 for (cnt = 0, c = num; *c != '\0' && isdigit(*c); c++)
2367 2367 cnt = cnt * 10 + (*c - '0');
2368 2368
2369 2369 if (*c != '\0')
2370 2370 return (DCMD_USAGE);
2371 2371
2372 2372 } else if (argc != 0) {
2373 2373 return (DCMD_USAGE);
2374 2374 }
2375 2375
2376 2376 mdb_get_pipe(&p);
2377 2377
2378 2378 if (p.pipe_data == NULL)
2379 2379 return (DCMD_OK);
2380 2380 p.pipe_len = MIN(p.pipe_len, cnt);
2381 2381
2382 2382 if (flags & DCMD_PIPE_OUT) {
2383 2383 mdb_set_pipe(&p);
2384 2384 } else {
2385 2385 while (p.pipe_len-- > 0)
2386 2386 mdb_printf("%lx\n", *p.pipe_data++);
2387 2387 }
2388 2388
2389 2389 return (DCMD_OK);
2390 2390 }
2391 2391
2392 2392 static void
2393 2393 head_help(void)
2394 2394 {
2395 2395 mdb_printf(
2396 2396 "-n num\n or\n"
2397 2397 "-num pass only the first `num' elements in the pipe.\n"
2398 2398 "\n%<b>Note:%</b> `num' is a decimal number.\n");
2399 2399 }
2400 2400
2401 2401 static int
2402 2402 cmd_typeset(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2403 2403 {
2404 2404 int add_tag = 0, del_tag = 0;
2405 2405 const char *p;
2406 2406 mdb_var_t *v;
2407 2407
2408 2408 if (argc == 0)
2409 2409 return (cmd_vars(addr, flags, argc, argv));
2410 2410
2411 2411 if (argv->a_type == MDB_TYPE_STRING && (argv->a_un.a_str[0] == '-' ||
2412 2412 argv->a_un.a_str[0] == '+')) {
2413 2413 if (argv->a_un.a_str[1] != 't')
2414 2414 return (DCMD_USAGE);
2415 2415 if (argv->a_un.a_str[0] == '-')
2416 2416 add_tag++;
2417 2417 else
2418 2418 del_tag++;
2419 2419 argc--;
2420 2420 argv++;
2421 2421 }
2422 2422
2423 2423 if (!(flags & DCMD_ADDRSPEC))
2424 2424 addr = 0; /* set variables to zero unless explicit addr given */
2425 2425
2426 2426 for (; argc-- != 0; argv++) {
2427 2427 if (argv->a_type != MDB_TYPE_STRING)
2428 2428 continue;
2429 2429
2430 2430 if (argv->a_un.a_str[0] == '-' || argv->a_un.a_str[0] == '+') {
2431 2431 mdb_warn("ignored bad option -- %s\n",
2432 2432 argv->a_un.a_str);
2433 2433 continue;
2434 2434 }
2435 2435
2436 2436 if ((p = strbadid(argv->a_un.a_str)) != NULL) {
2437 2437 mdb_warn("'%c' may not be used in a variable "
2438 2438 "name\n", *p);
2439 2439 return (DCMD_ERR);
2440 2440 }
2441 2441
2442 2442 if ((v = mdb_nv_lookup(&mdb.m_nv, argv->a_un.a_str)) == NULL) {
2443 2443 v = mdb_nv_insert(&mdb.m_nv, argv->a_un.a_str,
2444 2444 NULL, addr, 0);
2445 2445 } else if (flags & DCMD_ADDRSPEC)
2446 2446 mdb_nv_set_value(v, addr);
2447 2447
2448 2448 if (v != NULL) {
2449 2449 if (add_tag)
2450 2450 v->v_flags |= MDB_NV_TAGGED;
2451 2451 if (del_tag)
2452 2452 v->v_flags &= ~MDB_NV_TAGGED;
2453 2453 }
2454 2454 }
2455 2455
2456 2456 return (DCMD_OK);
2457 2457 }
2458 2458
2459 2459 #ifndef _KMDB
2460 2460 /*ARGSUSED*/
2461 2461 static int
2462 2462 cmd_context(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2463 2463 {
2464 2464 if (argc != 0 || !(flags & DCMD_ADDRSPEC))
2465 2465 return (DCMD_USAGE);
2466 2466
2467 2467 if (mdb_tgt_setcontext(mdb.m_target, (void *)addr) == 0)
2468 2468 return (DCMD_OK);
2469 2469
2470 2470 return (DCMD_ERR);
2471 2471 }
2472 2472 #endif
2473 2473
2474 2474 /*ARGSUSED*/
2475 2475 static int
2476 2476 cmd_prompt(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2477 2477 {
2478 2478 const char *p = "";
2479 2479
2480 2480 if (argc != 0) {
2481 2481 if (argc > 1 || argv->a_type != MDB_TYPE_STRING)
2482 2482 return (DCMD_USAGE);
2483 2483 p = argv->a_un.a_str;
2484 2484 }
2485 2485
2486 2486 (void) mdb_set_prompt(p);
2487 2487 return (DCMD_OK);
2488 2488 }
2489 2489
2490 2490 /*ARGSUSED*/
2491 2491 static int
2492 2492 cmd_term(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2493 2493 {
2494 2494 mdb_printf("%s\n", mdb.m_termtype);
2495 2495
2496 2496 return (DCMD_OK);
2497 2497 }
2498 2498
2499 2499 /*ARGSUSED*/
2500 2500 static int
2501 2501 cmd_vtop(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2502 2502 {
2503 2503 physaddr_t pa;
2504 2504 mdb_tgt_as_t as = MDB_TGT_AS_VIRT;
2505 2505
2506 2506 if (mdb_getopts(argc, argv, 'a', MDB_OPT_UINTPTR, (uintptr_t *)&as,
2507 2507 NULL) != argc)
2508 2508 return (DCMD_USAGE);
2509 2509
2510 2510 if (mdb_tgt_vtop(mdb.m_target, as, addr, &pa) == -1) {
2511 2511 mdb_warn("failed to get physical mapping");
2512 2512 return (DCMD_ERR);
2513 2513 }
2514 2514
2515 2515 if (flags & DCMD_PIPE_OUT)
2516 2516 mdb_printf("%llr\n", pa);
2517 2517 else
2518 2518 mdb_printf("virtual %lr mapped to physical %llr\n", addr, pa);
2519 2519 return (DCMD_OK);
2520 2520 }
2521 2521
2522 2522 #define EVENTS_OPT_A 0x1 /* ::events -a (show all events) */
2523 2523 #define EVENTS_OPT_V 0x2 /* ::events -v (verbose display) */
2524 2524
2525 2525 static const char *
2526 2526 event_action(const mdb_tgt_spec_desc_t *sp)
2527 2527 {
2528 2528 if (!(sp->spec_flags & MDB_TGT_SPEC_HIDDEN) && sp->spec_data != NULL)
2529 2529 return (sp->spec_data);
2530 2530
2531 2531 return ("-");
2532 2532 }
2533 2533
2534 2534 static void
2535 2535 print_evsep(void)
2536 2536 {
2537 2537 static const char dash20[] = "--------------------";
2538 2538 mdb_printf("----- - -- -- -- %s%s --%s\n", dash20, dash20, dash20);
2539 2539 }
2540 2540
2541 2541 /*ARGSUSED*/
2542 2542 static int
2543 2543 print_event(mdb_tgt_t *t, void *private, int vid, void *data)
2544 2544 {
2545 2545 uint_t opts = (uint_t)(uintptr_t)private;
2546 2546 mdb_tgt_spec_desc_t sp;
2547 2547 char s1[41], s2[22];
2548 2548 const char *s2str;
2549 2549 int visible;
2550 2550
2551 2551 (void) mdb_tgt_vespec_info(t, vid, &sp, s1, sizeof (s1));
2552 2552 visible = !(sp.spec_flags & (MDB_TGT_SPEC_HIDDEN|MDB_TGT_SPEC_DELETED));
2553 2553
2554 2554 if ((opts & EVENTS_OPT_A) || visible) {
2555 2555 int encoding = (!(sp.spec_flags & MDB_TGT_SPEC_DISABLED)) |
2556 2556 (!(sp.spec_flags & MDB_TGT_SPEC_MATCHED) << 1);
2557 2557
2558 2558 char ldelim = "<<(["[encoding];
2559 2559 char rdelim = ">>)]"[encoding];
2560 2560
2561 2561 char state = "0-+*!"[sp.spec_state];
2562 2562
2563 2563 char tflag = "T "[!(sp.spec_flags & MDB_TGT_SPEC_STICKY)];
2564 2564 char aflag = "d "[!(sp.spec_flags & MDB_TGT_SPEC_AUTODIS)];
2565 2565
2566 2566 if (sp.spec_flags & MDB_TGT_SPEC_TEMPORARY)
2567 2567 tflag = 't'; /* TEMP takes precedence over STICKY */
2568 2568 if (sp.spec_flags & MDB_TGT_SPEC_AUTODEL)
2569 2569 aflag = 'D'; /* AUTODEL takes precedence over AUTODIS */
2570 2570 if (sp.spec_flags & MDB_TGT_SPEC_AUTOSTOP)
2571 2571 aflag = 's'; /* AUTOSTOP takes precedence over both */
2572 2572
2573 2573 if (opts & EVENTS_OPT_V) {
2574 2574 if (sp.spec_state == MDB_TGT_SPEC_IDLE ||
2575 2575 sp.spec_state == MDB_TGT_SPEC_ERROR)
2576 2576 s2str = mdb_strerror(sp.spec_errno);
2577 2577 else
2578 2578 s2str = "-";
2579 2579 } else
2580 2580 s2str = event_action(&sp);
2581 2581
2582 2582 if (mdb_snprintf(s2, sizeof (s2), "%s", s2str) >= sizeof (s2))
2583 2583 (void) strabbr(s2, sizeof (s2));
2584 2584
2585 2585 if (vid > -10 && vid < 10)
2586 2586 mdb_printf("%c%2d %c", ldelim, vid, rdelim);
2587 2587 else
2588 2588 mdb_printf("%c%3d%c", ldelim, vid, rdelim);
2589 2589
2590 2590 mdb_printf(" %c %c%c %2u %2u %-40s %-21s\n",
2591 2591 state, tflag, aflag, sp.spec_hits, sp.spec_limit, s1, s2);
2592 2592
2593 2593 if (opts & EVENTS_OPT_V) {
2594 2594 mdb_printf("%-17s%s\n", "", event_action(&sp));
2595 2595 print_evsep();
2596 2596 }
2597 2597 }
2598 2598
2599 2599 return (0);
2600 2600 }
2601 2601
2602 2602 /*ARGSUSED*/
2603 2603 static int
2604 2604 cmd_events(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2605 2605 {
2606 2606 uint_t opts = 0;
2607 2607
2608 2608 if ((flags & DCMD_ADDRSPEC) || mdb_getopts(argc, argv,
2609 2609 'a', MDB_OPT_SETBITS, EVENTS_OPT_A, &opts,
2610 2610 'v', MDB_OPT_SETBITS, EVENTS_OPT_V, &opts, NULL) != argc)
2611 2611 return (DCMD_USAGE);
2612 2612
2613 2613
2614 2614 if (opts & EVENTS_OPT_V) {
2615 2615 mdb_printf(" ID S TA HT LM %-40s %-21s\n%-17s%s\n",
2616 2616 "Description", "Status", "", "Action");
2617 2617 } else {
2618 2618 mdb_printf(" ID S TA HT LM %-40s %-21s\n",
2619 2619 "Description", "Action");
2620 2620 }
2621 2621
2622 2622 print_evsep();
2623 2623 return (mdb_tgt_vespec_iter(mdb.m_target, print_event,
2624 2624 (void *)(uintptr_t)opts));
2625 2625 }
2626 2626
2627 2627 static int
2628 2628 tgt_status(const mdb_tgt_status_t *tsp)
2629 2629 {
2630 2630 const char *format;
2631 2631 char buf[BUFSIZ];
2632 2632
2633 2633 if (tsp->st_flags & MDB_TGT_BUSY)
2634 2634 return (DCMD_OK);
2635 2635
2636 2636 if (tsp->st_pc != 0) {
2637 2637 if (mdb_dis_ins2str(mdb.m_disasm, mdb.m_target, MDB_TGT_AS_VIRT,
2638 2638 buf, sizeof (buf), tsp->st_pc) != tsp->st_pc)
2639 2639 format = "target stopped at:\n%-#16a%8T%s\n";
2640 2640 else
2641 2641 format = "target stopped at %a:\n";
2642 2642 mdb_warn(format, tsp->st_pc, buf);
2643 2643 }
2644 2644
2645 2645 switch (tsp->st_state) {
2646 2646 case MDB_TGT_IDLE:
2647 2647 mdb_warn("target is idle\n");
2648 2648 break;
2649 2649 case MDB_TGT_RUNNING:
2650 2650 if (tsp->st_flags & MDB_TGT_DSTOP)
2651 2651 mdb_warn("target is running, stop directive pending\n");
2652 2652 else
2653 2653 mdb_warn("target is running\n");
2654 2654 break;
2655 2655 case MDB_TGT_STOPPED:
2656 2656 if (tsp->st_pc == 0)
2657 2657 mdb_warn("target is stopped\n");
2658 2658 break;
2659 2659 case MDB_TGT_UNDEAD:
2660 2660 mdb_warn("target has terminated\n");
2661 2661 break;
2662 2662 case MDB_TGT_DEAD:
2663 2663 mdb_warn("target is a core dump\n");
2664 2664 break;
2665 2665 case MDB_TGT_LOST:
2666 2666 mdb_warn("target is no longer under debugger control\n");
2667 2667 break;
2668 2668 }
2669 2669
2670 2670 mdb_set_dot(tsp->st_pc);
2671 2671 return (DCMD_OK);
2672 2672 }
2673 2673
2674 2674 /*
2675 2675 * mdb continue/step commands take an optional signal argument, but the
2676 2676 * corresponding kmdb versions don't.
2677 2677 */
2678 2678 #ifdef _KMDB
2679 2679 #define CONT_MAXARGS 0 /* no optional SIG argument */
2680 2680 #else
2681 2681 #define CONT_MAXARGS 1
2682 2682 #endif
2683 2683
2684 2684 /*ARGSUSED*/
2685 2685 static int
2686 2686 cmd_cont_common(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv,
2687 2687 int (*t_cont)(mdb_tgt_t *, mdb_tgt_status_t *), const char *name)
2688 2688 {
2689 2689 mdb_tgt_t *t = mdb.m_target;
2690 2690 mdb_tgt_status_t st;
2691 2691 int sig = 0;
2692 2692
2693 2693 if ((flags & DCMD_ADDRSPEC) || argc > CONT_MAXARGS)
2694 2694 return (DCMD_USAGE);
2695 2695
2696 2696 if (argc > 0) {
2697 2697 if (argv->a_type == MDB_TYPE_STRING) {
2698 2698 if (proc_str2sig(argv->a_un.a_str, &sig) == -1) {
2699 2699 mdb_warn("invalid signal name -- %s\n",
2700 2700 argv->a_un.a_str);
2701 2701 return (DCMD_USAGE);
2702 2702 }
2703 2703 } else
2704 2704 sig = (int)(intmax_t)argv->a_un.a_val;
2705 2705 }
2706 2706
2707 2707 (void) mdb_tgt_status(t, &st);
2708 2708
2709 2709 if (st.st_state == MDB_TGT_IDLE && mdb_tgt_run(t, 0, NULL) == -1) {
2710 2710 if (errno != EMDB_TGT)
2711 2711 mdb_warn("failed to create new target");
2712 2712 return (DCMD_ERR);
2713 2713 }
2714 2714
2715 2715 if (sig != 0 && mdb_tgt_signal(t, sig) == -1) {
2716 2716 mdb_warn("failed to post signal %d", sig);
2717 2717 return (DCMD_ERR);
2718 2718 }
2719 2719
2720 2720 if (st.st_state == MDB_TGT_IDLE && t_cont == &mdb_tgt_step) {
2721 2721 (void) mdb_tgt_status(t, &st);
2722 2722 return (tgt_status(&st));
2723 2723 }
2724 2724
2725 2725 if (t_cont(t, &st) == -1) {
2726 2726 if (errno != EMDB_TGT)
2727 2727 mdb_warn("failed to %s target", name);
2728 2728 return (DCMD_ERR);
2729 2729 }
2730 2730
2731 2731 return (tgt_status(&st));
2732 2732 }
2733 2733
2734 2734 static int
2735 2735 cmd_step(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
↓ open down ↓ |
2696 lines elided |
↑ open up ↑ |
2736 2736 {
2737 2737 int (*func)(mdb_tgt_t *, mdb_tgt_status_t *) = &mdb_tgt_step;
2738 2738 const char *name = "single-step";
2739 2739
2740 2740 if (argc > 0 && argv->a_type == MDB_TYPE_STRING) {
2741 2741 if (strcmp(argv->a_un.a_str, "out") == 0) {
2742 2742 func = &mdb_tgt_step_out;
2743 2743 name = "step (out)";
2744 2744 argv++;
2745 2745 argc--;
2746 - } else if (strcmp(argv->a_un.a_str, "branch") == 0) {
2747 - func = &mdb_tgt_step_branch;
2748 - name = "step (branch)";
2749 - argv++;
2750 - argc--;
2751 2746 } else if (strcmp(argv->a_un.a_str, "over") == 0) {
2752 2747 func = &mdb_tgt_next;
2753 2748 name = "step (over)";
2754 2749 argv++;
2755 2750 argc--;
2756 2751 }
2757 2752 }
2758 2753
2759 2754 return (cmd_cont_common(addr, flags, argc, argv, func, name));
2760 2755 }
2761 2756
2762 2757 static int
2763 2758 cmd_step_out(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2764 2759 {
2765 2760 return (cmd_cont_common(addr, flags, argc, argv,
2766 2761 &mdb_tgt_step_out, "step (out)"));
2767 2762 }
2768 2763
2769 2764 static int
2770 2765 cmd_next(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2771 2766 {
2772 2767 return (cmd_cont_common(addr, flags, argc, argv,
2773 2768 &mdb_tgt_next, "step (over)"));
2774 2769 }
2775 2770
2776 2771 static int
2777 2772 cmd_cont(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2778 2773 {
2779 2774 return (cmd_cont_common(addr, flags, argc, argv,
2780 2775 &mdb_tgt_continue, "continue"));
2781 2776 }
2782 2777
2783 2778 #ifndef _KMDB
2784 2779 /*ARGSUSED*/
2785 2780 static int
2786 2781 cmd_run(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2787 2782 {
2788 2783 if (flags & DCMD_ADDRSPEC)
2789 2784 return (DCMD_USAGE);
2790 2785
2791 2786 if (mdb_tgt_run(mdb.m_target, argc, argv) == -1) {
2792 2787 if (errno != EMDB_TGT)
2793 2788 mdb_warn("failed to create new target");
2794 2789 return (DCMD_ERR);
2795 2790 }
2796 2791 return (cmd_cont(NULL, 0, 0, NULL));
2797 2792 }
2798 2793 #endif
2799 2794
2800 2795 /*
2801 2796 * To simplify the implementation of :d, :z, and ::delete, we use the sp
2802 2797 * parameter to store the criteria for what to delete. If spec_base is set,
2803 2798 * we delete vespecs with a matching address. If spec_id is set, we delete
2804 2799 * vespecs with a matching id. Otherwise, we delete all vespecs. We bump
2805 2800 * sp->spec_size so the caller can tell how many vespecs were deleted.
2806 2801 */
2807 2802 static int
2808 2803 ve_delete(mdb_tgt_t *t, mdb_tgt_spec_desc_t *sp, int vid, void *data)
2809 2804 {
2810 2805 mdb_tgt_spec_desc_t spec;
2811 2806 int status = -1;
2812 2807
2813 2808 if (vid < 0)
2814 2809 return (0); /* skip over target implementation events */
2815 2810
2816 2811 if (sp->spec_base != NULL) {
2817 2812 (void) mdb_tgt_vespec_info(t, vid, &spec, NULL, 0);
2818 2813 if (sp->spec_base - spec.spec_base < spec.spec_size)
2819 2814 status = mdb_tgt_vespec_delete(t, vid);
2820 2815 } else if (sp->spec_id == 0) {
2821 2816 (void) mdb_tgt_vespec_info(t, vid, &spec, NULL, 0);
2822 2817 if (!(spec.spec_flags & MDB_TGT_SPEC_STICKY))
2823 2818 status = mdb_tgt_vespec_delete(t, vid);
2824 2819 } else if (sp->spec_id == vid)
2825 2820 status = mdb_tgt_vespec_delete(t, vid);
2826 2821
2827 2822 if (status == 0) {
2828 2823 if (data != NULL)
2829 2824 strfree(data);
2830 2825 sp->spec_size++;
2831 2826 }
2832 2827
2833 2828 return (0);
2834 2829 }
2835 2830
2836 2831 static int
2837 2832 ve_delete_spec(mdb_tgt_spec_desc_t *sp)
2838 2833 {
2839 2834 (void) mdb_tgt_vespec_iter(mdb.m_target,
2840 2835 (mdb_tgt_vespec_f *)ve_delete, sp);
2841 2836
2842 2837 if (sp->spec_size == 0) {
2843 2838 if (sp->spec_id != 0 || sp->spec_base != NULL)
2844 2839 mdb_warn("no traced events matched description\n");
2845 2840 }
2846 2841
2847 2842 return (DCMD_OK);
2848 2843 }
2849 2844
2850 2845 /*ARGSUSED*/
2851 2846 static int
2852 2847 cmd_zapall(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2853 2848 {
2854 2849 mdb_tgt_spec_desc_t spec;
2855 2850
2856 2851 if ((flags & DCMD_ADDRSPEC) || argc != 0)
2857 2852 return (DCMD_USAGE);
2858 2853
2859 2854 bzero(&spec, sizeof (spec));
2860 2855 return (ve_delete_spec(&spec));
2861 2856 }
2862 2857
2863 2858 static int
2864 2859 cmd_delete(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2865 2860 {
2866 2861 mdb_tgt_spec_desc_t spec;
2867 2862
2868 2863 if (((flags & DCMD_ADDRSPEC) && argc > 0) || argc > 1)
2869 2864 return (DCMD_USAGE);
2870 2865
2871 2866 bzero(&spec, sizeof (spec));
2872 2867
2873 2868 if (flags & DCMD_ADDRSPEC)
2874 2869 spec.spec_base = addr;
2875 2870 else if (argc == 0)
2876 2871 spec.spec_base = mdb_get_dot();
2877 2872 else if (argv->a_type == MDB_TYPE_STRING &&
2878 2873 strcmp(argv->a_un.a_str, "all") != 0)
2879 2874 spec.spec_id = (int)(intmax_t)mdb_strtonum(argv->a_un.a_str,
2880 2875 10);
2881 2876 else if (argv->a_type == MDB_TYPE_IMMEDIATE)
2882 2877 spec.spec_id = (int)(intmax_t)argv->a_un.a_val;
2883 2878
2884 2879 return (ve_delete_spec(&spec));
2885 2880 }
2886 2881
2887 2882 static int
2888 2883 cmd_write(uintptr_t addr, uint_t flags, int argc, const mdb_arg_t *argv)
2889 2884 {
2890 2885 mdb_tgt_as_t as;
2891 2886 int rdback = mdb.m_flags & MDB_FL_READBACK;
2892 2887 mdb_tgt_addr_t naddr;
2893 2888 size_t forced_size = 0;
2894 2889 boolean_t opt_p, opt_o, opt_l;
2895 2890 uint64_t val = 0;
2896 2891 int i;
2897 2892
2898 2893 opt_p = opt_o = opt_l = B_FALSE;
2899 2894
2900 2895 i = mdb_getopts(argc, argv,
2901 2896 'p', MDB_OPT_SETBITS, B_TRUE, &opt_p,
2902 2897 'o', MDB_OPT_SETBITS, B_TRUE, &opt_o,
2903 2898 'l', MDB_OPT_UINTPTR_SET, &opt_l, (uintptr_t *)&forced_size, NULL);
2904 2899
2905 2900 if (!(flags & DCMD_ADDRSPEC))
2906 2901 return (DCMD_USAGE);
2907 2902
2908 2903 if (opt_p && opt_o) {
2909 2904 mdb_warn("-o and -p are incompatible\n");
2910 2905 return (DCMD_USAGE);
2911 2906 }
2912 2907
2913 2908 argc -= i;
2914 2909 argv += i;
2915 2910
2916 2911 if (argc == 0)
2917 2912 return (DCMD_USAGE);
2918 2913
2919 2914 switch (argv[0].a_type) {
2920 2915 case MDB_TYPE_STRING:
2921 2916 val = mdb_strtoull(argv[0].a_un.a_str);
2922 2917 break;
2923 2918 case MDB_TYPE_IMMEDIATE:
2924 2919 val = argv[0].a_un.a_val;
2925 2920 break;
2926 2921 default:
2927 2922 return (DCMD_USAGE);
2928 2923 }
2929 2924
2930 2925 if (opt_p)
2931 2926 as = MDB_TGT_AS_PHYS;
2932 2927 else if (opt_o)
2933 2928 as = MDB_TGT_AS_FILE;
2934 2929 else
2935 2930 as = MDB_TGT_AS_VIRT;
2936 2931
2937 2932 if (opt_l)
2938 2933 naddr = write_var_uint(as, addr, val, forced_size, rdback);
2939 2934 else
2940 2935 naddr = write_ctf_uint(as, addr, val, rdback);
2941 2936
2942 2937 if (addr == naddr) {
2943 2938 mdb_warn("failed to write %llr at address %#llx", val, addr);
2944 2939 return (DCMD_ERR);
2945 2940 }
2946 2941
2947 2942 return (DCMD_OK);
2948 2943 }
2949 2944
2950 2945 void
2951 2946 write_help(void)
2952 2947 {
2953 2948 mdb_printf(
2954 2949 "-l length force a write with the specified length in bytes\n"
2955 2950 "-o write data to the object file location specified\n"
2956 2951 "-p write data to the physical address specified\n"
2957 2952 "\n"
2958 2953 "Attempts to write the given value to the address provided.\n"
2959 2954 "If -l is not specified, the address must be the position of a\n"
2960 2955 "symbol that is either of integer, pointer, or enum type. The\n"
2961 2956 "type and the size of the symbol are inferred by the CTF found\n"
2962 2957 "in the provided address. The length of the write is guaranteed\n"
2963 2958 "to be the inferred size of the symbol.\n"
2964 2959 "\n"
2965 2960 "If no CTF data exists, or the address provided is not a symbol\n"
2966 2961 "of integer or pointer type, then the write fails. At that point\n"
2967 2962 "the user can force the write by using the '-l' option and\n"
2968 2963 "specifying its length.\n"
2969 2964 "\n"
2970 2965 "Note that forced writes with a length that are bigger than\n"
2971 2966 "the size of the biggest data pointer supported are not allowed."
2972 2967 "\n");
2973 2968 }
2974 2969
2975 2970 static void
2976 2971 srcexec_file_help(void)
2977 2972 {
2978 2973 mdb_printf(
2979 2974 "The library of macros delivered with previous versions of Solaris have been\n"
2980 2975 "superseded by the dcmds and walkers provided by MDB. See ::help for\n"
2981 2976 "commands that can be used to list the available dcmds and walkers.\n"
2982 2977 "\n"
2983 2978 "Aliases have been created for several of the more popular macros. To see\n"
2984 2979 "the list of aliased macros, as well as their native MDB equivalents,\n"
2985 2980 "type $M.\n");
2986 2981
2987 2982 #ifdef _KMDB
2988 2983 mdb_printf(
2989 2984 "When invoked, the $< and $<< dcmds will consult the macro alias list. If an\n"
2990 2985 "alias cannot be found, an attempt will be made to locate a data type whose\n"
2991 2986 "name corresponds to the requested macro. If such a type can be found, it\n"
2992 2987 "will be displayed using the ::print dcmd.\n");
2993 2988 #else
2994 2989 mdb_printf(
2995 2990 "When invoked, the $< and $<< dcmds will first attempt to locate a macro with\n"
2996 2991 "the indicated name. If no macro can be found, and if no alias exists for\n"
2997 2992 "this macro, an attempt will be made to locate a data type whose name\n"
2998 2993 "corresponds to the requested macro. If such a type can be found, it will be\n"
2999 2994 "displayed using the ::print dcmd.\n");
3000 2995 #endif
3001 2996 }
3002 2997
3003 2998 static void
3004 2999 events_help(void)
3005 3000 {
3006 3001 mdb_printf("Options:\n"
3007 3002 "-a show all events, including internal debugger events\n"
3008 3003 "-v show verbose display, including inactivity reason\n"
3009 3004 "\nOutput Columns:\n"
3010 3005 "ID decimal event specifier id number:\n"
3011 3006 " [ ] event tracing is enabled\n"
3012 3007 " ( ) event tracing is disabled\n"
3013 3008 " < > target is currently stopped on this type of event\n\n"
3014 3009 "S event specifier state:\n"
3015 3010 " - event specifier is idle (not applicable yet)\n"
3016 3011 " + event specifier is active\n"
3017 3012 " * event specifier is armed (target program running)\n"
3018 3013 " ! error occurred while attempting to arm event\n\n"
3019 3014 "TA event specifier flags:\n"
3020 3015 " t event specifier is temporary (delete at next stop)\n"
3021 3016 " T event specifier is sticky (::delete all has no effect)\n"
3022 3017 " d event specifier will be disabled when HT = LM\n"
3023 3018 " D event specifier will be deleted when HT = LM\n"
3024 3019 " s target will automatically stop when HT = LM\n\n"
3025 3020 "HT hit count (number of times event has occurred)\n"
3026 3021 "LM hit limit (limit for autostop, disable, delete)\n");
3027 3022 }
3028 3023
3029 3024 static void
3030 3025 dump_help(void)
3031 3026 {
3032 3027 mdb_printf(
3033 3028 "-e adjust for endianness\n"
3034 3029 " (assumes 4-byte words; use -g to change word size)\n"
3035 3030 #ifdef _KMDB
3036 3031 "-f no effect\n"
3037 3032 #else
3038 3033 "-f dump from object file\n"
3039 3034 #endif
3040 3035 "-g n display bytes in groups of n\n"
3041 3036 " (default is 4; n must be a power of 2, divide line width)\n"
3042 3037 "-l n display n bytes\n"
3043 3038 " (default is 1; rounded up to multiple of line width)\n"
3044 3039 "-p dump from physical memory\n"
3045 3040 "-q don't print ASCII\n"
3046 3041 "-r use relative numbering (automatically sets -u)\n"
3047 3042 "-s elide repeated lines\n"
3048 3043 "-t only read from and display contents of specified addresses\n"
3049 3044 " (default is to read and print entire lines)\n"
3050 3045 "-u un-align output\n"
3051 3046 " (default is to align output at paragraph boundary)\n"
3052 3047 "-w n display n 16-byte paragraphs per line\n"
3053 3048 " (default is 1, maximum is 16)\n");
3054 3049 }
3055 3050
3056 3051 /*
3057 3052 * Table of built-in dcmds associated with the root 'mdb' module. Future
3058 3053 * expansion of this program should be done here, or through the external
3059 3054 * loadable module interface.
3060 3055 */
3061 3056 const mdb_dcmd_t mdb_dcmd_builtins[] = {
3062 3057
3063 3058 /*
3064 3059 * dcmds common to both mdb and kmdb
3065 3060 */
3066 3061 { ">", "variable-name", "assign variable", cmd_assign_variable },
3067 3062 { "/", "fmt-list", "format data from virtual as", cmd_print_core },
3068 3063 { "\\", "fmt-list", "format data from physical as", cmd_print_phys },
3069 3064 { "@", "fmt-list", "format data from physical as", cmd_print_phys },
3070 3065 { "=", "fmt-list", "format immediate value", cmd_print_value },
3071 3066 { "$<", "macro-name", "replace input with macro",
3072 3067 cmd_exec_file, srcexec_file_help },
3073 3068 { "$<<", "macro-name", "source macro",
3074 3069 cmd_src_file, srcexec_file_help},
3075 3070 { "$%", NULL, NULL, cmd_quit },
3076 3071 { "$?", NULL, "print status and registers", cmd_notsup },
3077 3072 { "$a", NULL, NULL, cmd_algol },
3078 3073 { "$b", "[-av]", "list traced software events",
3079 3074 cmd_events, events_help },
3080 3075 { "$c", "?[cnt]", "print stack backtrace", cmd_notsup },
3081 3076 { "$C", "?[cnt]", "print stack backtrace", cmd_notsup },
3082 3077 { "$d", NULL, "get/set default output radix", cmd_radix },
3083 3078 { "$D", "?[mode,...]", NULL, cmd_dbmode },
3084 3079 { "$e", NULL, "print listing of global symbols", cmd_globals },
3085 3080 { "$f", NULL, "print listing of source files", cmd_files },
3086 3081 { "$m", "?[name]", "print address space mappings", cmd_mappings },
3087 3082 { "$M", NULL, "list macro aliases", cmd_macalias_list },
3088 3083 { "$P", "[prompt]", "set debugger prompt string", cmd_prompt },
3089 3084 { "$q", NULL, "quit debugger", cmd_quit },
3090 3085 { "$Q", NULL, "quit debugger", cmd_quit },
3091 3086 { "$r", NULL, "print general-purpose registers", cmd_notsup },
3092 3087 { "$s", NULL, "get/set symbol matching distance", cmd_symdist },
3093 3088 { "$v", NULL, "print non-zero variables", cmd_nzvars },
3094 3089 { "$V", "[mode]", "get/set disassembly mode", cmd_dismode },
3095 3090 { "$w", NULL, "get/set output page width", cmd_pgwidth },
3096 3091 { "$W", NULL, "re-open target in write mode", cmd_reopen },
3097 3092 { ":a", ":[cmd...]", "set read access watchpoint", cmd_oldwpr },
3098 3093 { ":b", ":[cmd...]", "breakpoint at the specified address", cmd_oldbp },
3099 3094 { ":d", "?[id|all]", "delete traced software events", cmd_delete },
3100 3095 { ":p", ":[cmd...]", "set execute access watchpoint", cmd_oldwpx },
3101 3096 { ":S", NULL, NULL, cmd_step },
3102 3097 { ":w", ":[cmd...]", "set write access watchpoint", cmd_oldwpw },
3103 3098 { ":z", NULL, "delete all traced software events", cmd_zapall },
3104 3099 { "array", ":[type count] [variable]", "print each array element's "
3105 3100 "address", cmd_array },
3106 3101 { "bp", "?[+/-dDestT] [-c cmd] [-n count] sym ...", "breakpoint at the "
3107 3102 "specified addresses or symbols", cmd_bp, bp_help },
3108 3103 { "dcmds", NULL, "list available debugger commands", cmd_dcmds },
3109 3104 { "delete", "?[id|all]", "delete traced software events", cmd_delete },
3110 3105 { "dis", "?[-abfw] [-n cnt] [addr]", "disassemble near addr", cmd_dis },
3111 3106 { "disasms", NULL, "list available disassemblers", cmd_disasms },
3112 3107 { "dismode", "[mode]", "get/set disassembly mode", cmd_dismode },
3113 3108 { "dmods", "[-l] [mod]", "list loaded debugger modules", cmd_dmods },
3114 3109 { "dump", "?[-eqrstu] [-f|-p] [-g bytes] [-l bytes] [-w paragraphs]",
3115 3110 "dump memory from specified address", cmd_dump, dump_help },
3116 3111 { "echo", "args ...", "echo arguments", cmd_echo },
3117 3112 { "enum", "?[-ex] enum [name]", "print an enumeration", cmd_enum,
3118 3113 enum_help },
3119 3114 { "eval", "command", "evaluate the specified command", cmd_eval },
3120 3115 { "events", "[-av]", "list traced software events",
3121 3116 cmd_events, events_help },
3122 3117 { "evset", "?[+/-dDestT] [-c cmd] [-n count] id ...",
3123 3118 "set software event specifier attributes", cmd_evset, evset_help },
3124 3119 { "files", "[object]", "print listing of source files", cmd_files },
3125 3120 #ifdef __sparc
3126 3121 { "findsym", "?[-g] [symbol|addr ...]", "search for symbol references "
3127 3122 "in all known functions", cmd_findsym, NULL },
3128 3123 #endif
3129 3124 { "formats", NULL, "list format specifiers", cmd_formats },
3130 3125 { "grep", "?expr", "print dot if expression is true", cmd_grep },
3131 3126 { "head", "-num|-n num", "limit number of elements in pipe", cmd_head,
3132 3127 head_help },
3133 3128 { "help", "[cmd]", "list commands/command help", cmd_help, NULL,
3134 3129 cmd_help_tab },
3135 3130 { "list", "?type member [variable]",
3136 3131 "walk list using member as link pointer", cmd_list, NULL,
3137 3132 mdb_tab_complete_mt },
3138 3133 { "map", "?expr", "print dot after evaluating expression", cmd_map },
3139 3134 { "mappings", "?[name]", "print address space mappings", cmd_mappings },
3140 3135 { "nm", "?[-DPdghnopuvx] [-f format] [-t types] [object]",
3141 3136 "print symbols", cmd_nm, nm_help },
3142 3137 { "nmadd", ":[-fo] [-e end] [-s size] name",
3143 3138 "add name to private symbol table", cmd_nmadd, nmadd_help },
3144 3139 { "nmdel", "name", "remove name from private symbol table", cmd_nmdel },
3145 3140 { "obey", NULL, NULL, cmd_obey },
3146 3141 { "objects", "[-v]", "print load objects information", cmd_objects },
3147 3142 { "offsetof", "type member", "print the offset of a given struct "
3148 3143 "or union member", cmd_offsetof, NULL, mdb_tab_complete_mt },
3149 3144 { "print", "?[-aCdhiLptx] [-c lim] [-l lim] [type] [member|offset ...]",
3150 3145 "print the contents of a data structure", cmd_print, print_help,
3151 3146 cmd_print_tab },
3152 3147 { "printf", "?format type member ...", "print and format the "
3153 3148 "member(s) of a data structure", cmd_printf, printf_help,
3154 3149 cmd_printf_tab },
3155 3150 { "regs", NULL, "print general purpose registers", cmd_notsup },
3156 3151 { "set", "[-wF] [+/-o opt] [-s dist] [-I path] [-L path] [-P prompt]",
3157 3152 "get/set debugger properties", cmd_set },
3158 3153 { "showrev", "[-pv]", "print version information", cmd_showrev },
3159 3154 { "sizeof", "type", "print the size of a type", cmd_sizeof, NULL,
3160 3155 cmd_sizeof_tab },
3161 3156 { "stack", "?[cnt]", "print stack backtrace", cmd_notsup },
3162 3157 { "stackregs", "?", "print stack backtrace and registers",
3163 3158 cmd_notsup },
3164 3159 { "status", NULL, "print summary of current target", cmd_notsup },
3165 3160 { "term", NULL, "display current terminal type", cmd_term },
3166 3161 { "typeset", "[+/-t] var ...", "set variable attributes", cmd_typeset },
3167 3162 { "typedef", "[-c model | -d | -l | -r file | -w file ] [type] [name]",
3168 3163 "create synthetic types", cmd_typedef, cmd_typedef_help },
3169 3164 { "unset", "[name ...]", "unset variables", cmd_unset },
3170 3165 { "vars", "[-npt]", "print listing of variables", cmd_vars },
3171 3166 { "version", NULL, "print debugger version string", cmd_version },
3172 3167 { "vtop", ":[-a as]", "print physical mapping of virtual address",
3173 3168 cmd_vtop },
3174 3169 { "walk", "?name [variable]", "walk data structure", cmd_walk, NULL,
3175 3170 cmd_walk_tab },
3176 3171 { "walkers", NULL, "list available walkers", cmd_walkers },
3177 3172 { "whatis", ":[-aikqv]", "given an address, return information",
3178 3173 cmd_whatis, whatis_help },
3179 3174 { "whence", "[-v] name ...", "show source of walk or dcmd", cmd_which },
3180 3175 { "which", "[-v] name ...", "show source of walk or dcmd", cmd_which },
3181 3176 { "write", "?[-op] [-l len] value",
3182 3177 "write value to the provided memory location", cmd_write,
3183 3178 write_help },
3184 3179 { "xdata", NULL, "print list of external data buffers", cmd_xdata },
3185 3180
3186 3181 #ifdef _KMDB
3187 3182 /*
3188 3183 * dcmds specific to kmdb, or which have kmdb-specific arguments
3189 3184 */
3190 3185 { "?", "fmt-list", "format data from virtual as", cmd_print_core },
3191 3186 { ":c", NULL, "continue target execution", cmd_cont },
3192 3187 { ":e", NULL, "step target over next instruction", cmd_next },
3193 3188 { ":s", NULL, "single-step target to next instruction", cmd_step },
3194 3189 { ":u", NULL, "step target out of current function", cmd_step_out },
3195 3190 { "cont", NULL, "continue target execution", cmd_cont },
3196 3191 { "load", "[-sd] module", "load debugger module", cmd_load, load_help },
3197 3192 { "next", NULL, "step target over next instruction", cmd_next },
3198 3193 { "quit", "[-u]", "quit debugger", cmd_quit, quit_help },
3199 3194 { "step", "[ over | out ]",
3200 3195 "single-step target to next instruction", cmd_step },
3201 3196 { "unload", "[-d] module", "unload debugger module", cmd_unload,
3202 3197 unload_help },
3203 3198 { "wp", ":[+/-dDelstT] [-rwx] [-pi] [-c cmd] [-n count] [-L size]",
3204 3199 "set a watchpoint at the specified address", cmd_wp, wp_help },
3205 3200
3206 3201 #else
3207 3202 /*
3208 3203 * dcmds specific to mdb, or which have mdb-specific arguments
3209 3204 */
3210 3205 { "?", "fmt-list", "format data from object file", cmd_print_object },
3211 3206 { "$>", "[file]", "log session to a file", cmd_old_log },
3212 3207 { "$g", "?", "get/set C++ demangling options", cmd_demflags },
3213 3208 { "$G", NULL, "enable/disable C++ demangling support", cmd_demangle },
3214 3209 { "$i", NULL, "print signals that are ignored", cmd_notsup },
3215 3210 { "$l", NULL, "print the representative thread's lwp id", cmd_notsup },
3216 3211 { "$p", ":", "change debugger target context", cmd_context },
3217 3212 { "$x", NULL, "print floating point registers", cmd_notsup },
3218 3213 { "$X", NULL, "print floating point registers", cmd_notsup },
3219 3214 { "$y", NULL, "print floating point registers", cmd_notsup },
3220 3215 { "$Y", NULL, "print floating point registers", cmd_notsup },
3221 3216 { ":A", "?[core|pid]", "attach to process or core file", cmd_notsup },
3222 3217 { ":c", "[SIG]", "continue target execution", cmd_cont },
3223 3218 { ":e", "[SIG]", "step target over next instruction", cmd_next },
3224 3219 { ":i", ":", "ignore signal (delete all matching events)", cmd_notsup },
3225 3220 { ":k", NULL, "forcibly kill and release target", cmd_notsup },
3226 3221 { ":t", "?[+/-dDestT] [-c cmd] [-n count] SIG ...", "stop on delivery "
3227 3222 "of the specified signals", cmd_sigbp, sigbp_help },
3228 3223 { ":r", "[ args ... ]", "run a new target process", cmd_run },
3229 3224 { ":R", NULL, "release the previously attached process", cmd_notsup },
3230 3225 { ":s", "[SIG]", "single-step target to next instruction", cmd_step },
3231 3226 { ":u", "[SIG]", "step target out of current function", cmd_step_out },
3232 3227 { "attach", "?[core|pid]",
3233 3228 "attach to process or core file", cmd_notsup },
3234 3229 { "cat", "[file ...]", "concatenate and display files", cmd_cat },
3235 3230 { "cont", "[SIG]", "continue target execution", cmd_cont },
3236 3231 { "context", ":", "change debugger target context", cmd_context },
3237 3232 { "dem", "name ...", "demangle C++ symbol names", cmd_demstr },
3238 3233 { "fltbp", "?[+/-dDestT] [-c cmd] [-n count] fault ...",
3239 3234 "stop on machine fault", cmd_fltbp, fltbp_help },
3240 3235 { "fpregs", NULL, "print floating point registers", cmd_notsup },
3241 3236 { "kill", NULL, "forcibly kill and release target", cmd_notsup },
3242 3237 { "load", "[-s] module", "load debugger module", cmd_load, load_help },
3243 3238 { "log", "[-d | [-e] file]", "log session to a file", cmd_log },
3244 3239 { "next", "[SIG]", "step target over next instruction", cmd_next },
3245 3240 { "quit", NULL, "quit debugger", cmd_quit },
3246 3241 { "release", NULL,
3247 3242 "release the previously attached process", cmd_notsup },
3248 3243 { "run", "[ args ... ]", "run a new target process", cmd_run },
3249 3244 { "sigbp", "?[+/-dDestT] [-c cmd] [-n count] SIG ...", "stop on "
3250 3245 "delivery of the specified signals", cmd_sigbp, sigbp_help },
3251 3246 { "step", "[ over | out ] [SIG]",
3252 3247 "single-step target to next instruction", cmd_step },
3253 3248 { "sysbp", "?[+/-dDestT] [-io] [-c cmd] [-n count] syscall ...",
3254 3249 "stop on entry or exit from system call", cmd_sysbp, sysbp_help },
3255 3250 { "unload", "module", "unload debugger module", cmd_unload },
3256 3251 { "wp", ":[+/-dDelstT] [-rwx] [-c cmd] [-n count] [-L size]",
3257 3252 "set a watchpoint at the specified address", cmd_wp, wp_help },
3258 3253 #endif
3259 3254
3260 3255 { NULL }
3261 3256 };
↓ open down ↓ |
501 lines elided |
↑ open up ↑ |
XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX