Source to osfmk/ddb/db_examine.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:47 wsanchez
* Import of Mac OS X kernel (~semeria)
*
* Revision 1.2 1998/04/24 19:34:23 semeria
* KDP and KDB support
*
* Revision 1.1.1.1 1998/03/07 02:26:09 wsanchez
* Import of OSF Mach kernel (~mburg)
*
* Revision 1.2.42.2 1997/09/12 17:15:15 stephen
* make x/x do zero fill right justified hex display
* [1997/09/12 16:31:04 stephen]
*
* Revision 1.2.42.1 1997/03/27 18:46:31 barbou
* Add 'p' option to the "examine" command - values in
* memory treated as addresses and rendered as sym+offset
* [1995/12/29 21:32:33 mod]
* ri-osc CR1560: make search command output address of any matching
* data it finds (so user knows it did something).
* [1995/09/20 15:24:55 bolinger]
* [97/02/25 barbou]
*
* Revision 1.2.25.5 1996/01/09 19:15:38 devrcs
* Add db_print_loc() & db_print_inst() functions.
* Make 'l' display 32 bits and new 'q' to display 64 bits.
* Allow 'u' to display unsigned decimal values (same as 'U').
* Changed declarations of 'register foo' to 'register int foo'.
* [1995/12/01 21:42:03 jfraser]
*
* Merged '64-bit safe' changes from DEC alpha port.
* [1995/11/21 18:02:58 jfraser]
*
* Revision 1.2.25.4 1995/06/13 18:21:27 sjs
* Merge with flipc_shared.
* [95/05/22 sjs]
*
* Revision 1.2.30.1 1995/04/03 17:35:17 randys
* Minor change; allow a repeat count to work properly when multiple
* modifier flags are given to the ddb 'x' command. This allows,
* for instance, examination of multiple words in activations other
* than the current one.
* [95/04/03 randys]
*
* Revision 1.2.25.3 1995/01/06 19:10:09 devrcs
* mk6 CR668 - 1.3b26 merge
* * Revision 1.2.6.7 1994/05/06 18:39:09 tmt
* Merged osc1.3dec/shared with osc1.3b19
* Merge Alpha changes into osc1.312b source code.
* 64bit cleanup.
* * End1.3merge
* [1994/11/04 08:49:22 dwm]
*
* Revision 1.2.25.2 1994/09/23 01:18:44 ezf
* change marker to not FREE
* [1994/09/22 21:09:44 ezf]
*
* Revision 1.2.25.1 1994/06/11 21:11:43 bolinger
* Merge up to NMK17.2.
* [1994/06/11 20:01:31 bolinger]
*
* Revision 1.2.23.1 1994/02/08 10:57:47 bernadat
* Fixed output of an examine command to have a power of 2
* number of fields.
* [93/09/29 paire]
*
* Added dump of hexadecimal address in each line of examine command.
* Fixed beginning of line to be always located at position 0.
* [93/08/11 paire]
* [94/02/07 bernadat]
*
* Revision 1.2.21.4 1994/03/17 22:35:27 dwm
* The infamous name change: thread_activation + thread_shuttle = thread.
* [1994/03/17 21:25:43 dwm]
*
* Revision 1.2.21.3 1994/01/12 17:50:40 dwm
* Coloc: initial restructuring to follow Utah model.
* [1994/01/12 17:13:08 dwm]
*
* Revision 1.2.21.2 1993/10/12 16:38:58 dwm
* Print '\n' in x/s statements. [rwd]
* [1993/10/12 16:14:41 dwm]
*
* Revision 1.2.6.5 1993/08/11 20:37:37 elliston
* Add ANSI Prototypes. CR #9523.
* [1993/08/11 03:33:05 elliston]
*
* Revision 1.2.6.4 1993/08/09 19:34:42 dswartz
* Add ANSI prototypes - CR#9523
* [1993/08/06 15:47:32 dswartz]
*
* Revision 1.2.6.3 1993/07/27 18:27:07 elliston
* Add ANSI prototypes. CR #9523.
* [1993/07/27 18:11:21 elliston]
*
* Revision 1.2.6.2 1993/06/09 02:20:00 gm
* Added to OSF/1 R1.3 from NMK15.0.
* [1993/06/02 20:56:10 jeffc]
*
* Revision 1.2 1993/04/19 16:01:58 devrcs
* Changes from mk78:
* Added void type to functions that needed it.
* Added init to 'size' in db_search_cmd(). Removed unused variables.
* Other cleanup to quiet gcc warnings.
* [92/05/16 jfriedl]
* x/u now examines current user space. x/t still examines user
* space of the the specified thread. x/tu is redundant.
* To examine an value as unsigned decimal, use x/U.
* [92/04/18 danner]
* [93/02/02 bruel]
*
* Remember count argument when repeating commands instead of the
* default command, also apply all the formats to current address
* instead of incrementing addresses when switching to next format.
* [[email protected]]
*
* Support 'A' format for print 'p' command [[email protected]]
* [92/12/03 bernadat]
*
* Revision 1.1 1992/09/30 02:01:01 robert
* Initial revision
*
* $EndLog$
*/
/* CMU_HIST */
/*
* Revision 2.7 91/10/09 15:59:28 af
* Revision 2.6.1.1 91/10/05 13:05:49 jeffreyh
* Supported non current task space data examination and search.
* Added 'm' format and db_xcdump to print with hex and characters.
* Added db_examine_{forward, backward}.
* Changed db_print_cmd to support variable number of parameters
* including string constant.
* Included "db_access.h".
* [91/08/29 tak]
*
* Revision 2.6.1.1 91/10/05 13:05:49 jeffreyh
* Supported non current task space data examination and search.
* Added 'm' format and db_xcdump to print with hex and characters.
* Added db_examine_{forward, backward}.
* Changed db_print_cmd to support variable number of parameters
* including string constant.
* Included "db_access.h".
* [91/08/29 tak]
*
* Revision 2.6 91/08/28 11:11:01 jsb
* Added 'A' flag to examine: just like 'a' (address), but prints addr
* as a procedure type, thus printing file/line info if available.
* Useful when called as 'x/Ai'.
* [91/08/13 18:14:55 jsb]
*
* Revision 2.5 91/05/14 15:33:31 mrt
* Correcting copyright
*
* Revision 2.4 91/02/05 17:06:20 mrt
* Changed to new Mach copyright
* [91/01/31 16:17:37 mrt]
*
* Revision 2.3 90/11/07 16:49:23 rpd
* Added db_search_cmd, db_search.
* [90/11/06 rpd]
*
* Revision 2.2 90/08/27 21:50:38 dbg
* Add 'r', 'z' to print and examine formats.
* Change calling sequence of db_disasm.
* db_examine sets db_prev and db_next instead of explicitly
* advancing dot.
* [90/08/20 dbg]
* Reflected changes in db_printsym()'s calling seq.
* [90/08/20 af]
* Reduce lint.
* [90/08/07 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 <string.h> /* For strcpy() */
#include <mach/boolean.h>
#include <machine/db_machdep.h>
#include <ddb/db_access.h>
#include <ddb/db_lex.h>
#include <ddb/db_output.h>
#include <ddb/db_command.h>
#include <ddb/db_sym.h>
#include <ddb/db_task_thread.h>
#include <ddb/db_command.h> /* For db_option() */
#include <ddb/db_examine.h>
#include <ddb/db_expr.h>
#include <kern/thread.h>
#include <kern/task.h>
#include <mach/vm_param.h>
#define db_act_to_task(thr_act) ((thr_act)? thr_act->task: TASK_NULL)
char db_examine_format[TOK_STRING_SIZE] = "x";
int db_examine_count = 1;
db_addr_t db_examine_prev_addr = 0;
thread_act_t db_examine_act = THR_ACT_NULL;
extern int db_max_width;
/* Prototypes for functions local to this file. XXX -- should be static!
*/
int db_xcdump(
db_addr_t addr,
int size,
int count,
task_t task);
int db_examine_width(
int size,
int *items,
int *remainder);
/*
* Examine (print) data.
*/
void
db_examine_cmd(
db_expr_t addr,
int have_addr,
db_expr_t count,
char * modif)
{
thread_act_t thr_act;
extern char db_last_modifier[];
if (modif[0] != '\0')
strcpy(db_examine_format, modif);
if (count == -1)
count = 1;
db_examine_count = count;
if (db_option(modif, 't')) {
if (modif == db_last_modifier)
thr_act = db_examine_act;
else if (!db_get_next_act(&thr_act, 0))
return;
} else
if (db_option(modif,'u'))
thr_act = current_act();
else
thr_act = THR_ACT_NULL;
db_examine_act = thr_act;
db_examine((db_addr_t) addr, db_examine_format, count,
db_act_to_task(thr_act));
}
void
db_examine_forward(
db_expr_t addr,
int have_addr,
db_expr_t count,
char * modif)
{
db_examine(db_next, db_examine_format, db_examine_count,
db_act_to_task(db_examine_act));
}
void
db_examine_backward(
db_expr_t addr,
int have_addr,
db_expr_t count,
char * modif)
{
db_examine(db_examine_prev_addr - (db_next - db_examine_prev_addr),
db_examine_format, db_examine_count,
db_act_to_task(db_examine_act));
}
int
db_examine_width(
int size,
int *items,
int *remainder)
{
int sz;
int entry;
int width;
width = size * 2 + 1;
sz = (db_max_width - (sizeof (void *) * 2 + 4)) / width;
for (entry = 1; (entry << 1) < sz; entry <<= 1)
continue;
sz = sizeof (void *) * 2 + 4 + entry * width;
while (sz + entry < db_max_width) {
width++;
sz += entry;
}
*remainder = (db_max_width - sz + 1) / 2;
*items = entry;
return width;
}
void
db_examine(
db_addr_t addr,
char * fmt, /* format string */
int count, /* repeat count */
task_t task)
{
int c;
db_expr_t value;
int size;
int width;
int leader;
int items;
int nitems;
char * fp;
db_addr_t next_addr;
int sz;
db_examine_prev_addr = addr;
while (--count >= 0) {
fp = fmt;
size = sizeof(int);
width = db_examine_width(size, &items, &leader);
while ((c = *fp++) != 0) {
switch (c) {
case 'b':
size = sizeof(char);
width = db_examine_width(size, &items, &leader);
break;
case 'h':
size = sizeof(short);
width = db_examine_width(size, &items, &leader);
break;
case 'l':
size = sizeof(int);
width = db_examine_width(size, &items, &leader);
break;
case 'q':
size = sizeof(long);
width = db_examine_width(size, &items, &leader);
break;
case 'a': /* address */
case 'A': /* function address */
/* always forces a new line */
if (db_print_position() != 0)
db_printf("\n");
db_prev = addr;
next_addr = addr + 4;
db_task_printsym(addr,
(c == 'a')?DB_STGY_ANY:DB_STGY_PROC,
task);
db_printf(":\t");
break;
case 'm':
db_next = db_xcdump(addr, size, count+1, task);
return;
case 't':
case 'u':
break;
default:
restart:
/* Reset next_addr in case we are printing in
multiple formats. */
next_addr = addr;
if (db_print_position() == 0) {
/* If we hit a new symbol, print it */
char * name;
db_addr_t off;
db_find_task_sym_and_offset(addr,&name,&off,task);
if (off == 0)
db_printf("\r%s:\n", name);
db_printf("%#n: ", addr);
for (sz = 0; sz < leader; sz++)
db_putchar(' ');
db_prev = addr;
nitems = items;
}
switch (c) {
case 'p': /* Addrs rendered symbolically. */
if( size == sizeof(void *) ) {
char *symName;
db_addr_t offset;
items = 1;
value = db_get_task_value( next_addr,
sizeof(db_expr_t), FALSE, task );
db_find_task_sym_and_offset( value,
&symName, &offset, task);
db_printf("\n\t*%8x(%8X) = %s",
next_addr, value, symName );
if( offset ) {
db_printf("+%X", offset );
}
next_addr += size;
}
break;
case 'r': /* signed, current radix */
for (sz = size, next_addr = addr;
sz >= sizeof (db_expr_t);
sz -= sizeof (db_expr_t)) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr,
sizeof (db_expr_t),
TRUE,task);
db_printf("%-*r", width, value);
next_addr += sizeof (db_expr_t);
}
if (sz > 0) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr, sz,
TRUE, task);
db_printf("%-*R", width, value);
next_addr += sz;
}
break;
#ifdef APPLE
case 'X': /* unsigned hex */
#endif
case 'x': /* unsigned hex */
for (sz = size, next_addr = addr;
sz >= sizeof (db_expr_t);
sz -= sizeof (db_expr_t)) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr,
sizeof (db_expr_t),
FALSE,task);
#ifdef APPLE
if ( c == 'X')
db_printf("%0*X ", 2*size, value);
else
db_printf("%-*x", width, value);
#else
db_printf("%-*x", width, value);
#endif
next_addr += sizeof (db_expr_t);
}
if (sz > 0) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr, sz,
FALSE, task);
#ifdef APPLE
if ( c == 'X')
db_printf("%0*X ", 2*size, value);
else
db_printf("%-*X", width, value);
#else
db_printf("%-*X", width, value);
#endif
next_addr += sz;
}
break;
case 'z': /* signed hex */
for (sz = size, next_addr = addr;
sz >= sizeof (db_expr_t);
sz -= sizeof (db_expr_t)) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr,
sizeof (db_expr_t),
TRUE, task);
db_printf("%-*z", width, value);
next_addr += sizeof (db_expr_t);
}
if (sz > 0) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr,sz,
TRUE,task);
db_printf("%-*Z", width, value);
next_addr += sz;
}
break;
case 'd': /* signed decimal */
for (sz = size, next_addr = addr;
sz >= sizeof (db_expr_t);
sz -= sizeof (db_expr_t)) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr,
sizeof (db_expr_t),
TRUE,task);
db_printf("%-*d", width, value);
next_addr += sizeof (db_expr_t);
}
if (sz > 0) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr, sz,
TRUE, task);
db_printf("%-*D", width, value);
next_addr += sz;
}
break;
case 'U': /* unsigned decimal */
case 'u':
for (sz = size, next_addr = addr;
sz >= sizeof (db_expr_t);
sz -= sizeof (db_expr_t)) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr,
sizeof (db_expr_t),
FALSE,task);
db_printf("%-*u", width, value);
next_addr += sizeof (db_expr_t);
}
if (sz > 0) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr, sz,
FALSE, task);
db_printf("%-*U", width, value);
next_addr += sz;
}
break;
case 'o': /* unsigned octal */
for (sz = size, next_addr = addr;
sz >= sizeof (db_expr_t);
sz -= sizeof (db_expr_t)) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr,
sizeof (db_expr_t),
FALSE,task);
db_printf("%-*o", width, value);
next_addr += sizeof (db_expr_t);
}
if (sz > 0) {
if (nitems-- == 0) {
db_putchar('\n');
goto restart;
}
value = db_get_task_value(next_addr, sz,
FALSE, task);
db_printf("%-*o", width, value);
next_addr += sz;
}
break;
case 'c': /* character */
for (sz = 0, next_addr = addr;
sz < size;
sz++, next_addr++) {
value = db_get_task_value(next_addr,1,
FALSE,task);
if ((value >= ' ' && value <= '~') ||
value == '\n' ||
value == '\t')
db_printf("%c", value);
else
db_printf("\\%03o", value);
}
break;
case 's': /* null-terminated string */
size = 0;
for (;;) {
value = db_get_task_value(next_addr,1,
FALSE,task);
next_addr += 1;
size++;
if (value == 0)
break;
if (value >= ' ' && value <= '~')
db_printf("%c", value);
else
db_printf("\\%03o", value);
}
break;
case 'i': /* instruction */
next_addr = db_disasm(addr, FALSE, task);
size = next_addr - addr;
break;
case 'I': /* instruction, alternate form */
next_addr = db_disasm(addr, TRUE, task);
size = next_addr - addr;
break;
default:
break;
}
if (db_print_position() != 0)
db_end_line();
break;
}
}
addr = next_addr;
}
db_next = addr;
}
/*
* Print value.
*/
char db_print_format = 'x';
void
db_print_cmd(void)
{
db_expr_t value;
int t;
task_t task = TASK_NULL;
if ((t = db_read_token()) == tSLASH) {
if (db_read_token() != tIDENT) {
db_printf("Bad modifier \"/%s\"\n", db_tok_string);
db_error(0);
/* NOTREACHED */
}
if (db_tok_string[0])
db_print_format = db_tok_string[0];
if (db_option(db_tok_string, 't')) {
if (db_default_act)
task = db_default_act->task;
if (db_print_format == 't')
db_print_format = db_tok_string[1];
}
} else
db_unread_token(t);
for ( ; ; ) {
t = db_read_token();
if (t == tSTRING) {
db_printf("%s", db_tok_string);
continue;
}
db_unread_token(t);
if (!db_expression(&value))
break;
switch (db_print_format) {
case 'a':
case 'A':
db_task_printsym((db_addr_t)value,
(db_print_format == 'a') ? DB_STGY_ANY:
DB_STGY_PROC,
task);
break;
case 'r':
db_printf("%11r", value);
break;
case 'x':
db_printf("%08x", value);
break;
case 'z':
db_printf("%8z", value);
break;
case 'd':
db_printf("%11d", value);
break;
case 'u':
db_printf("%11u", value);
break;
case 'o':
db_printf("%16o", value);
break;
case 'c':
value = value & 0xFF;
if (value >= ' ' && value <= '~')
db_printf("%c", value);
else
db_printf("\\%03o", value);
break;
default:
db_printf("Unknown format %c\n", db_print_format);
db_print_format = 'x';
db_error(0);
}
}
}
void
db_print_loc(
db_addr_t loc,
task_t task)
{
db_task_printsym(loc, DB_STGY_PROC, task);
}
void
db_print_inst(
db_addr_t loc,
task_t task)
{
(void) db_disasm(loc, TRUE, task);
}
void
db_print_loc_and_inst(
db_addr_t loc,
task_t task)
{
db_task_printsym(loc, DB_STGY_PROC, task);
db_printf(":\t");
(void) db_disasm(loc, TRUE, task);
}
/*
* Search for a value in memory.
* Syntax: search [/bhl] addr value [mask] [,count] [thread]
*/
void
db_search_cmd(void)
{
int t;
db_addr_t addr;
int size = 0;
db_expr_t value;
db_expr_t mask;
db_addr_t count;
thread_act_t thr_act;
boolean_t thread_flag = FALSE;
register char *p;
t = db_read_token();
if (t == tSLASH) {
t = db_read_token();
if (t != tIDENT) {
bad_modifier:
db_printf("Bad modifier \"/%s\"\n", db_tok_string);
db_flush_lex();
return;
}
for (p = db_tok_string; *p; p++) {
switch(*p) {
case 'b':
size = sizeof(char);
break;
case 'h':
size = sizeof(short);
break;
case 'l':
size = sizeof(long);
break;
case 't':
thread_flag = TRUE;
break;
default:
goto bad_modifier;
}
}
} else {
db_unread_token(t);
size = sizeof(int);
}
if (!db_expression((db_expr_t *) &addr)) {
db_printf("Address missing\n");
db_flush_lex();
return;
}
if (!db_expression(&value)) {
db_printf("Value missing\n");
db_flush_lex();
return;
}
if (!db_expression(&mask))
mask = ~0;
t = db_read_token();
if (t == tCOMMA) {
if (!db_expression((db_expr_t *) &count)) {
db_printf("Count missing\n");
db_flush_lex();
return;
}
} else {
db_unread_token(t);
count = -1; /* effectively forever */
}
if (thread_flag) {
if (!db_get_next_act(&thr_act, 0))
return;
} else
thr_act = THR_ACT_NULL;
db_search(addr, size, value, mask, count, db_act_to_task(thr_act));
}
void
db_search(
db_addr_t addr,
int size,
db_expr_t value,
db_expr_t mask,
unsigned int count,
task_t task)
{
while (count-- != 0) {
db_prev = addr;
if ((db_get_task_value(addr,size,FALSE,task) & mask) == value)
break;
addr += size;
}
db_printf("0x%x: ", addr);
db_next = addr;
}
#define DB_XCDUMP_NC 16
int
db_xcdump(
db_addr_t addr,
int size,
int count,
task_t task)
{
register int i, n;
db_expr_t value;
int bcount;
db_addr_t off;
char *name;
char data[DB_XCDUMP_NC];
db_find_task_sym_and_offset(addr, &name, &off, task);
for (n = count*size; n > 0; n -= bcount) {
db_prev = addr;
if (off == 0) {
db_printf("%s:\n", name);
off = -1;
}
db_printf("%0*X:%s", 2*sizeof(db_addr_t), addr,
(size != 1) ? " " : "" );
bcount = ((n > DB_XCDUMP_NC)? DB_XCDUMP_NC: n);
if (trunc_page(addr) != trunc_page(addr+bcount-1)) {
db_addr_t next_page_addr = trunc_page(addr+bcount-1);
if (!DB_CHECK_ACCESS(next_page_addr, sizeof(int), task))
bcount = next_page_addr - addr;
}
db_read_bytes((vm_offset_t)addr, bcount, data, task);
for (i = 0; i < bcount && off != 0; i += size) {
if (i % 4 == 0)
db_printf(" ");
value = db_get_task_value(addr, size, FALSE, task);
db_printf("%0*x ", size*2, value);
addr += size;
db_find_task_sym_and_offset(addr, &name, &off, task);
}
db_printf("%*s",
((DB_XCDUMP_NC-i)/size)*(size*2+1)+(DB_XCDUMP_NC-i)/4,
"");
bcount = i;
db_printf("%s*", (size != 1)? " ": "");
for (i = 0; i < bcount; i++) {
value = data[i];
db_printf("%c", (value >= ' ' && value <= '~')? value: '.');
}
db_printf("*\n");
}
return(addr);
}