Source to osfmk/ddb/db_variables.c
/*
* Copyright (c) 2000 Apple Computer, Inc. All rights reserved.
*
* @APPLE_LICENSE_HEADER_START@
*
* The contents of this file constitute Original Code as defined in and
* are subject to the Apple Public Source License Version 1.1 (the
* "License"). You may not use this file except in compliance with the
* License. Please obtain a copy of the License at
* http://www.apple.com/publicsource and read it before using this file.
*
* This Original Code and all software distributed under the License are
* distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY KIND, EITHER
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE OR NON-INFRINGEMENT. Please see the
* License for the specific language governing rights and limitations
* under the License.
*
* @APPLE_LICENSE_HEADER_END@
*/
/*
* @OSF_COPYRIGHT@
*/
/*
* HISTORY
*
* Revision 1.1.1.1 1998/09/22 21:05:48 wsanchez
* Import of Mac OS X kernel (~semeria)
*
* Revision 1.1.1.1 1998/03/07 02:26:09 wsanchez
* Import of OSF Mach kernel (~mburg)
*
* Revision 1.2.18.5 1996/01/09 19:16:34 devrcs
* Search the alternate register names if configured
* Changed declarations of 'register foo' to 'register int foo'.
* [1995/12/01 21:42:42 jfraser]
*
* Merged '64-bit safe' changes from DEC alpha port.
* [1995/11/21 18:03:56 jfraser]
*
* Revision 1.2.18.4 1995/02/23 21:43:56 alanl
* Merged with DIPC2_SHARED.
* [1995/01/05 13:35:55 alanl]
*
* Revision 1.2.21.1 1994/11/04 09:53:26 dwm
* mk6 CR668 - 1.3b26 merge
* * Revision 1.2.4.6 1994/05/06 18:40:13 tmt
* Merged osc1.3dec/shared with osc1.3b19
* Merge Alpha changes into osc1.312b source code.
* 64bit cleanup.
* * End1.3merge
* [1994/11/04 08:50:12 dwm]
*
* Revision 1.2.18.2 1994/09/23 01:22:35 ezf
* change marker to not FREE
* [1994/09/22 21:11:24 ezf]
*
* Revision 1.2.18.1 1994/06/11 21:12:37 bolinger
* Merge up to NMK17.2.
* [1994/06/11 20:03:04 bolinger]
*
* Revision 1.2.23.1 1994/12/06 19:43:18 alanl
* Intel merge, Oct 94 code drop.
* Added db_find_reg_name (came from db_print.c).
* [94/11/28 mmp]
*
* Revision 1.2.16.1 1994/02/08 10:59:08 bernadat
* Added completion variable.
* [93/08/17 paire]
*
* Set up new fields (hidden_xxx) of db_vars[] array that are supposed
* to be helpful to display variables depending on an internal value
* like db_macro_level for macro arguments.
* Added db_auto_wrap as new variable.
* Added "set help" for listing all available variables.
* Added db_show_variable() and db_show_one_variable()
* to print variable values.
* [93/08/12 paire]
* [94/02/08 bernadat]
*
* Revision 1.2.4.4 1993/08/11 20:38:20 elliston
* Add ANSI Prototypes. CR #9523.
* [1993/08/11 03:34:13 elliston]
*
* Revision 1.2.4.3 1993/07/27 18:28:27 elliston
* Add ANSI prototypes. CR #9523.
* [1993/07/27 18:13:22 elliston]
*
* Revision 1.2.4.2 1993/06/09 02:21:02 gm
* Added to OSF/1 R1.3 from NMK15.0.
* [1993/06/02 20:57:43 jeffc]
*
* Revision 1.2 1993/04/19 16:03:25 devrcs
* Changes from mk78:
* Added void to db_read_write_variable().
* Removed unused variable 'func' from db_set_cmd().
* [92/05/16 jfriedl]
* [93/02/02 bruel]
*
* Print old value when changing register values.
* [[email protected]]
* [92/12/03 bernadat]
*
* Revision 1.1 1992/09/30 02:01:31 robert
* Initial revision
*
* $EndLog$
*/
/* CMU_HIST */
/*
* Revision 2.5 91/10/09 16:03:59 af
* Revision 2.4.3.1 91/10/05 13:08:27 jeffreyh
* Added suffix handling and thread handling of variables.
* Added new variables: lines, task, thread, work and arg.
* Moved db_read_variable and db_write_variable to db_variables.h
* as macros, and added db_read_write_variable instead.
* Changed some error messages.
* [91/08/29 tak]
*
* Revision 2.4.3.1 91/10/05 13:08:27 jeffreyh
* Added suffix handling and thread handling of variables.
* Added new variables: lines, task, thread, work and arg.
* Moved db_read_variable and db_write_variable to db_variables.h
* as macros, and added db_read_write_variable instead.
* Changed some error messages.
* [91/08/29 tak]
*
* Revision 2.4 91/05/14 15:36:57 mrt
* Correcting copyright
*
* Revision 2.3 91/02/05 17:07:19 mrt
* Changed to new Mach copyright
* [91/01/31 16:19:46 mrt]
*
* Revision 2.2 90/08/27 21:53:24 dbg
* New db_read/write_variable functions. Should be used instead
* of dereferencing valuep directly, which might not be a true
* pointer if there is an fcn() access function.
* [90/08/20 af]
*
* Fix declarations.
* Check for trailing garbage after last expression on command line.
* [90/08/10 14:34:54 dbg]
*
* Created.
* [90/07/25 dbg]
*
*/
/* CMU_ENDHIST */
/*
* Mach Operating System
* Copyright (c) 1991,1990 Carnegie Mellon University
* All Rights Reserved.
*
* Permission to use, copy, modify and distribute this software and its
* documentation is hereby granted, provided that both the copyright
* notice and this permission notice appear in all copies of the
* software, derivative works or modified versions, and any portions
* thereof, and that both notices appear in supporting documentation.
*
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
*
* Carnegie Mellon requests users of this software to return to
*
* Software Distribution Coordinator or [email protected]
* School of Computer Science
* Carnegie Mellon University
* Pittsburgh PA 15213-3890
*
* any improvements or extensions that they make and grant Carnegie Mellon
* the rights to redistribute these changes.
*/
/*
*/
/*
* Author: David B. Golub, Carnegie Mellon University
* Date: 7/90
*/
#include <machine/db_machdep.h>
#include <string.h> /* For strcpy() */
#include <ddb/db_lex.h>
#include <ddb/db_variables.h>
#include <ddb/db_task_thread.h>
#include <ddb/db_sym.h>
#include <ddb/db_command.h>
#include <ddb/db_expr.h>
#include <ddb/db_macro.h>
#include <ddb/db_output.h> /* For db_printf() */
extern db_expr_t db_radix;
extern db_expr_t db_max_width;
extern db_expr_t db_tab_stop_width;
extern db_expr_t db_max_line;
extern db_expr_t db_auto_wrap;
extern db_expr_t db_macro_level;
extern db_expr_t db_auto_completion;
#define DB_NWORK 32 /* number of work variable */
db_expr_t db_work[DB_NWORK]; /* work variable */
struct db_variable db_vars[] = {
{ "maxoff", (db_expr_t*)&db_maxoff, FCN_NULL },
{ "autowrap", &db_auto_wrap, FCN_NULL },
{ "completion", &db_auto_completion, FCN_NULL },
{ "maxwidth", &db_max_width, FCN_NULL },
{ "radix", &db_radix, FCN_NULL },
{ "tabstops", &db_tab_stop_width, FCN_NULL },
{ "lines", &db_max_line, FCN_NULL },
{ "thr_act", 0, db_set_default_act },
{ "task", 0, db_get_task_act,
1, 2, -1, -1 },
{ "work", &db_work[0], FCN_NULL,
1, 1, 0, DB_NWORK-1 },
{ "arg", 0, db_arg_variable,
1, 1, 1, DB_MACRO_NARGS,
1, 0, DB_MACRO_LEVEL-1, (int *)&db_macro_level },
};
struct db_variable *db_evars = db_vars + sizeof(db_vars)/sizeof(db_vars[0]);
/* Prototypes for functions local to this file.
*/
static char *db_get_suffix(
register char *suffix,
short *suffix_value);
static boolean_t db_cmp_variable_name(
struct db_variable *vp,
char *name,
register db_var_aux_param_t ap);
static int db_find_variable(
struct db_variable **varp,
db_var_aux_param_t ap);
static int db_set_variable(db_expr_t value);
void db_list_variable(void);
static char *
db_get_suffix(
register char *suffix,
short *suffix_value)
{
register int value;
for (value = 0; *suffix && *suffix != '.' && *suffix != ':'; suffix++) {
if (*suffix < '0' || *suffix > '9')
return(0);
value = value*10 + *suffix - '0';
}
*suffix_value = value;
if (*suffix == '.')
suffix++;
return(suffix);
}
static boolean_t
db_cmp_variable_name(
struct db_variable *vp,
char *name,
register db_var_aux_param_t ap)
{
register char *var_np, *np;
register int level;
for (np = name, var_np = vp->name; *var_np; ) {
if (*np++ != *var_np++)
return(FALSE);
}
for (level = 0; *np && *np != ':' && level < vp->max_level; level++){
if ((np = db_get_suffix(np, &ap->suffix[level])) == 0)
return(FALSE);
}
if ((*np && *np != ':') || level < vp->min_level
|| (level > 0 && (ap->suffix[0] < vp->low
|| (vp->high >= 0 && ap->suffix[0] > vp->high))))
return(FALSE);
strcpy(ap->modif, (*np)? np+1: "");
ap->thr_act = (db_option(ap->modif, 't')?db_default_act: THR_ACT_NULL);
ap->level = level;
ap->hidden_level = -1;
return(TRUE);
}
static int
db_find_variable(
struct db_variable **varp,
db_var_aux_param_t ap)
{
int t;
struct db_variable *vp;
t = db_read_token();
if (t == tIDENT) {
for (vp = db_vars; vp < db_evars; vp++) {
if (db_cmp_variable_name(vp, db_tok_string, ap)) {
*varp = vp;
return (1);
}
}
for (vp = db_regs; vp < db_eregs; vp++) {
if (db_cmp_variable_name(vp, db_tok_string, ap)) {
*varp = vp;
return (1);
}
}
#if defined(ALTERNATE_REGISTER_DEFS)
for (vp = db_altregs; vp < db_ealtregs; vp++) {
if (db_cmp_variable_name(vp, db_tok_string, ap)) {
*varp = vp;
return (1);
}
}
#endif /* defined(ALTERNATE_REGISTER_DEFS) */
}
db_printf("Unknown variable \"$%s\"\n", db_tok_string);
db_error(0);
return (0);
}
int
db_get_variable(db_expr_t *valuep)
{
struct db_variable *vp;
struct db_var_aux_param aux_param;
char modif[TOK_STRING_SIZE];
aux_param.modif = modif;
if (!db_find_variable(&vp, &aux_param))
return (0);
db_read_write_variable(vp, valuep, DB_VAR_GET, &aux_param);
return (1);
}
static int
db_set_variable(db_expr_t value)
{
struct db_variable *vp;
struct db_var_aux_param aux_param;
char modif[TOK_STRING_SIZE];
aux_param.modif = modif;
if (!db_find_variable(&vp, &aux_param))
return (0);
db_read_write_variable(vp, &value, DB_VAR_SET, &aux_param);
return (1);
}
void
db_read_write_variable(
struct db_variable *vp,
db_expr_t *valuep,
int rw_flag,
db_var_aux_param_t ap)
{
int (*func)(struct db_variable*, db_expr_t*,int, db_var_aux_param_t)
= vp->fcn;
struct db_var_aux_param aux_param;
db_expr_t old_value;
if (ap == 0) {
ap = &aux_param;
ap->modif = "";
ap->level = 0;
ap->thr_act = THR_ACT_NULL;
}
if (rw_flag == DB_VAR_SET && vp->precious)
db_read_write_variable(vp, &old_value, DB_VAR_GET, ap);
if (func == FCN_NULL) {
if (rw_flag == DB_VAR_SET)
vp->valuep[(ap->level)? (ap->suffix[0] - vp->low): 0] = *valuep;
else
*valuep = vp->valuep[(ap->level)? (ap->suffix[0] - vp->low): 0];
} else
(*func)(vp, valuep, rw_flag, ap);
if (rw_flag == DB_VAR_SET && vp->precious)
db_printf("\t$%s:%s<%#x>\t%#8n\t=\t%#8n\n", vp->name,
ap->modif, ap->thr_act, old_value, *valuep);
}
void
db_list_variable(void)
{
register struct db_variable *new;
register struct db_variable *old;
register struct db_variable *cur;
unsigned int l;
unsigned int len;
short i;
unsigned int j;
len = 1;
for (cur = db_vars; cur < db_evars; cur++) {
if (cur->min_level > 0 || cur->max_level > 0) {
j = 3 * (cur->max_level - cur->min_level + 1) - 1;
if (cur->max_level > cur->min_level)
j += 2;
} else
j = 0;
if ((l = strlen(cur->name) + j) >= len)
len = l + 1;
}
old = (struct db_variable *)0;
for (;;) {
new = (struct db_variable *)0;
for (cur = db_vars; cur < db_evars; cur++)
if ((new == (struct db_variable *)0 ||
strcmp(cur->name, new->name) < 0) &&
(old == (struct db_variable *)0 ||
strcmp(cur->name, old->name) > 0))
new = cur;
if (new == (struct db_variable *)0)
return;
db_reserve_output_position(len);
db_printf(new->name);
j = strlen(new->name);
if (new->min_level > 0) {
db_putchar('?');
db_putchar('?');
j += 2;
for (i = new->min_level - 1; i > 0; i--) {
db_putchar('.');
db_putchar('?');
db_putchar('?');
j += 3;
}
if (new->max_level > new->min_level) {
db_putchar('[');
db_putchar('.');
db_putchar('?');
db_putchar('?');
j += 4;
}
i = new->min_level + 1;
} else {
if (new->max_level > new->min_level) {
db_putchar('[');
j++;
}
i = new->min_level;
}
while (i++ < new->max_level) {
db_putchar('.');
db_putchar('?');
db_putchar('?');
j += 3;
}
if (new->max_level > new->min_level) {
db_putchar(']');
j++;
}
while (j++ < len)
db_putchar(' ');
old = new;
}
}
void
db_set_cmd(void)
{
db_expr_t value;
int t;
struct db_variable *vp;
struct db_var_aux_param aux_param;
char modif[TOK_STRING_SIZE];
aux_param.modif = modif;
t = db_read_token();
if (t == tIDENT && strcmp("help", db_tok_string) == 0) {
db_list_variable();
return;
}
if (t != tDOLLAR) {
db_error("Variable name should be prefixed with $\n");
return;
}
if (!db_find_variable(&vp, &aux_param)) {
db_error("Unknown variable\n");
return;
}
t = db_read_token();
if (t != tEQ)
db_unread_token(t);
if (!db_expression(&value)) {
db_error("No value\n");
return;
}
if ((t = db_read_token()) == tSEMI_COLON)
db_unread_token(t);
else if (t != tEOL)
db_error("?\n");
db_read_write_variable(vp, &value, DB_VAR_SET, &aux_param);
}
void
db_show_one_variable(void)
{
struct db_variable *cur;
unsigned int len;
unsigned int sl;
unsigned int slen;
short h;
short i;
short j;
short k;
short low;
int hidden_level;
struct db_var_aux_param aux_param;
char *p;
char *q;
char *name;
db_addr_t offset;
for (cur = db_vars; cur < db_evars; cur++)
if (db_cmp_variable_name(cur, db_tok_string, &aux_param))
break;
if (cur == db_evars) {
for (cur = db_vars; cur < db_evars; cur++) {
for (q = cur->name, p = db_tok_string; *q && *p == *q; p++,q++)
continue;
if (*q == '\0')
break;
}
if (cur == db_evars) {
db_error("Unknown variable\n");
return;
}
for (i = 0; *p && *p != ':' && i < cur->max_level; i++, p = q)
if ((q = db_get_suffix(p, &aux_param.suffix[i])) == 0)
break;
aux_param.level = i;
if ((*p && *p != ':') ||
(i > 0 && (aux_param.suffix[0] < cur->low ||
(cur->high >= 0 &&
aux_param.suffix[0] > cur->high)))) {
db_error("Unknown variable format\n");
return;
}
strcpy(aux_param.modif, *p ? p + 1 : "");
aux_param.thr_act = (db_option(aux_param.modif, 't') ?
db_default_act : THR_ACT_NULL);
}
if (cur->hidden_level)
if (*cur->hidden_levelp >= cur->hidden_low &&
*cur->hidden_levelp <= cur->hidden_high) {
hidden_level = 1;
aux_param.hidden_level = h = *cur->hidden_levelp;
} else {
hidden_level = 0;
aux_param.hidden_level = h = cur->hidden_low;
slen = 1;
for (k = aux_param.level > 0 ? aux_param.suffix[0] : cur->high;
k > 9; k /= 10)
slen++;
}
else
aux_param.hidden_level = -1;
if ((cur->min_level == 0 && !cur->hidden_level) || cur->high < 0)
j = 0;
else {
if (cur->min_level > 0) {
j = 1;
for (k = aux_param.level > 0 ?
aux_param.suffix[0] : cur->high; k > 9; k /= 10)
j++;
} else
j = 0;
if (cur->hidden_level && hidden_level == 0) {
j += 3;
for (k = aux_param.hidden_level >= 0 ?
aux_param.hidden_level : cur->hidden_high; k > 9; k /= 10)
j++;
}
}
len = strlen(cur->name) + j;
i = low = aux_param.level > 0 ? aux_param.suffix[0] : cur->low;
for (;;) {
db_printf(cur->name);
j = strlen(cur->name);
if (cur->high >= 0) {
if (cur->min_level > 0) {
db_printf("%d", i);
j++;
for (k = i; k > 9; k /= 10)
j++;
}
if (cur->hidden_level && hidden_level == 0) {
sl = 1;
for (k = i; k > 9; k /= 10)
sl++;
while (sl++ < slen) {
db_putchar(' ');
j++;
}
db_printf("[%d]", h);
j += 3;
for (k = h; k > 9; k /= 10)
j++;
}
}
while (j++ < len)
db_putchar(' ');
db_putchar(':');
db_putchar(' ');
if (cur->fcn) {
aux_param.suffix[0] = i;
(*cur->fcn)(cur, (db_expr_t *)0, DB_VAR_SHOW, &aux_param);
} else {
db_printf("%#n", *(cur->valuep + i));
db_find_xtrn_task_sym_and_offset(*(cur->valuep + i), &name,
&offset, TASK_NULL);
if (name != (char *)0 && offset <= db_maxoff &&
offset != *(cur->valuep + i)) {
db_printf("\t%s", name);
if (offset != 0)
db_printf("+%#r", offset);
}
}
db_putchar('\n');
if (cur->high < 0)
break;
if (aux_param.level > 0 || i++ == cur->high) {
if (!cur->hidden_level ||
hidden_level == 0 ||
h++ == cur->hidden_high)
break;
aux_param.hidden_level = h;
i = low;
}
}
}
void
db_show_variable(void)
{
struct db_variable *cur;
unsigned int l;
unsigned int len;
unsigned int sl;
unsigned int slen;
short h;
short i;
short j;
short k;
int t;
int t1;
struct db_var_aux_param aux_param;
char *name;
db_addr_t offset;
switch(t = db_read_token()) {
case tEOL:
case tEOF:
case tSEMI_COLON:
break;
case tDOLLAR:
t1 = db_read_token();
if (t1 == tIDENT) {
db_show_one_variable();
return;
}
db_error("Not a variable name after $\n");
db_unread_token(t);
return;
default:
db_error("Variable name should be prefixed with $\n");
db_unread_token(t);
return;
}
db_unread_token(t);
slen = len = 1;
for (cur = db_vars; cur < db_evars; cur++) {
if ((cur->min_level == 0 && !cur->hidden_level) || cur->high < 0)
j = 0;
else {
if (cur->min_level > 0) {
j = 1;
for (k = cur->high; k > 9; k /= 10)
j++;
} else
j = 0;
if (cur->hidden_level &&
(*cur->hidden_levelp < cur->hidden_low ||
*cur->hidden_levelp > cur->hidden_high)) {
j += 3;
for (k = cur->hidden_high; k > 9; k /= 10)
j++;
}
}
if ((l = strlen(cur->name) + j) >= len)
len = l + 1;
}
aux_param.modif = "";
aux_param.level = 1;
aux_param.thr_act = THR_ACT_NULL;
for (cur = db_vars; cur < db_evars; cur++) {
i = cur->low;
if (cur->hidden_level) {
if (*cur->hidden_levelp >= cur->hidden_low &&
*cur->hidden_levelp <= cur->hidden_high) {
h = cur->hidden_low - 1;
aux_param.hidden_level = *cur->hidden_levelp;
} else {
h = cur->hidden_low;
aux_param.hidden_level = cur->hidden_low;
}
slen = 1;
for (k = cur->high; k > 9; k /= 10)
slen++;
} else
aux_param.hidden_level = -1;
if (cur != db_vars && cur->high >= 0 &&
(cur->min_level > 0 || cur->hidden_level))
db_putchar('\n');
for (;;) {
db_printf(cur->name);
j = strlen(cur->name);
if (cur->high >= 0) {
if (cur->min_level > 0) {
db_printf("%d", i);
j++;
for (k = i; k > 9; k /= 10)
j++;
}
if (cur->hidden_level && h >= cur->hidden_low) {
sl = 1;
for (k = i; k > 9; k /= 10)
sl++;
while (sl++ < slen) {
db_putchar(' ');
j++;
}
db_printf("[%d]", h);
j += 3;
for (k = h; k > 9; k /= 10)
j++;
}
}
while (j++ < len)
db_putchar(' ');
db_putchar(':');
db_putchar(' ');
if (cur->fcn) {
aux_param.suffix[0] = i;
(*cur->fcn)(cur, (db_expr_t *)0, DB_VAR_SHOW, &aux_param);
} else {
db_printf("%#n", *(cur->valuep + i));
db_find_xtrn_task_sym_and_offset(*(cur->valuep + i), &name,
&offset, TASK_NULL);
if (name != (char *)0 && offset <= db_maxoff &&
offset != *(cur->valuep + i)) {
db_printf("\t%s", name);
if (offset != 0)
db_printf("+%#r", offset);
}
}
db_putchar('\n');
if (cur->high < 0)
break;
if (i++ == cur->high) {
if (!cur->hidden_level || h++ == cur->hidden_high)
break;
aux_param.hidden_level = h;
i = cur->low;
}
}
}
}
/*
* given a name of a machine register, return a variable pointer to it.
*/
db_variable_t
db_find_reg_name(
char *s)
{
register db_variable_t regp;
if ( s == (char *)0 )
return DB_VAR_NULL;
for (regp = db_regs; regp < db_eregs; regp++) {
if ( strcmp( s, regp->name) == 0 )
return regp;
}
return DB_VAR_NULL;
}