#include <string.h>
#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>
#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_t db_examine_act = THREAD_NULL;
extern int db_max_width;
int db_xcdump(
db_addr_t addr,
int size,
int count,
task_t task);
int db_examine_width(
int size,
int *items,
int *remainder);
extern char db_last_modifier[];
void
db_examine_cmd(db_expr_t addr, __unused boolean_t have_addr, db_expr_t count,
char *modif)
{
thread_t thr_act;
if (modif[0] != '\0')
strlcpy(db_examine_format, modif, TOK_STRING_SIZE);
if (count == (db_expr_t)-1)
count = 1;
db_examine_count = (int)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_thread();
else
thr_act = THREAD_NULL;
db_examine_act = thr_act;
db_examine((db_addr_t) addr, db_examine_format, (int)count,
db_act_to_task(thr_act));
}
void
db_examine_forward(__unused db_expr_t addr, __unused boolean_t have_addr,
__unused db_expr_t count, __unused char *modif)
{
db_examine(db_next, db_examine_format, db_examine_count,
db_act_to_task(db_examine_act));
}
void
db_examine_backward(__unused db_expr_t addr, __unused boolean_t have_addr,
__unused db_expr_t count, __unused 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,
int count,
task_t task)
{
int c;
db_expr_t value;
int size;
int width;
int leader;
int items;
int nitems = 0;
char * fp;
db_addr_t next_addr = 0;
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':
case 'A':
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:
next_addr = addr;
if (db_print_position() == 0) {
const 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("%#lln: ", (unsigned long long)addr);
for (sz = 0; sz < leader; sz++)
db_putchar(' ');
db_prev = addr;
nitems = items;
}
switch (c) {
case 'p':
if( size == sizeof(void *) ) {
const 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*%8llX(%8llX) = %s",
(unsigned long long)next_addr, (unsigned long long)value, symName );
if( offset ) {
db_printf("+%llX", (unsigned long long)offset );
}
next_addr += size;
}
break;
case 'r':
for (sz = size, next_addr = addr;
sz >= (signed)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("%-*llr", width, (unsigned long long)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("%-*llR", width, (unsigned long long)value);
next_addr += sz;
}
break;
case 'X':
case 'x':
for (sz = size, next_addr = addr;
sz >= (signed)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);
if ( c == 'X')
db_printf("%0*llX ", 2*size, (unsigned long long)value);
else
db_printf("%-*llx", width, (unsigned long long)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);
if ( c == 'X')
db_printf("%0*llX ", 2*size, (unsigned long long)value);
else
db_printf("%-*llX", width, (unsigned long long)value);
next_addr += sz;
}
break;
case 'z':
for (sz = size, next_addr = addr;
sz >= (signed)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("%-*llz", width, (unsigned long long)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("%-*llZ", width, (unsigned long long)value);
next_addr += sz;
}
break;
case 'd':
for (sz = size, next_addr = addr;
sz >= (signed)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("%-*lld", width, (unsigned long long)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("%-*llD", width, (unsigned long long)value);
next_addr += sz;
}
break;
case 'U':
case 'u':
for (sz = size, next_addr = addr;
sz >= (signed)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("%-*llu", width, (unsigned long long)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("%-*llU", width, (unsigned long long)value);
next_addr += sz;
}
break;
case 'o':
for (sz = size, next_addr = addr;
sz >= (signed)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("%-*llo", width, (unsigned long long)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("%-*llo", width, (unsigned long long)value);
next_addr += sz;
}
break;
case 'c':
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("%llc", (unsigned long long)value);
else
db_printf("\\%03llo", (unsigned long long)value);
}
break;
case 's':
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("%llc", (unsigned long long)value);
else
db_printf("\\%03llo", (unsigned long long)value);
}
break;
case 'i':
next_addr = db_disasm(addr, FALSE, task);
size = (int)(next_addr - addr);
break;
case 'I':
next_addr = db_disasm(addr, TRUE, task);
size = (int)(next_addr - addr);
break;
default:
break;
}
if (db_print_position() != 0)
db_end_line();
break;
}
}
addr = next_addr;
}
db_next = addr;
}
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);
}
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("%11llr", (unsigned long long)value);
break;
case 'X':
db_printf("%016llX", (unsigned long long)value);
break;
case 'x':
db_printf("%016llx", (unsigned long long)value);
break;
case 'z':
db_printf("%16llz", (unsigned long long)value);
break;
case 'd':
db_printf("%11lld", (unsigned long long)value);
break;
case 'u':
db_printf("%11llu", (unsigned long long)value);
break;
case 'o':
db_printf("%16llo", (unsigned long long)value);
break;
case 'c':
value = value & 0xFF;
if (value >= ' ' && value <= '~')
db_printf("%llc", (unsigned long long)value);
else
db_printf("\\%03llo", (unsigned long long)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);
}
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_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;
}
if (thread_flag) {
if (!db_get_next_act(&thr_act, 0))
return;
} else
thr_act = THREAD_NULL;
db_search(addr, size, value, mask, (unsigned int)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%llx: ", (unsigned long long)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;
const 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*llX:%s", 2*sizeof(db_addr_t),(unsigned long long) 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((vm_offset_t)next_page_addr, (int)sizeof(int), task))
bcount = (int)(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*llX ", size*2, (unsigned long long)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("%llc", (value >= ' ' && value <= '~')? (unsigned long long)value: (unsigned long long)'.');
}
db_printf("*\n");
}
return((int)addr);
}