diff options
author | Ranjith Kumaran <ranjith@cygnus.com> | 2000-03-17 22:48:54 +0000 |
---|---|---|
committer | Ranjith Kumaran <ranjith@cygnus.com> | 2000-03-17 22:48:54 +0000 |
commit | 03261851a10dd2d6900a0a00a7515a0a46fb5d76 (patch) | |
tree | 7c22ac6cbbc99fd5cd1b5426853be8d4fd7bfcf1 /libgloss/debug.c | |
parent | fae4c299f14fc23e2829c8656992eba21f79242a (diff) | |
download | newlib-03261851a10dd2d6900a0a00a7515a0a46fb5d76.zip newlib-03261851a10dd2d6900a0a00a7515a0a46fb5d76.tar.gz newlib-03261851a10dd2d6900a0a00a7515a0a46fb5d76.tar.bz2 |
20000317 sourceware import
Diffstat (limited to 'libgloss/debug.c')
-rw-r--r-- | libgloss/debug.c | 848 |
1 files changed, 848 insertions, 0 deletions
diff --git a/libgloss/debug.c b/libgloss/debug.c new file mode 100644 index 0000000..3b1c8ff --- /dev/null +++ b/libgloss/debug.c @@ -0,0 +1,848 @@ +/* + * Copyright (c) 1995, 1996 Cygnus Support + * + * The authors hereby grant permission to use, copy, modify, distribute, + * and license this software and its documentation for any purpose, provided + * that existing copyright notices are retained in all copies and that this + * notice is included verbatim in any distributions. No written agreement, + * license, or royalty fee is required for any of the authorized uses. + * Modifications to this software may be copyrighted by their authors + * and need not follow the licensing terms described here, provided that + * the new terms are clearly indicated on the first page of each file where + * they apply. + */ + +/* + * A debug packet whose contents are <data> looks like: + * + * $ <data> # CSUM1 CSUM2 + * + * <data> must be ASCII alphanumeric and cannot include characters + * '$' or '#'. If <data> starts with two characters followed by + * ':', then the existing stubs interpret this as a sequence number. + * + * CSUM1 and CSUM2 are ascii hex representation of an 8-bit + * checksum of <data>, the most significant nibble is sent first. + * the hex digits 0-9,a-f are used. + * + * We respond with: + * + * + - if CSUM is correct and ready for next packet + * - - if CSUM is incorrect + * + * <data> is as follows: + * Most values are encoded in ascii hex digits. + */ + +#include "debug.h" +#include <signal.h> + +/* + * buffers that hold the packets while they're being constructed. + */ +char packet_in_buf[BUFMAX]; +char packet_out_buf[BUFMAX]; +int packet_index; + +/* + * indicate to caller of mem2hex or hex2mem that there has been an error. + * 0 means ok, 1 means error + */ +volatile int mem_err = 0; + +/* + * 1 means print debugging messages from the target, 0 means be quiet. This is + * changed by gdb_debug(). + */ +int remote_debug = 0; + +/* + * indicate whether the debug vectors ahave been initialized + * 0 means not yet, 1 means yep, it's ready. + */ +int initialized = 0; + +/* + * These variables are instantialted in the GDB stub code. + */ + +/* this is a list of signal to exception mappings. */ +extern struct trap_info hard_trap_info[]; + +/* this is a memory fault exception handler, used by mem2hex & hex2mem */ +extern void set_mem_fault_trap(); + +/* + * print debugging messages. This uses print, rather than one of the + * stdio routines, cause if there are stack or memory problems, the + * stdio routines don't work. + * params are the debug level, and the string to print + * it doesn't return anything. + */ +void +debuglog(int level, char *msg) +{ + char *p; + unsigned char buf[BUFMAX]; + char newmsg[BUFMAX]; + int i; + + if (level > remote_debug) + return; + + if ((level <0) || (level > 100)) { + print ("ERROR: debug print level out of range"); + return; + } + + /* convert some characters so it'll look right in the log */ + p = newmsg; + for (i = 0 ; msg[i] != '\0'; i++) { + if (i > BUFMAX) + print ("\r\nERROR: Debug message too long\r\n"); + switch (msg[i]) { + case '\n': /* newlines */ + *p++ = '\\'; + *p++ = 'n'; + continue; + case '\r': /* carriage returns */ + *p++ = '\\'; + *p++ = 'r'; + continue; + case '\033': /* escape */ + *p++ = '\\'; + *p++ = 'e'; + continue; + case '\t': /* tab */ + *p++ = '\\'; + *p++ = 't'; + continue; + case '\b': /* backspace */ + *p++ = '\\'; + *p++ = 'b'; + continue; + default: /* no change */ + *p++ = msg[i]; + } + + if (msg[i] < 26) { /* modify control characters */ + *p++ = '^'; + *p++ = msg[i] + 'A'; + continue; + } + if (msg[i] >= 127) { /* modify control characters */ + *p++ = '!'; + *p++ = msg[i] + 'A'; + continue; + } + } + *p = '\0'; /* terminate the string */ + print (newmsg); + print ("\r\n"); +} + +/* + * convert an ascii hex digit to a number. + * param is hex digit. + * returns a decimal digit. + */ +int +hex2digit (int digit) +{ + if (digit == 0) + return 0; + + if (digit >= '0' && digit <= '9') + return digit - '0'; + if (digit >= 'a' && digit <= 'f') + return digit - 'a' + 10; + if (digit >= 'A' && digit <= 'F') + return digit - 'A' + 10; + + /* shouldn't ever get this far */ + return ERROR; +} + +/* + * convert number NIB to a hex digit. + * param is a decimal digit. + * returns a hex digit. + */ +char +digit2hex(int digit) +{ + if (digit < 10) + return '0' + digit; + else + return 'a' + digit - 10; +} + +/* + * Convert the memory pointed to by mem into hex, placing result in buf. + * Return a pointer to the last char put in buf (null), in case of mem fault, + * return 0. + * If MAY_FAULT is non-zero, then we will handle memory faults by returning + * a 0, else treat a fault like any other fault in the stub. + */ +unsigned char * +mem2hex(unsigned char *mem, unsigned char *buf, int count, int may_fault) +{ + unsigned char ch; + + DEBUG (1, "In mem2hex"); + + set_mem_fault_trap(MAY_FAULT); + + while (count-- > 0) { + ch = *mem++; + if (mem_err) { + DEBUG (1, "memory fault in mem2hex"); + return 0; + } + *buf++ = digit2hex(ch >> 4); + *buf++ = digit2hex(ch & 0xf); + } + + *buf = 0; + + set_mem_fault_trap(OK); + + return buf; +} + +/* + * Convert the hex array pointed to by buf into binary to be placed in mem + * return a pointer to the character AFTER the last byte written + */ +unsigned char * +hex2mem(unsigned char *buf, unsigned char *mem, int count, int may_fault) +{ + int i; + unsigned char ch; + + DEBUG (1, "In hex2mem"); + + set_mem_fault_trap(may_fault); + + for (i=0; i<count; i++) { + ch = hex2digit(*buf++) << 4; + ch |= hex2digit(*buf++); + *mem++ = ch; + if (mem_err) + return 0; + } + + set_mem_fault_trap(0); + + return mem; +} + +/* + * while we find nice hex chars, build an int. + * param is a pointer to the string. + * returns the int in the param field, and the number of chars processed. + */ +int +hex2int (char **ptr, int *intValue) +{ + int numChars = 0; + int hexValue; + + *intValue = 0; + + while (**ptr) + { + hexValue = hex2digit(**ptr); + if (hexValue < 0) + break; + + *intValue = (*intValue << 4) | hexValue; + numChars ++; + (*ptr)++; + } + return (numChars); +} + +/* + * Scan for the sequence $<data>#<checksum> + */ +void +getpacket(unsigned char *buffer) +{ + unsigned char checksum; + unsigned char xmitcsum; + int i; + int count; + unsigned char ch; + + do { + /* wait around for the start character, ignore all other characters */ + while ((ch = (inbyte() & 0x7f)) != '$') ; + + checksum = 0; + xmitcsum = -1; + + count = 0; + + /* now, read until a # or end of buffer is found */ + while (count < BUFMAX) { + ch = inbyte() & 0x7f; + if (ch == '#') + break; + checksum = checksum + ch; + buffer[count] = ch; + count = count + 1; + } + + if (count >= BUFMAX) + continue; + + buffer[count] = 0; + + if (ch == '#') { + xmitcsum = hex2digit(inbyte() & 0x7f) << 4; + xmitcsum |= hex2digit(inbyte() & 0x7f); +#if 1 + /* Humans shouldn't have to figure out checksums to type to it. */ + outbyte ('+'); + return; +#endif + if (checksum != xmitcsum) + outbyte('-'); /* failed checksum */ + else { + outbyte('+'); /* successful transfer */ + /* if a sequence char is present, reply the sequence ID */ + if (buffer[2] == ':') { + outbyte(buffer[0]); + outbyte(buffer[1]); + /* remove sequence chars from buffer */ + count = strlen(buffer); + for (i=3; i <= count; i++) + buffer[i-3] = buffer[i]; + } + } + } + } + while (checksum != xmitcsum); +} + +/* + * Send the packet in buffer. + */ +void +putpacket(unsigned char *buffer) +{ + unsigned char checksum; + int count; + unsigned char ch; + + /* $<packet info>#<checksum>. */ + do { + outbyte('$'); + checksum = 0; + count = 0; + + while (ch = buffer[count]) { + if (! outbyte(ch)) + return; + checksum += ch; + count += 1; + } + + outbyte('#'); + outbyte(digit2hex(checksum >> 4)); + outbyte(digit2hex(checksum & 0xf)); + + } + while ((inbyte() & 0x7f) != '+'); +} + +/* + * + */ +void +gdb_event_loop(int sigval, unsigned long *registers) +{ + int addr; + int length; + unsigned char *ptr; + ptr = packet_out_buf; + + DEBUG (1, "In gdb_event_loop"); + + while (1) { + packet_out_buf[0] = 0; + + getpacket(packet_in_buf); + ptr = &packet_in_buf[1]; + + switch (packet_in_buf[0]) { + case '?': /* get the last known signal */ + gdb_last_signal(sigval); + break; + + case 'd': /* toggle debug messages from the stub */ + gdb_toggle(); + break; + + case 'g': /* return the value of the CPU registers */ + target_read_registers(registers); + break; + + case 'G': /* set the value of the CPU registers - return OK */ + target_write_registers(registers); + break; + + case 'm': /* mAA..AA,LLLL Read LLLL bytes at address AA..AA */ + /* Try to read %x,%x. */ + if (hex2int((char **)&ptr, &addr) + && *ptr++ == ',' + && hex2int((char **)&ptr, &length)) { + gdb_read_memory(addr, length); + } else { + make_return_packet(1); + } + break; + + case 'M': /* MAA..AA,LLLL: Write LLLL bytes at address AA.AA return OK */ + /* Try to read '%x,%x:'. */ + if (hex2int((char **)&ptr, &addr) + && *ptr++ == ',' + && hex2int((char **)&ptr, &length) + && *ptr++ == ':') { + gdb_write_memory (addr, length, ptr); + } else { + make_return_packet(2); + } + break; + + case 'c': /* cAA..AA Continue at address AA..AA(optional) */ + /* try to read optional parameter, pc unchanged if no parm */ + if (hex2int((char **)&ptr, &addr)) { + write_pc(registers, addr); + } + + /* + * we need to flush the instruction cache here, as we may have + * deposited a breakpoint, and the icache probably has no way of + * knowing that a data ref to some location may have changed + * something that is in the instruction cache. + */ + + flush_i_cache(); + /* by returning, we pick up execution where we left off */ + return; + + /* kill the program */ + case 'k' : + gdb_kill(); + break; + case 'r': /* Reset */ + target_reset(); + break; + } /* switch */ + + /* reply to the request */ + putpacket(packet_out_buf); + } + DEBUG (1, "Leaving handle_exception()"); +} + +/* Convert the hardware trap type code to a unix signal number. */ + +int +computeSignal(int tt) +{ + struct trap_info *ht; + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) + if (ht->tt == tt) + return ht->signo; + + return SIGHUP; /* default for things we don't know about */ +} + +/* + * Set up exception handlers for tracing and breakpoints + */ +void +set_debug_traps() +{ + struct trap_info *ht; + + DEBUG (1, "Entering set_debug_traps()"); + + if (hard_trap_info->tt == 0) { + print ("ERROR: ARG#$@%^&*!! no hard trap info!!\r\n"); + } + + for (ht = hard_trap_info; ht->tt && ht->signo; ht++) { + exception_handler(ht->tt, (unsigned long)default_trap_hook); + } + + /* In case GDB is started before us, ack any packets (presumably + "$?#xx") sitting there. */ + + outbyte ('+'); + initialized = 1; + + DEBUG (1, "Leaving set_debug_traps()"); +} + +/* + * make a return packet. + * param is the value to return. + * 0 = OK, any other value is converted to a two digit hex number. + * returns a string or "OK" or "ENN", where NN is the error number. Each N + * is an ASCII encoded hex digit. + */ +char * +make_return_packet(int val) +{ + if (val == 0) { + packet_out_buf[0] = 'O'; + packet_out_buf[1] = 'K'; + packet_out_buf[2] = 0; + } else { + packet_out_buf[0] = 'E'; + packet_out_buf[1] = digit2hex((val >> 4) & 0xf); + packet_out_buf[2] = digit2hex(val & 0xf); + packet_out_buf[3] = 0; + } + return(packet_out_buf); +} + +/* + * g - read registers. + * no params. + * returns a vector of words, size is NUM_REGS. + */ +char * +gdb_read_registers() +{ +} + +/* + * G - write registers. + * param is a vector of words, size is NUM_REGS. + * returns an OK or an error number. + */ +char * +gdb_write_registers(char *regs) +{ +} + +/* + * m - read memory. + * params are the address to start the read at and the number of + * bytes to read. + * returns a vector of nbytes or an error number. + * Can be fewer bytes than requested if able to read only part of the + * data. + */ +char * +gdb_read_memory(long addr, int nbytes) +{ + if (mem2hex((char *)addr, packet_out_buf, nbytes, MAY_FAULT)) + return(packet_out_buf); + else { + return(make_return_packet(3)); + } +} + +/* + * M write memory + * params are the address to start writing to, the number of + * bytes to write, and the new values of the bytes. + * returns an OK or an error number. + */ +char * +gdb_write_memory(long addr, int nbytes, char *mem) +{ + if (hex2mem(mem, (char *)addr, nbytes, MAY_FAULT)) + return(make_return_packet(OK)); + else { + return(make_return_packet(3)); + } +} + +/* + * c - continue at address. + * param is the address to start at, and an optional signal. If + * sig is zero, then ignore it. + * returns an OK or an error number. + */ +char * +gdb_continue(int sig, long addr) +{ +} + +/* + * s - step instruction(s) + * param is the address to start at, and an optional signal. If + * sig is zero, then ignore it. + * returns an OK or an error number. + */ +char * +gdb_step(int sig, long addr) +{ +} + +/* + * k - kill program. + * no params. + * returns an OK or an error number. + */ +char * +gdb_kill() +{ + /* generically, we can't do anything for this command */ + return(make_return_packet(OK)); +} + +/* + * ? - last signal. + * no params. + * returns the last signal number. + */ +char * +gdb_last_signal(int val) +{ + DEBUG (1, "Entering gdb_last_signal()"); + + packet_out_buf[0] = 'S'; + packet_out_buf[1] = digit2hex(val >> 4); + packet_out_buf[2] = digit2hex(val & 0xf); + packet_out_buf[3] = 0; + + DEBUG (1, "Leaving gdb_last_signal()"); + return (packet_out_buf); +} + +/* + * b - change baud rate. + * param is the new baudrate + * returns the baud rate. + */ +char * +gdb_baudrate(int baud) +{ + /* generically, we can't do anything for this command */ + return(make_return_packet(OK)); +} + +/* + * T - dump state. + * no params. + * returns the signal number, the registers, the thread ID, and + * possible extensions in a vector that looks like: + * TAAn...:r...;n...:r...;n...:r...; where: + * AA = signal number + * n... = register number (hex) + * r... = register contents + * n... = `thread' + * r... = thread process ID. This is a hex integer. + * n... = other string not starting with valid hex digit. + * gdb should ignore this n,r pair and go on to + * the next. This way we can extend the protocol. + */ +char * +gdb_dump_state() +{ +} + +/* + * D - host requests a detach + * no params. + * returns either a S, T, W, or X command. + * returns an OK or an error number. + */ +char * +gdb_detach() +{ +} + +/* + * H - set thread. + * params are the command to execute and the thread ID. + * cmd = 'c' for thread used in step and continue; + * cmd = 'g' for thread used in other operations. + * tid = -1 for all threads. + * tid = zero, pick a thread,any thread. + * returns an OK or an error number. + */ +char * +gdb_set_thread(int cmd, int tid) +{ + /* generically, we can't do anything for this command */ + return(make_return_packet(OK)); +} + +/* + * p - read one register. + * param is the register number. + * returns the register value or ENN. + */ +char * +gdb_read_reg(int reg) +{ + /* generically, we can't do anything for this command */ + return(make_return_packet(OK)); +} + +/* + * P - write one register. + * params are the register number, and it's new value. + * returns the register value or ENN. + */ +char * +gdb_write_reg(int reg, long val) +{ + /* generically, we can't do anything for this command */ + + return(make_return_packet(OK)); +} + +/* + * W - process exited. + * no params. + * returns the exit status. + */ +char * +gdb_exited() +{ + /* generically, we can't do anything for this command */ + return(make_return_packet(OK)); +} + +/* + * X - process terminated. + * no params. + * returns the last signal. + */ +char * +gdb_terminated() +{ +} + +/* + * O - hex encoding. + * params are a vector of bytes, and the number of bytes to encode. + * returns a vector of ASCII encoded hex numbers. + */ +char * +gdb_hex(char *str, int nbytes) +{ +} + +/* + * A - tread alive request. + * param is the thread ID. + * returns an OK or an error number. + */ +char * +gdb_thread_alive(int tid) +{ + /* generically, we can't do anything for this command */ + return(make_return_packet(OK)); +} + +/* + * ! - extended protocol. + * no params. + * returns an OK or an error number. + */ +char * +gdb_extended() +{ + /* generically, we can't do anything for this command */ + return(make_return_packet(OK)); +} + +/* + * d - toggle gdb stub diagnostics. + * no params. + * returns an OK or an error number. + */ +char * +gdb_debug() +{ + if (remote_debug > 0) + remote_debug = 0; + else + remote_debug = 1; + + return(make_return_packet(OK)); +} + +/* + * d - toggle gdb stub. + * no params. + * returns an OK or an error number. + */ +char * +gdb_toggle() +{ + static int level = 0; + + if (remote_debug) { + level = remote_debug; + remote_debug = 0; + } else { + remote_debug = level; + } + + return(make_return_packet(OK)); +} + +/* + * r - reset target + * no params. + * returns an OK or an error number. + */ +char * +gdb_reset() +{ + /* generically, we can't do anything for this command */ + return(make_return_packet(OK)); +} + +/* + * t - search backwards. + * params are the address to start searching from, a pattern to match, and + * the mask to use. + * FIXME: not entirely sure what this is supposed to return. + */ +char * +gdb_search(long addr, long pat, long mask) +{ + /* generically, we can't do anything for this command */ + return(make_return_packet(OK)); +} + +/* + * q - general get query. + * param is a string, that's the query to be executed. + * FIXME: not entirely sure what this is supposed to return. + */ +char * +gdb_get_query(char *query) +{ + /* generically, we can't do anything for this command */ + return(make_return_packet(OK)); +} + +/* + * Q - general set query + * param is a string, that's the query to be executed. + * FIXME: not entirely sure what this means. + * returns an OK or an error number. + */ +char * +gdb_set(char *query) +{ + /* generically, we can't do anything for this command */ + return(make_return_packet(OK)); +} + + |