]> git.saurik.com Git - apple/xnu.git/blob - osfmk/ddb/db_variables.c
f30e5cad416edbd709835acddb05e7747ec4e3e0
[apple/xnu.git] / osfmk / ddb / db_variables.c
1 /*
2 * Copyright (c) 2000-2005 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_OSREFERENCE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. The rights granted to you under the License
10 * may not be used to create, or enable the creation or redistribution of,
11 * unlawful or unlicensed copies of an Apple operating system, or to
12 * circumvent, violate, or enable the circumvention or violation of, any
13 * terms of an Apple operating system software license agreement.
14 *
15 * Please obtain a copy of the License at
16 * http://www.opensource.apple.com/apsl/ and read it before using this file.
17 *
18 * The Original Code and all software distributed under the License are
19 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
20 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
21 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
23 * Please see the License for the specific language governing rights and
24 * limitations under the License.
25 *
26 * @APPLE_OSREFERENCE_LICENSE_HEADER_END@
27 */
28 /*
29 * @OSF_COPYRIGHT@
30 */
31 /*
32 * Mach Operating System
33 * Copyright (c) 1991,1990 Carnegie Mellon University
34 * All Rights Reserved.
35 *
36 * Permission to use, copy, modify and distribute this software and its
37 * documentation is hereby granted, provided that both the copyright
38 * notice and this permission notice appear in all copies of the
39 * software, derivative works or modified versions, and any portions
40 * thereof, and that both notices appear in supporting documentation.
41 *
42 * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
43 * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
44 * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
45 *
46 * Carnegie Mellon requests users of this software to return to
47 *
48 * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
49 * School of Computer Science
50 * Carnegie Mellon University
51 * Pittsburgh PA 15213-3890
52 *
53 * any improvements or extensions that they make and grant Carnegie Mellon
54 * the rights to redistribute these changes.
55 */
56 /*
57 */
58 /*
59 * Author: David B. Golub, Carnegie Mellon University
60 * Date: 7/90
61 */
62
63 #include <machine/db_machdep.h>
64 #include <string.h> /* For strcpy() */
65
66 #include <ddb/db_lex.h>
67 #include <ddb/db_variables.h>
68 #include <ddb/db_task_thread.h>
69 #include <ddb/db_sym.h>
70 #include <ddb/db_command.h>
71 #include <ddb/db_expr.h>
72 #include <ddb/db_macro.h>
73 #include <ddb/db_output.h> /* For db_printf() */
74
75 extern db_expr_t db_max_width;
76 extern db_expr_t db_tab_stop_width;
77 extern db_expr_t db_max_line;
78 extern db_expr_t db_auto_wrap;
79 extern db_expr_t db_macro_level;
80 extern db_expr_t db_auto_completion;
81
82 #define DB_NWORK 32 /* number of work variable */
83
84 db_expr_t db_work[DB_NWORK]; /* work variable */
85
86 struct db_variable db_vars[] = {
87 {
88 .name = "maxoff",
89 .valuep = (db_expr_t*)&db_maxoff,
90 },
91 {
92 .name = "autowrap",
93 .valuep = &db_auto_wrap,
94 },
95 {
96 .name = "completion",
97 .valuep = &db_auto_completion,
98 },
99 {
100 .name = "maxwidth",
101 .valuep = &db_max_width,
102 },
103 {
104 .name = "radix",
105 .valuep = &db_radix,
106 },
107 {
108 .name = "tabstops",
109 .valuep = &db_tab_stop_width,
110 },
111 {
112 .name = "lines",
113 .valuep = &db_max_line,
114 },
115 {
116 .name = "thr_act",
117 .fcn = db_set_default_act,
118 },
119 {
120 .name = "task",
121 .fcn = db_get_task_act,
122 .min_level = 1,
123 .max_level = 2,
124 .low = -1,
125 .high = -1,
126 },
127 {
128 .name = "work",
129 .valuep = &db_work[0],
130 .min_level = 1,
131 .max_level = 1,
132 .high = DB_NWORK - 1,
133 },
134 {
135 .name = "arg",
136 .fcn = db_arg_variable,
137 .min_level = 1,
138 .max_level = 1,
139 .low = 1,
140 .high = DB_MACRO_NARGS,
141 .hidden_level = 1,
142 .hidden_low = 0,
143 .hidden_high = DB_MACRO_LEVEL - 1,
144 .hidden_levelp = (int *)&db_macro_level,
145 },
146 };
147 struct db_variable *db_evars = db_vars + sizeof(db_vars)/sizeof(db_vars[0]);
148
149
150
151 /* Prototypes for functions local to this file.
152 */
153
154 static const char *db_get_suffix(const char *, short *);
155
156 static boolean_t db_cmp_variable_name(struct db_variable *, const char *,
157 db_var_aux_param_t);
158
159 static int db_find_variable(
160 struct db_variable **varp,
161 db_var_aux_param_t ap);
162
163 void db_list_variable(void);
164
165 static const char *
166 db_get_suffix(const char *suffix, short *suffix_value)
167 {
168 register int value;
169
170 for (value = 0; *suffix && *suffix != '.' && *suffix != ':'; suffix++) {
171 if (*suffix < '0' || *suffix > '9')
172 return(0);
173 value = value*10 + *suffix - '0';
174 }
175 *suffix_value = value;
176 if (*suffix == '.')
177 suffix++;
178 return(suffix);
179 }
180
181 static boolean_t
182 db_cmp_variable_name(struct db_variable *vp, const char *name,
183 db_var_aux_param_t ap)
184 {
185 const char *var_np, *np;
186 int level;
187
188 for (np = name, var_np = vp->name; *var_np; ) {
189 if (*np++ != *var_np++)
190 return(FALSE);
191 }
192 for (level = 0; *np && *np != ':' && level < vp->max_level; level++){
193 if ((np = db_get_suffix(np, &ap->suffix[level])) == 0)
194 return(FALSE);
195 }
196 if ((*np && *np != ':') || level < vp->min_level
197 || (level > 0 && (ap->suffix[0] < vp->low
198 || (vp->high >= 0 && ap->suffix[0] > vp->high))))
199 return(FALSE);
200 strlcpy(ap->modif, (*np)? np+1: "", TOK_STRING_SIZE);
201 ap->thr_act = (db_option(ap->modif, 't')?db_default_act: THREAD_NULL);
202 ap->level = level;
203 ap->hidden_level = -1;
204 return(TRUE);
205 }
206
207 static int
208 db_find_variable(
209 struct db_variable **varp,
210 db_var_aux_param_t ap)
211 {
212 int t;
213 struct db_variable *vp;
214
215 t = db_read_token();
216 if (t == tIDENT) {
217 for (vp = db_vars; vp < db_evars; vp++) {
218 if (db_cmp_variable_name(vp, db_tok_string, ap)) {
219 *varp = vp;
220 return (1);
221 }
222 }
223 for (vp = db_regs; vp < db_eregs; vp++) {
224 if (db_cmp_variable_name(vp, db_tok_string, ap)) {
225 *varp = vp;
226 return (1);
227 }
228 }
229 #if defined(ALTERNATE_REGISTER_DEFS)
230 for (vp = db_altregs; vp < db_ealtregs; vp++) {
231 if (db_cmp_variable_name(vp, db_tok_string, ap)) {
232 *varp = vp;
233 return (1);
234 }
235 }
236 #endif /* defined(ALTERNATE_REGISTER_DEFS) */
237 }
238 db_printf("Unknown variable \"$%s\"\n", db_tok_string);
239 db_error(0);
240 return (0);
241 }
242
243 int
244 db_get_variable(db_expr_t *valuep)
245 {
246 struct db_variable *vp;
247 struct db_var_aux_param aux_param;
248 char modif[TOK_STRING_SIZE];
249
250 aux_param.modif = modif;
251 if (!db_find_variable(&vp, &aux_param))
252 return (0);
253
254 db_read_write_variable(vp, valuep, DB_VAR_GET, &aux_param);
255
256 return (1);
257 }
258
259 void
260 db_read_write_variable(
261 struct db_variable *vp,
262 db_expr_t *valuep,
263 int rw_flag,
264 db_var_aux_param_t ap)
265 {
266 int (*func)(struct db_variable*, db_expr_t*,int, db_var_aux_param_t)
267 = vp->fcn;
268 struct db_var_aux_param aux_param;
269 db_expr_t old_value;
270
271 if (ap == 0) {
272 ap = &aux_param;
273 ap->modif = NULL;
274 ap->level = 0;
275 ap->thr_act = THREAD_NULL;
276 }
277 if (rw_flag == DB_VAR_SET && vp->precious)
278 db_read_write_variable(vp, &old_value, DB_VAR_GET, ap);
279 if (func == FCN_NULL) {
280 if (rw_flag == DB_VAR_SET)
281 vp->valuep[(ap->level)? (ap->suffix[0] - vp->low): 0] = *valuep;
282 else
283 *valuep = vp->valuep[(ap->level)? (ap->suffix[0] - vp->low): 0];
284 } else
285 (*func)(vp, valuep, rw_flag, ap);
286 if (rw_flag == DB_VAR_SET && vp->precious)
287 db_printf("\t$%s:%s<%#x>\t%#8lln\t=\t%#8lln\n", vp->name,
288 ap->modif, ap->thr_act, (unsigned long long)old_value, (unsigned long long)*valuep);
289 }
290
291 void
292 db_list_variable(void)
293 {
294 register struct db_variable *new;
295 register struct db_variable *old;
296 register struct db_variable *cur;
297 unsigned int l;
298 unsigned int len;
299 short i;
300 unsigned int j;
301
302 len = 1;
303 for (cur = db_vars; cur < db_evars; cur++) {
304 if (cur->min_level > 0 || cur->max_level > 0) {
305 j = 3 * (cur->max_level - cur->min_level + 1) - 1;
306 if (cur->max_level > cur->min_level)
307 j += 2;
308 } else
309 j = 0;
310 if ((l = strlen(cur->name) + j) >= len)
311 len = l + 1;
312 }
313
314 old = (struct db_variable *)0;
315 for (;;) {
316 new = (struct db_variable *)0;
317 for (cur = db_vars; cur < db_evars; cur++)
318 if ((new == (struct db_variable *)0 ||
319 strcmp(cur->name, new->name) < 0) &&
320 (old == (struct db_variable *)0 ||
321 strcmp(cur->name, old->name) > 0))
322 new = cur;
323 if (new == (struct db_variable *)0)
324 return;
325 db_reserve_output_position(len);
326 db_printf(new->name);
327 j = strlen(new->name);
328 if (new->min_level > 0) {
329 db_putchar('?');
330 db_putchar('?');
331 j += 2;
332 for (i = new->min_level - 1; i > 0; i--) {
333 db_putchar('.');
334 db_putchar('?');
335 db_putchar('?');
336 j += 3;
337 }
338 if (new->max_level > new->min_level) {
339 db_putchar('[');
340 db_putchar('.');
341 db_putchar('?');
342 db_putchar('?');
343 j += 4;
344 }
345 i = new->min_level + 1;
346 } else {
347 if (new->max_level > new->min_level) {
348 db_putchar('[');
349 j++;
350 }
351 i = new->min_level;
352 }
353 while (i++ < new->max_level) {
354 db_putchar('.');
355 db_putchar('?');
356 db_putchar('?');
357 j += 3;
358 }
359 if (new->max_level > new->min_level) {
360 db_putchar(']');
361 j++;
362 }
363 while (j++ < len)
364 db_putchar(' ');
365 old = new;
366 }
367 }
368
369 void
370 db_set_cmd(void)
371 {
372 db_expr_t value;
373 int t;
374 struct db_variable *vp;
375 struct db_var_aux_param aux_param;
376 char modif[TOK_STRING_SIZE];
377
378 aux_param.modif = modif;
379 t = db_read_token();
380 if (t == tIDENT && strcmp("help", db_tok_string) == 0) {
381 db_list_variable();
382 return;
383 }
384 if (t != tDOLLAR) {
385 db_error("Variable name should be prefixed with $\n");
386 return;
387 }
388 if (!db_find_variable(&vp, &aux_param)) {
389 db_error("Unknown variable\n");
390 return;
391 }
392
393 t = db_read_token();
394 if (t != tEQ)
395 db_unread_token(t);
396
397 if (!db_expression(&value)) {
398 db_error("No value\n");
399 return;
400 }
401 if ((t = db_read_token()) == tSEMI_COLON)
402 db_unread_token(t);
403 else if (t != tEOL)
404 db_error("?\n");
405
406 db_read_write_variable(vp, &value, DB_VAR_SET, &aux_param);
407 }
408
409 void
410 db_show_one_variable(void)
411 {
412 struct db_variable *cur;
413 unsigned int len;
414 unsigned int sl;
415 unsigned int slen = 0;
416 short h = 0;
417 short i;
418 unsigned short j;
419 short k;
420 short low;
421 int hidden_level = 0;
422 struct db_var_aux_param aux_param;
423 const char *p = NULL, *q;
424 char *name;
425 db_addr_t offset = 0;
426
427 for (cur = db_vars; cur < db_evars; cur++)
428 if (db_cmp_variable_name(cur, db_tok_string, &aux_param))
429 break;
430 if (cur == db_evars) {
431 for (cur = db_vars; cur < db_evars; cur++) {
432 for (q = cur->name, p = db_tok_string; *q && *p == *q; p++,q++)
433 continue;
434 if (*q == '\0')
435 break;
436 }
437 if (cur == db_evars) {
438 db_error("Unknown variable\n");
439 return;
440 }
441
442 for (i = 0; *p && *p != ':' && i < cur->max_level; i++, p = q)
443 if ((q = db_get_suffix(p, &aux_param.suffix[i])) == 0)
444 break;
445 aux_param.level = i;
446 if ((*p && *p != ':') ||
447 (i > 0 && (aux_param.suffix[0] < cur->low ||
448 (cur->high >= 0 &&
449 aux_param.suffix[0] > cur->high)))) {
450 db_error("Unknown variable format\n");
451 return;
452 }
453
454 strlcpy(aux_param.modif, *p ? p + 1 : "", TOK_STRING_SIZE);
455 aux_param.thr_act = (db_option(aux_param.modif, 't') ?
456 db_default_act : THREAD_NULL);
457 }
458
459 if (cur->hidden_level)
460 if (*cur->hidden_levelp >= cur->hidden_low &&
461 *cur->hidden_levelp <= cur->hidden_high) {
462 hidden_level = 1;
463 aux_param.hidden_level = h = *cur->hidden_levelp;
464 } else {
465 hidden_level = 0;
466 aux_param.hidden_level = h = cur->hidden_low;
467 slen = 1;
468 for (k = aux_param.level > 0 ? aux_param.suffix[0] : cur->high;
469 k > 9; k /= 10)
470 slen++;
471 }
472 else
473 aux_param.hidden_level = -1;
474
475 if ((cur->min_level == 0 && !cur->hidden_level) || cur->high < 0)
476 j = 0;
477 else {
478 if (cur->min_level > 0) {
479 j = 1;
480 for (k = aux_param.level > 0 ?
481 aux_param.suffix[0] : cur->high; k > 9; k /= 10)
482 j++;
483 } else
484 j = 0;
485 if (cur->hidden_level && hidden_level == 0) {
486 j += 3;
487 for (k = aux_param.hidden_level >= 0 ?
488 aux_param.hidden_level : cur->hidden_high; k > 9; k /= 10)
489 j++;
490 }
491 }
492 len = strlen(cur->name) + j;
493 i = low = aux_param.level > 0 ? aux_param.suffix[0] : cur->low;
494
495 for (;;) {
496 db_printf(cur->name);
497 j = strlen(cur->name);
498 if (cur->high >= 0) {
499 if (cur->min_level > 0) {
500 db_printf("%d", i);
501 j++;
502 for (k = i; k > 9; k /= 10)
503 j++;
504 }
505 if (cur->hidden_level && hidden_level == 0) {
506 sl = 1;
507 for (k = i; k > 9; k /= 10)
508 sl++;
509 while (sl++ < slen) {
510 db_putchar(' ');
511 j++;
512 }
513 db_printf("[%d]", h);
514 j += 3;
515 for (k = h; k > 9; k /= 10)
516 j++;
517 }
518 }
519
520 while (j++ < len)
521 db_putchar(' ');
522 db_putchar(':');
523 db_putchar(' ');
524
525 if (cur->fcn) {
526 aux_param.suffix[0] = i;
527 (*cur->fcn)(cur, (db_expr_t *)0, DB_VAR_SHOW, &aux_param);
528 } else {
529 db_printf("%#lln", (unsigned long long)*(cur->valuep + i));
530 db_find_xtrn_task_sym_and_offset(*(cur->valuep + i), &name,
531 &offset, TASK_NULL);
532 if (name != (char *)0 && offset <= db_maxoff &&
533 offset != *(cur->valuep + i)) {
534 db_printf("\t%s", name);
535 if (offset != 0)
536 db_printf("+%#llr", (unsigned long long)offset);
537 }
538 }
539 db_putchar('\n');
540 if (cur->high < 0)
541 break;
542 if (aux_param.level > 0 || i++ == cur->high) {
543 if (!cur->hidden_level ||
544 hidden_level == 0 ||
545 h++ == cur->hidden_high)
546 break;
547 aux_param.hidden_level = h;
548 i = low;
549 }
550 }
551 }
552
553 void
554 db_show_variable(__unused db_expr_t addr, __unused boolean_t have_addr,
555 __unused db_expr_t count, __unused char *modif)
556 {
557 struct db_variable *cur;
558 unsigned int l;
559 unsigned int len;
560 unsigned int sl;
561 unsigned int slen;
562 short h = 0;
563 short i;
564 unsigned short j;
565 short k;
566 int t;
567 int t1;
568 struct db_var_aux_param aux_param;
569 char *name;
570 db_addr_t offset;
571
572 switch(t = db_read_token()) {
573 case tEOL:
574 case tEOF:
575 case tSEMI_COLON:
576 break;
577
578 case tDOLLAR:
579 t1 = db_read_token();
580 if (t1 == tIDENT) {
581 db_show_one_variable();
582 return;
583 }
584 db_error("Not a variable name after $\n");
585 db_unread_token(t);
586 return;
587
588 default:
589 db_error("Variable name should be prefixed with $\n");
590 db_unread_token(t);
591 return;
592 }
593 db_unread_token(t);
594
595 slen = len = 1;
596 for (cur = db_vars; cur < db_evars; cur++) {
597 if ((cur->min_level == 0 && !cur->hidden_level) || cur->high < 0)
598 j = 0;
599 else {
600 if (cur->min_level > 0) {
601 j = 1;
602 for (k = cur->high; k > 9; k /= 10)
603 j++;
604 } else
605 j = 0;
606 if (cur->hidden_level &&
607 (*cur->hidden_levelp < cur->hidden_low ||
608 *cur->hidden_levelp > cur->hidden_high)) {
609 j += 3;
610 for (k = cur->hidden_high; k > 9; k /= 10)
611 j++;
612 }
613 }
614 if ((l = strlen(cur->name) + j) >= len)
615 len = l + 1;
616 }
617
618 aux_param.modif = NULL;
619 aux_param.level = 1;
620 aux_param.thr_act = THREAD_NULL;
621
622 for (cur = db_vars; cur < db_evars; cur++) {
623 i = cur->low;
624 if (cur->hidden_level) {
625 if (*cur->hidden_levelp >= cur->hidden_low &&
626 *cur->hidden_levelp <= cur->hidden_high) {
627 h = cur->hidden_low - 1;
628 aux_param.hidden_level = *cur->hidden_levelp;
629 } else {
630 h = cur->hidden_low;
631 aux_param.hidden_level = cur->hidden_low;
632 }
633 slen = 1;
634 for (k = cur->high; k > 9; k /= 10)
635 slen++;
636 } else
637 aux_param.hidden_level = -1;
638
639 if (cur != db_vars && cur->high >= 0 &&
640 (cur->min_level > 0 || cur->hidden_level))
641 db_putchar('\n');
642
643 for (;;) {
644 db_printf(cur->name);
645 j = strlen(cur->name);
646 if (cur->high >= 0) {
647 if (cur->min_level > 0) {
648 db_printf("%d", i);
649 j++;
650 for (k = i; k > 9; k /= 10)
651 j++;
652 }
653 if (cur->hidden_level && h >= cur->hidden_low) {
654 sl = 1;
655 for (k = i; k > 9; k /= 10)
656 sl++;
657 while (sl++ < slen) {
658 db_putchar(' ');
659 j++;
660 }
661 db_printf("[%d]", h);
662 j += 3;
663 for (k = h; k > 9; k /= 10)
664 j++;
665 }
666 }
667 while (j++ < len)
668 db_putchar(' ');
669 db_putchar(':');
670 db_putchar(' ');
671
672 if (cur->fcn) {
673 aux_param.suffix[0] = i;
674 (*cur->fcn)(cur, (db_expr_t *)0, DB_VAR_SHOW, &aux_param);
675 } else {
676 db_printf("%#lln", (unsigned long long)*(cur->valuep + i));
677 db_find_xtrn_task_sym_and_offset(*(cur->valuep + i), &name,
678 &offset, TASK_NULL);
679 if (name != (char *)0 && offset <= db_maxoff &&
680 offset != *(cur->valuep + i)) {
681 db_printf("\t%s", name);
682 if (offset != 0)
683 db_printf("+%#llr", (unsigned long long)offset);
684 }
685 }
686 db_putchar('\n');
687 if (cur->high < 0)
688 break;
689 if (i++ == cur->high) {
690 if (!cur->hidden_level || h++ == cur->hidden_high)
691 break;
692 aux_param.hidden_level = h;
693 i = cur->low;
694 }
695 }
696 }
697 }
698
699 /*
700 * given a name of a machine register, return a variable pointer to it.
701 */
702 db_variable_t
703 db_find_reg_name(
704 char *s)
705 {
706 register db_variable_t regp;
707
708 if ( s == (char *)0 )
709 return DB_VAR_NULL;
710
711 for (regp = db_regs; regp < db_eregs; regp++) {
712 if ( strcmp( s, regp->name) == 0 )
713 return regp;
714 }
715 return DB_VAR_NULL;
716 }