FreeBSD/Linux Kernel Cross Reference
sys/ddb/db_examine.c
1 /*
2 * Mach Operating System
3 * Copyright (c) 1993,1992,1991,1990 Carnegie Mellon University
4 * All Rights Reserved.
5 *
6 * Permission to use, copy, modify and distribute this software and its
7 * documentation is hereby granted, provided that both the copyright
8 * notice and this permission notice appear in all copies of the
9 * software, derivative works or modified versions, and any portions
10 * thereof, and that both notices appear in supporting documentation.
11 *
12 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
13 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
14 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
15 *
16 * Carnegie Mellon requests users of this software to return to
17 *
18 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
19 * School of Computer Science
20 * Carnegie Mellon University
21 * Pittsburgh PA 15213-3890
22 *
23 * any improvements or extensions that they make and grant Carnegie Mellon
24 * the rights to redistribute these changes.
25 */
26 /*
27 * HISTORY
28 * $Log: db_examine.c,v $
29 * Revision 2.14 93/11/17 16:21:44 dbg
30 * ANSI-fied. Removed db_strcpy in favor of string package
31 * (kern/strings.h).
32 * [93/10/11 dbg]
33 *
34 * Revision 2.13 93/05/17 10:26:31 rvb
35 * Type casts, etc to quiet gcc 2.3.3 warnings
36 *
37 * Revision 2.12 93/03/09 17:55:55 danner
38 * Added ',' format to examine command.
39 * [93/02/25 jfriedl]
40 *
41 * Revision 2.11 93/01/14 17:24:52 danner
42 * 64bit cleanup.
43 * [92/12/10 16:16:01 af]
44 *
45 * Revision 2.10 92/08/03 17:30:58 jfriedl
46 * removed silly prototypes
47 * [92/08/02 jfriedl]
48 *
49 * Revision 2.9 92/05/21 17:06:44 jfriedl
50 * Added void type to functions that needed it.
51 * Added init to 'size' in db_search_cmd(). Removed unused variables.
52 * Other cleanup to quiet gcc warnings.
53 * [92/05/16 jfriedl]
54 *
55 * Revision 2.8 92/05/04 11:23:59 danner
56 * x/u now examines current user space. x/t still examines user
57 * space of the the specified thread. x/tu is redundant.
58 * To examine an value as unsigned decimal, use x/U.
59 * [92/04/18 danner]
60 *
61 * Revision 2.7 91/10/09 15:59:28 af
62 * Supported non current task space data examination and search.
63 * Added 'm' format and db_xcdump to print with hex and characters.
64 * Added db_examine_{forward, backward}.
65 * Changed db_print_cmd to support variable number of parameters
66 * including string constant.
67 * Included "db_access.h".
68 * [91/08/29 tak]
69 *
70 * Revision 2.6 91/08/28 11:11:01 jsb
71 * Added 'A' flag to examine: just like 'a' (address), but prints addr
72 * as a procedure type, thus printing file/line info if available.
73 * Useful when called as 'x/Ai'.
74 * [91/08/13 18:14:55 jsb]
75 *
76 * Revision 2.5 91/05/14 15:33:31 mrt
77 * Correcting copyright
78 *
79 * Revision 2.4 91/02/05 17:06:20 mrt
80 * Changed to new Mach copyright
81 * [91/01/31 16:17:37 mrt]
82 *
83 * Revision 2.3 90/11/07 16:49:23 rpd
84 * Added db_search_cmd, db_search.
85 * [90/11/06 rpd]
86 *
87 * Revision 2.2 90/08/27 21:50:38 dbg
88 * Add 'r', 'z' to print and examine formats.
89 * Change calling sequence of db_disasm.
90 * db_examine sets db_prev and db_next instead of explicitly
91 * advancing dot.
92 * [90/08/20 dbg]
93 * Reflected changes in db_printsym()'s calling seq.
94 * [90/08/20 af]
95 * Reduce lint.
96 * [90/08/07 dbg]
97 * Created.
98 * [90/07/25 dbg]
99 *
100 */
101 /*
102 * Author: David B. Golub, Carnegie Mellon University
103 * Date: 7/90
104 */
105 #include <mach/boolean.h>
106 #include <machine/db_machdep.h>
107
108 #include <kern/strings.h>
109
110 #include <ddb/db_access.h>
111 #include <ddb/db_lex.h>
112 #include <ddb/db_output.h>
113 #include <ddb/db_command.h>
114 #include <ddb/db_sym.h>
115 #include <ddb/db_task_thread.h>
116 #include <ddb/db_examine.h>
117
118 #include <kern/thread.h>
119 #include <kern/task.h>
120
121 #include <mach/vm_param.h>
122
123 #define db_thread_to_task(thread) ((thread)? thread->task: TASK_NULL)
124
125 char db_examine_format[TOK_STRING_SIZE] = "x";
126 int db_examine_count = 1;
127 db_addr_t db_examine_prev_addr = 0;
128 thread_t db_examine_thread = THREAD_NULL;
129
130 extern db_addr_t db_disasm(db_addr_t pc, boolean_t altform, task_t task);
131 /* instruction disassembler */
132
133 #define DB_XCDUMP_NC 16
134
135 db_addr_t
136 db_xcdump(
137 db_addr_t addr,
138 int size,
139 int count,
140 task_t task)
141 {
142 register i, n;
143 db_expr_t value;
144 int bcount;
145 db_addr_t off;
146 char *name;
147 char data[DB_XCDUMP_NC];
148
149 db_find_task_sym_and_offset(addr, &name, &off, task);
150 for (n = count*size; n > 0; n -= bcount) {
151 db_prev = addr;
152 if (off == 0) {
153 db_printf("%s:\n", name);
154 off = -1;
155 }
156 db_printf("%0*X:%s", 2*sizeof(db_addr_t), addr,
157 (size != 1)? " ": "");
158 bcount = ((n > DB_XCDUMP_NC)? DB_XCDUMP_NC: n);
159 if (trunc_page(addr) != trunc_page(addr+bcount-1)) {
160 db_addr_t next_page_addr = trunc_page(addr+bcount-1);
161 if (!DB_CHECK_ACCESS(next_page_addr, sizeof(int), task))
162 bcount = next_page_addr - addr;
163 }
164 db_read_bytes(addr, bcount, data, task);
165 for (i = 0; i < bcount && off != 0; i += size) {
166 if (i % 4 == 0)
167 db_printf(" ");
168 value = db_get_task_value(addr, size, FALSE, task);
169 db_printf("%0*x ", size*2, value);
170 addr += size;
171 db_find_task_sym_and_offset(addr, &name, &off, task);
172 }
173 db_printf("%*s",
174 ((DB_XCDUMP_NC-i)/size)*(size*2+1)+(DB_XCDUMP_NC-i)/4,
175 "");
176 bcount = i;
177 db_printf("%s*", (size != 1)? " ": "");
178 for (i = 0; i < bcount; i++) {
179 value = data[i];
180 db_printf("%c", (value >= ' ' && value <= '~')? value: '.');
181 }
182 db_printf("*\n");
183 }
184 return addr;
185 }
186
187 void
188 db_examine(
189 register
190 db_addr_t addr,
191 char * fmt, /* format string */
192 int count, /* repeat count */
193 task_t task)
194 {
195 int c;
196 db_expr_t value;
197 int size; /* in bytes */
198 int width;
199 char * fp;
200
201 db_examine_prev_addr = addr;
202 while (--count >= 0) {
203 fp = fmt;
204 size = sizeof(int);
205 width = 4*size;
206 while ((c = *fp++) != 0) {
207 switch (c) {
208 case 'b':
209 size = sizeof(char);
210 width = 4*size;
211 break;
212 case 'h':
213 size = sizeof(short);
214 width = 4*size;
215 break;
216 case 'l':
217 size = sizeof(long);
218 width = 4*size;
219 break;
220 case 'a': /* address */
221 case 'A': /* function address */
222 /* always forces a new line */
223 if (db_print_position() != 0)
224 db_printf("\n");
225 db_prev = addr;
226 db_task_printsym(addr,
227 (c == 'a')?DB_STGY_ANY:DB_STGY_PROC,
228 task);
229 db_printf(":\t");
230 break;
231 case 'm':
232 db_next = db_xcdump(addr, size, count+1, task);
233 return;
234 default:
235 if (db_print_position() == 0) {
236 /* If we hit a new symbol, print it */
237 char * name;
238 db_addr_t off;
239
240 db_find_task_sym_and_offset(addr,&name,&off,task);
241 if (off == 0)
242 db_printf("%s:\t", name);
243 else
244 db_printf("\t\t");
245
246 db_prev = addr;
247 }
248
249 switch (c) {
250 case ',': /* skip one unit w/o printing */
251 addr += size;
252 break;
253
254 case 'r': /* signed, current radix */
255 value = db_get_task_value(addr,size,TRUE,task);
256 addr += size;
257 db_printf("%-*R", width, value);
258 break;
259 case 'x': /* unsigned hex */
260 value = db_get_task_value(addr,size,FALSE,task);
261 addr += size;
262 db_printf("%-*X", width, value);
263 break;
264 case 'z': /* signed hex */
265 value = db_get_task_value(addr,size,TRUE,task);
266 addr += size;
267 db_printf("%-*Z", width, value);
268 break;
269 case 'd': /* signed decimal */
270 value = db_get_task_value(addr,size,TRUE,task);
271 addr += size;
272 db_printf("%-*D", width, value);
273 break;
274 case 'U': /* unsigned decimal */
275 value = db_get_task_value(addr,size,FALSE,task);
276 addr += size;
277 db_printf("%-*U", width, value);
278 break;
279 case 'o': /* unsigned octal */
280 value = db_get_task_value(addr,size,FALSE,task);
281 addr += size;
282 db_printf("%-*O", width, value);
283 break;
284 case 'c': /* character */
285 value = db_get_task_value(addr,1,FALSE,task);
286 addr += 1;
287 if (value >= ' ' && value <= '~')
288 db_printf("%c", value);
289 else
290 db_printf("\\%03o", value);
291 break;
292 case 's': /* null-terminated string */
293 for (;;) {
294 value = db_get_task_value(addr,1,FALSE,task);
295 addr += 1;
296 if (value == 0)
297 break;
298 if (value >= ' ' && value <= '~')
299 db_printf("%c", value);
300 else
301 db_printf("\\%03o", value);
302 }
303 break;
304 case 'i': /* instruction */
305 addr = db_disasm(addr, FALSE, task);
306 break;
307 case 'I': /* instruction, alternate form */
308 addr = db_disasm(addr, TRUE, task);
309 break;
310 default:
311 break;
312 }
313 if (db_print_position() != 0)
314 db_end_line();
315 break;
316 }
317 }
318 }
319 db_next = addr;
320 }
321
322 /*
323 * Examine (print) data.
324 */
325 /*ARGSUSED*/
326 void
327 db_examine_cmd(
328 db_expr_t addr,
329 int have_addr,
330 db_expr_t count,
331 char * modif)
332 {
333 thread_t thread;
334
335 if (modif[0] != '\0')
336 strcpy(db_examine_format, modif);
337
338 if (count == -1)
339 count = 1;
340 db_examine_count = count;
341 if (db_option(modif, 't'))
342 {
343 if (!db_get_next_thread(&thread, 0))
344 return;
345 }
346 else
347 if (db_option(modif,'u'))
348 thread = current_thread();
349 else
350 thread = THREAD_NULL;
351
352 db_examine_thread = thread;
353 db_examine((db_addr_t) addr, db_examine_format, count,
354 db_thread_to_task(thread));
355 }
356
357 /* ARGSUSED */
358 void
359 db_examine_forward(
360 db_expr_t addr,
361 int have_addr,
362 db_expr_t count,
363 char * modif)
364 {
365 db_examine(db_next, db_examine_format, db_examine_count,
366 db_thread_to_task(db_examine_thread));
367 }
368
369 /* ARGSUSED */
370 void
371 db_examine_backward(
372 db_expr_t addr,
373 int have_addr,
374 db_expr_t count,
375 char * modif)
376 {
377
378 db_examine(db_examine_prev_addr - (db_next - db_examine_prev_addr),
379 db_examine_format, db_examine_count,
380 db_thread_to_task(db_examine_thread));
381 }
382
383 /*
384 * Print value.
385 */
386 char db_print_format = 'x';
387
388 /*ARGSUSED*/
389 void
390 db_print_cmd()
391 {
392 db_expr_t value;
393 int t;
394 task_t task = TASK_NULL;
395
396 if ((t = db_read_token()) == tSLASH) {
397 if (db_read_token() != tIDENT) {
398 db_printf("Bad modifier \"/%s\"\n", db_tok_string);
399 db_error(0);
400 /* NOTREACHED */
401 }
402 if (db_tok_string[0])
403 db_print_format = db_tok_string[0];
404 if (db_option(db_tok_string, 't') && db_default_thread)
405 task = db_default_thread->task;
406 } else
407 db_unread_token(t);
408
409 for ( ; ; ) {
410 t = db_read_token();
411 if (t == tSTRING) {
412 db_printf("%s", db_tok_string);
413 continue;
414 }
415 db_unread_token(t);
416 if (!db_expression(&value))
417 break;
418 switch (db_print_format) {
419 case 'a':
420 db_task_printsym((db_addr_t)value, DB_STGY_ANY, task);
421 break;
422 case 'r':
423 db_printf("%*r", 3+2*sizeof(db_expr_t), value);
424 break;
425 case 'x':
426 db_printf("%*x", 2*sizeof(db_expr_t), value);
427 break;
428 case 'z':
429 db_printf("%*z", 2*sizeof(db_expr_t), value);
430 break;
431 case 'd':
432 db_printf("%*d", 3+2*sizeof(db_expr_t), value);
433 break;
434 case 'u':
435 db_printf("%*u", 3+2*sizeof(db_expr_t), value);
436 break;
437 case 'o':
438 db_printf("%o", 4*sizeof(db_expr_t), value);
439 break;
440 case 'c':
441 value = value & 0xFF;
442 if (value >= ' ' && value <= '~')
443 db_printf("%c", value);
444 else
445 db_printf("\\%03o", value);
446 break;
447 default:
448 db_printf("Unknown format %c\n", db_print_format);
449 db_print_format = 'x';
450 db_error(0);
451 }
452 }
453 }
454
455 void
456 db_print_loc_and_inst(
457 db_addr_t loc,
458 task_t task)
459 {
460 db_task_printsym(loc, DB_STGY_PROC, task);
461 db_printf(":\t");
462 (void) db_disasm(loc, TRUE, task);
463 }
464
465 void
466 db_search(
467 register
468 db_addr_t addr,
469 int size,
470 db_expr_t value,
471 db_expr_t mask,
472 unsigned int count,
473 task_t task)
474 {
475 while (count-- != 0) {
476 db_prev = addr;
477 if ((db_get_task_value(addr,size,FALSE,task) & mask) == value)
478 break;
479 addr += size;
480 }
481 db_next = addr;
482 }
483
484 /*
485 * Search for a value in memory.
486 * Syntax: search [/bhl] addr value [mask] [,count] [thread]
487 */
488 void
489 db_search_cmd()
490 {
491 int t;
492 db_addr_t addr;
493 int size = 0;
494 db_expr_t value;
495 db_expr_t mask;
496 db_addr_t count;
497 thread_t thread;
498 boolean_t thread_flag = FALSE;
499 register char *p;
500
501 t = db_read_token();
502 if (t == tSLASH) {
503 t = db_read_token();
504 if (t != tIDENT) {
505 bad_modifier:
506 db_printf("Bad modifier \"/%s\"\n", db_tok_string);
507 db_flush_lex();
508 return;
509 }
510
511 for (p = db_tok_string; *p; p++) {
512 switch(*p) {
513 case 'b':
514 size = sizeof(char);
515 break;
516 case 'h':
517 size = sizeof(short);
518 break;
519 case 'l':
520 size = sizeof(long);
521 break;
522 case 't':
523 thread_flag = TRUE;
524 break;
525 default:
526 goto bad_modifier;
527 }
528 }
529 } else {
530 db_unread_token(t);
531 size = sizeof(int);
532 }
533
534 if (!db_expression(&addr)) {
535 db_printf("Address missing\n");
536 db_flush_lex();
537 return;
538 }
539
540 if (!db_expression(&value)) {
541 db_printf("Value missing\n");
542 db_flush_lex();
543 return;
544 }
545
546 if (!db_expression(&mask))
547 mask = ~0;
548
549 t = db_read_token();
550 if (t == tCOMMA) {
551 if (!db_expression(&count)) {
552 db_printf("Count missing\n");
553 db_flush_lex();
554 return;
555 }
556 } else {
557 db_unread_token(t);
558 count = -1; /* effectively forever */
559 }
560 if (thread_flag) {
561 if (!db_get_next_thread(&thread, 0))
562 return;
563 } else
564 thread = THREAD_NULL;
565
566 db_search(addr, size, value, mask, count, db_thread_to_task(thread));
567 }
568
Cache object: c2edc8ccca150d4f1a9e4411db234926
|