- solve lots of problems with stuck GDB connections, making it impossible to connect to GDB, handle timeout of acknowledgement, etc.

- "monitor halt/resume" now works correctly
- "monitor sleep 10000" no longer makes the GDB protocol lock up. There is an error message and the protocol recovers nicely afterwards.
- it's now possible to connect to a target which needs a reset before halt works.
- handle failed memory access more gracefully. Connection is now closed instead of OpenOCD quitting.
- *much* improved handling of 2 second timeout on memory read packets. Especially important w/mouseover evaluation of  variables in Eclipse.
- fixed memory leak upon failed memory packet reply.
- 'O' packets w/progress info is no longer sent out randomly.
- faster packet reply code.
- Thanks to Øyvind Harboe for this patch

git-svn-id: svn://svn.berlios.de/openocd/trunk@300 b42882b7-edfa-0310-969c-e2dbd0fdcd60
__archive__
ntfreak 2008-02-16 15:21:13 +00:00
parent a2595950c7
commit e859281eb3
2 changed files with 183 additions and 99 deletions

View File

@ -43,11 +43,11 @@
#endif #endif
static unsigned short gdb_port; static unsigned short gdb_port;
static const char *DIGITS = "0123456789abcdef";
static void gdb_log_callback(void *privData, const char *file, int line, static void gdb_log_callback(void *priv, const char *file, int line,
const char *function, const char *format, va_list args); const char *function, const char *format, va_list args);
enum gdb_detach_mode enum gdb_detach_mode
{ {
GDB_DETACH_RESUME, GDB_DETACH_RESUME,
@ -107,10 +107,38 @@ int gdb_get_char(connection_t *connection, int* next_char)
return ERROR_OK; return ERROR_OK;
} }
while ((gdb_con->buf_cnt = read_socket(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE)) <= 0) for (;;)
{ {
#ifndef _WIN32
/* a non-blocking socket will block if there is 0 bytes available on the socket,
* but return with as many bytes as are available immediately
*/
struct timeval tv;
fd_set read_fds;
FD_ZERO(&read_fds);
FD_SET(connection->fd, &read_fds);
tv.tv_sec = 1;
tv.tv_usec = 0;
if (select(connection->fd + 1, &read_fds, NULL, NULL, &tv) == 0)
{
/* This can typically be because a "monitor" command took too long
* before printing any progress messages
*/
return ERROR_GDB_TIMEOUT;
}
#endif
gdb_con->buf_cnt = read_socket(connection->fd, gdb_con->buffer, GDB_BUFFER_SIZE);
if (gdb_con->buf_cnt > 0)
{
break;
}
if (gdb_con->buf_cnt == 0) if (gdb_con->buf_cnt == 0)
{
gdb_con->closed = 1;
return ERROR_SERVER_REMOTE_CLOSED; return ERROR_SERVER_REMOTE_CLOSED;
}
#ifdef _WIN32 #ifdef _WIN32
errno = WSAGetLastError(); errno = WSAGetLastError();
@ -140,7 +168,7 @@ int gdb_get_char(connection_t *connection, int* next_char)
return ERROR_SERVER_REMOTE_CLOSED; return ERROR_SERVER_REMOTE_CLOSED;
default: default:
ERROR("read: %s", strerror(errno)); ERROR("read: %s", strerror(errno));
exit(-1); return ERROR_SERVER_REMOTE_CLOSED;
} }
#endif #endif
} }
@ -184,11 +212,27 @@ int gdb_putback_char(connection_t *connection, int last_char)
return ERROR_OK; return ERROR_OK;
} }
/* The only way we can detect that the socket is closed is the first time
* we write to it, we will fail. Subsequent write operations will
* succeed. Shudder! */
int gdb_write(connection_t *connection, void *data, int len)
{
gdb_connection_t *gdb_con = connection->priv;
if (gdb_con->closed)
return ERROR_SERVER_REMOTE_CLOSED;
if (write_socket(connection->fd, data, len) == len)
{
return ERROR_OK;
}
gdb_con->closed = 1;
return ERROR_SERVER_REMOTE_CLOSED;
}
int gdb_put_packet_inner(connection_t *connection, char *buffer, int len) int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
{ {
int i; int i;
unsigned char my_checksum = 0; unsigned char my_checksum = 0;
char checksum[3];
#ifdef _DEBUG_GDB_IO_ #ifdef _DEBUG_GDB_IO_
char *debug_buffer; char *debug_buffer;
#endif #endif
@ -208,22 +252,55 @@ int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum); DEBUG("sending packet '$%s#%2.2x'", debug_buffer, my_checksum);
free(debug_buffer); free(debug_buffer);
#endif #endif
write_socket(connection->fd, "$", 1); #if 0
char checksum[3];
gdb_write(connection, "$", 1);
if (len > 0) if (len > 0)
write_socket(connection->fd, buffer, len); gdb_write(connection, buffer, len);
write_socket(connection->fd, "#", 1); gdb_write(connection, "#", 1);
snprintf(checksum, 3, "%2.2x", my_checksum); snprintf(checksum, 3, "%2.2x", my_checksum);
write_socket(connection->fd, checksum, 2); gdb_write(connection, checksum, 2);
#else
void *allocated = NULL;
char stackAlloc[1024];
char *t = stackAlloc;
int totalLen = 1 + len + 1 + 2;
if (totalLen > sizeof(stackAlloc))
{
allocated = malloc(totalLen);
t = allocated;
if (allocated == NULL)
{
ERROR("Ran out of memory trying to reply packet %d\n", totalLen);
exit(-1);
}
}
t[0] = '$';
memcpy(t + 1, buffer, len);
t[1 + len] = '#';
t[1 + len + 1] = DIGITS[(my_checksum >> 4) & 0xf];
t[1 + len + 2] = DIGITS[my_checksum & 0xf];
gdb_write(connection, t, totalLen);
if (allocated)
{
free(allocated);
}
#endif
if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK) if ((retval = gdb_get_char(connection, &reply)) != ERROR_OK)
return retval; return retval;
if (reply == '+') if (reply == '+')
break; break;
else if (reply == '-') else if (reply == '-')
{
/* Stop sending output packets for now */
log_setCallback(NULL, NULL);
WARNING("negative reply, retrying"); WARNING("negative reply, retrying");
}
else if (reply == 0x3) else if (reply == 0x3)
{ {
gdb_con->ctrl_c = 1; gdb_con->ctrl_c = 1;
@ -232,7 +309,11 @@ int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
if (reply == '+') if (reply == '+')
break; break;
else if (reply == '-') else if (reply == '-')
{
/* Stop sending output packets for now */
log_setCallback(NULL, NULL);
WARNING("negative reply, retrying"); WARNING("negative reply, retrying");
}
else else
{ {
ERROR("unknown character 0x%2.2x in reply, dropping connection", reply); ERROR("unknown character 0x%2.2x in reply, dropping connection", reply);
@ -245,16 +326,18 @@ int gdb_put_packet_inner(connection_t *connection, char *buffer, int len)
return ERROR_SERVER_REMOTE_CLOSED; return ERROR_SERVER_REMOTE_CLOSED;
} }
} }
if (gdb_con->closed)
return ERROR_SERVER_REMOTE_CLOSED;
return ERROR_OK; return ERROR_OK;
} }
int gdb_put_packet(connection_t *connection, char *buffer, int len) int gdb_put_packet(connection_t *connection, char *buffer, int len)
{ {
gdb_connection_t *gdb_connection = connection->priv; gdb_connection_t *gdb_con = connection->priv;
gdb_connection->output_disable=1; gdb_con->busy = 1;
int retval=gdb_put_packet_inner(connection, buffer, len); int retval = gdb_put_packet_inner(connection, buffer, len);
gdb_connection->output_disable=0; gdb_con->busy = 0;
return retval; return retval;
} }
@ -300,7 +383,7 @@ int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)
my_checksum = 0; my_checksum = 0;
count=0; count = 0;
gdb_connection_t *gdb_con = connection->priv; gdb_connection_t *gdb_con = connection->priv;
for (;;) for (;;)
{ {
@ -394,38 +477,33 @@ int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len)
if (my_checksum == strtoul(checksum, NULL, 16)) if (my_checksum == strtoul(checksum, NULL, 16))
{ {
write_socket(connection->fd, "+", 1); gdb_write(connection, "+", 1);
break; break;
} }
WARNING("checksum error, requesting retransmission"); WARNING("checksum error, requesting retransmission");
write_socket(connection->fd, "-", 1); gdb_write(connection, "-", 1);
} }
if (gdb_con->closed)
return ERROR_SERVER_REMOTE_CLOSED;
return ERROR_OK; return ERROR_OK;
} }
int gdb_get_packet(connection_t *connection, char *buffer, int *len) int gdb_get_packet(connection_t *connection, char *buffer, int *len)
{ {
gdb_connection_t *gdb_connection = connection->priv; gdb_connection_t *gdb_con = connection->priv;
gdb_connection->output_disable=1; gdb_con->busy = 1;
int retval=gdb_get_packet_inner(connection, buffer, len); int retval = gdb_get_packet_inner(connection, buffer, len);
gdb_connection->output_disable=0; gdb_con->busy = 0;
return retval; return retval;
} }
int gdb_output_con(connection_t *connection, char* line) int gdb_output_con(connection_t *connection, char* line)
{ {
gdb_connection_t *gdb_connection = connection->priv;
char *hex_buffer; char *hex_buffer;
int i, bin_size; int i, bin_size;
/* check if output is enabled */
if (gdb_connection->output_disable)
{
return ERROR_OK;
}
bin_size = strlen(line); bin_size = strlen(line);
hex_buffer = malloc(bin_size*2 + 4); hex_buffer = malloc(bin_size*2 + 4);
@ -445,8 +523,9 @@ int gdb_output_con(connection_t *connection, char* line)
int gdb_output(struct command_context_s *context, char* line) int gdb_output(struct command_context_s *context, char* line)
{ {
connection_t *connection = context->output_handler_priv; /* this will be dumped to the log and also sent as an O packet if possible */
return gdb_output_con(connection, line); ERROR(line);
return ERROR_OK;
} }
int gdb_program_handler(struct target_s *target, enum target_event event, void *priv) int gdb_program_handler(struct target_s *target, enum target_event event, void *priv)
@ -483,9 +562,18 @@ int gdb_target_callback_event_handler(struct target_s *target, enum target_event
switch (event) switch (event)
{ {
case TARGET_EVENT_HALTED: case TARGET_EVENT_HALTED:
/* In the GDB protocol when we are stepping or coninuing execution,
* we have a lingering reply. Upon receiving a halted event
* when we have that lingering packet, we reply to the original
* step or continue packet.
*
* Executing monitor commands can bring the target in and
* out of the running state so we'll see lots of TARGET_EVENT_XXX
* that are to be ignored.
*/
if (gdb_connection->frontend_state == TARGET_RUNNING) if (gdb_connection->frontend_state == TARGET_RUNNING)
{ {
// stop forwarding log packets! /* stop forwarding log packets! */
log_setCallback(NULL, NULL); log_setCallback(NULL, NULL);
if (gdb_connection->ctrl_c) if (gdb_connection->ctrl_c)
@ -503,12 +591,6 @@ int gdb_target_callback_event_handler(struct target_s *target, enum target_event
gdb_connection->frontend_state = TARGET_HALTED; gdb_connection->frontend_state = TARGET_HALTED;
} }
break; break;
case TARGET_EVENT_RESUMED:
if (gdb_connection->frontend_state == TARGET_HALTED)
{
gdb_connection->frontend_state = TARGET_RUNNING;
}
break;
case TARGET_EVENT_GDB_PROGRAM: case TARGET_EVENT_GDB_PROGRAM:
gdb_program_handler(target, event, connection->cmd_ctx); gdb_program_handler(target, event, connection->cmd_ctx);
break; break;
@ -534,7 +616,8 @@ int gdb_new_connection(connection_t *connection)
gdb_connection->ctrl_c = 0; gdb_connection->ctrl_c = 0;
gdb_connection->frontend_state = TARGET_HALTED; gdb_connection->frontend_state = TARGET_HALTED;
gdb_connection->vflash_image = NULL; gdb_connection->vflash_image = NULL;
gdb_connection->output_disable = 0; gdb_connection->closed = 0;
gdb_connection->busy = 0;
/* output goes through gdb connection */ /* output goes through gdb connection */
command_set_output_handler(connection->cmd_ctx, gdb_output, connection); command_set_output_handler(connection->cmd_ctx, gdb_output, connection);
@ -546,14 +629,12 @@ int gdb_new_connection(connection_t *connection)
if (((retval = gdb_service->target->type->halt(gdb_service->target)) != ERROR_OK) && if (((retval = gdb_service->target->type->halt(gdb_service->target)) != ERROR_OK) &&
(retval != ERROR_TARGET_ALREADY_HALTED)) (retval != ERROR_TARGET_ALREADY_HALTED))
{ {
ERROR("error when trying to halt target"); ERROR("error(%d) when trying to halt target, falling back to \"reset halt\"", retval);
exit(-1); command_run_line(connection->cmd_ctx, "reset halt");
} }
while (gdb_service->target->state != TARGET_HALTED) /* This will time out after 1 second */
{ command_run_line(connection->cmd_ctx, "wait_halt 1");
gdb_service->target->type->poll(gdb_service->target);
}
/* remove the initial ACK from the incoming buffer */ /* remove the initial ACK from the incoming buffer */
if ((retval = gdb_get_char(connection, &initial_ack)) != ERROR_OK) if ((retval = gdb_get_char(connection, &initial_ack)) != ERROR_OK)
@ -621,7 +702,6 @@ int gdb_last_signal_packet(connection_t *connection, target_t *target, char* pac
* case an entire byte is shown. */ * case an entire byte is shown. */
void gdb_str_to_target(target_t *target, char *tstr, reg_t *reg) void gdb_str_to_target(target_t *target, char *tstr, reg_t *reg)
{ {
static const char *DIGITS = "0123456789abcdef";
int i; int i;
u8 *buf; u8 *buf;
@ -935,13 +1015,19 @@ int gdb_memory_packet_error(connection_t *connection, int retval)
gdb_send_error(connection, EFAULT); gdb_send_error(connection, EFAULT);
break; break;
default: default:
ERROR("BUG: unexpected error %i", retval); /* This could be that the target reset itself. */
exit(-1); ERROR("unexpected error %i. Dropping connection.", retval);
return ERROR_SERVER_REMOTE_CLOSED;
} }
return ERROR_OK; return ERROR_OK;
} }
/* We don't have to worry about the default 2 second timeout for GDB packets,
* because GDB breaks up large memory reads into smaller reads.
*
* 8191 bytes by the looks of it. Why 8191 bytes instead of 8192?????
*/
int gdb_read_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size) int gdb_read_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
{ {
char *separator; char *separator;
@ -951,8 +1037,7 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
u8 *buffer; u8 *buffer;
char *hex_buffer; char *hex_buffer;
int i; int retval = ERROR_OK;
int retval;
/* skip command character */ /* skip command character */
packet++; packet++;
@ -985,11 +1070,14 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
else else
retval = target->type->read_memory(target, addr, 1, len, buffer); retval = target->type->read_memory(target, addr, 1, len, buffer);
break; break;
case 3:
case 1:
retval = target->type->read_memory(target, addr, 1, len, buffer);
break;
/* handle bulk reads */
default: default:
if (((addr % 4) == 0) && ((len % 4) == 0)) retval = target_read_buffer(target, addr, len, buffer);
retval = target->type->read_memory(target, addr, 4, len / 4, buffer); break;
else
retval = target->type->read_memory(target, addr, 1, len, buffer);
} }
#if 0 #if 0
@ -1008,8 +1096,13 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
{ {
hex_buffer = malloc(len * 2 + 1); hex_buffer = malloc(len * 2 + 1);
for (i=0; i<len; i++) int i;
snprintf(hex_buffer + 2*i, 3, "%2.2x", buffer[i]); for (i = 0; i < len; i++)
{
u8 t = buffer[i];
hex_buffer[2 * i] = DIGITS[(t >> 4) & 0xf];
hex_buffer[2 * i + 1] = DIGITS[t & 0xf];
}
gdb_put_packet(connection, hex_buffer, len * 2); gdb_put_packet(connection, hex_buffer, len * 2);
@ -1017,13 +1110,12 @@ int gdb_read_memory_packet(connection_t *connection, target_t *target, char *pac
} }
else else
{ {
if ((retval = gdb_memory_packet_error(connection, retval)) != ERROR_OK) retval = gdb_memory_packet_error(connection, retval);
return retval;
} }
free(buffer); free(buffer);
return ERROR_OK; return retval;
} }
int gdb_write_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size) int gdb_write_memory_packet(connection_t *connection, target_t *target, char *packet, int packet_size)
@ -1655,9 +1747,6 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
return ERROR_SERVER_REMOTE_CLOSED; return ERROR_SERVER_REMOTE_CLOSED;
} }
/* disable gdb output while programming */
gdb_connection->output_disable = 1;
/* assume all sectors need erasing - stops any problems /* assume all sectors need erasing - stops any problems
* when flash_write is called multiple times */ * when flash_write is called multiple times */
flash_set_dirty(); flash_set_dirty();
@ -1677,9 +1766,6 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
else else
gdb_put_packet(connection, "OK", 2); gdb_put_packet(connection, "OK", 2);
/* reenable gdb output */
gdb_connection->output_disable = 0;
return ERROR_OK; return ERROR_OK;
} }
@ -1702,9 +1788,6 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
} }
length = packet_size - (parse - packet); length = packet_size - (parse - packet);
/* disable gdb output while programming */
gdb_connection->output_disable = 1;
/* create a new image if there isn't already one */ /* create a new image if there isn't already one */
if (gdb_connection->vflash_image == NULL) if (gdb_connection->vflash_image == NULL)
{ {
@ -1717,9 +1800,6 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
gdb_put_packet(connection, "OK", 2); gdb_put_packet(connection, "OK", 2);
/* reenable gdb output */
gdb_connection->output_disable = 0;
return ERROR_OK; return ERROR_OK;
} }
@ -1728,9 +1808,6 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
u32 written; u32 written;
char *error_str; char *error_str;
/* disable gdb output while programming */
gdb_connection->output_disable = 1;
/* process the flashing buffer. No need to erase as GDB /* process the flashing buffer. No need to erase as GDB
* always issues a vFlashErase first. */ * always issues a vFlashErase first. */
if ((result = flash_write(gdb_service->target, gdb_connection->vflash_image, &written, &error_str, NULL, 0)) != ERROR_OK) if ((result = flash_write(gdb_service->target, gdb_connection->vflash_image, &written, &error_str, NULL, 0)) != ERROR_OK)
@ -1756,9 +1833,6 @@ int gdb_v_packet(connection_t *connection, target_t *target, char *packet, int p
free(gdb_connection->vflash_image); free(gdb_connection->vflash_image);
gdb_connection->vflash_image = NULL; gdb_connection->vflash_image = NULL;
/* reenable gdb output */
gdb_connection->output_disable = 0;
return ERROR_OK; return ERROR_OK;
} }
@ -1791,15 +1865,20 @@ int gdb_detach(connection_t *connection, target_t *target)
return ERROR_OK; return ERROR_OK;
} }
static void gdb_log_callback(void *priv, const char *file, int line,
static void gdb_log_callback(void *privData, const char *file, int line,
const char *function, const char *format, va_list args) const char *function, const char *format, va_list args)
{ {
connection_t *connection=(connection_t *)privData; connection_t *connection = priv;
gdb_connection_t *gdb_con = connection->priv;
char *t=allocPrintf(format, args); if (gdb_con->busy)
if (t==NULL) {
/* do not reply this using the O packet */
return;
}
char *t = allocPrintf(format, args);
if (t == NULL)
return; return;
gdb_output_con(connection, t); gdb_output_con(connection, t);
@ -1807,7 +1886,7 @@ static void gdb_log_callback(void *privData, const char *file, int line,
free(t); free(t);
} }
int gdb_input(connection_t *connection) int gdb_input_inner(connection_t *connection)
{ {
gdb_service_t *gdb_service = connection->service->priv; gdb_service_t *gdb_service = connection->service->priv;
target_t *target = gdb_service->target; target_t *target = gdb_service->target;
@ -1823,17 +1902,7 @@ int gdb_input(connection_t *connection)
packet_size = GDB_BUFFER_SIZE-1; packet_size = GDB_BUFFER_SIZE-1;
if ((retval = gdb_get_packet(connection, packet, &packet_size)) != ERROR_OK) if ((retval = gdb_get_packet(connection, packet, &packet_size)) != ERROR_OK)
{ {
switch (retval) return retval;
{
case ERROR_GDB_BUFFER_TOO_SMALL:
ERROR("BUG: buffer supplied for gdb packet was too small");
exit(-1);
case ERROR_SERVER_REMOTE_CLOSED:
return ERROR_SERVER_REMOTE_CLOSED;
default:
ERROR("BUG: unexpected error");
exit(-1);
}
} }
/* terminate with zero */ /* terminate with zero */
@ -1881,10 +1950,14 @@ int gdb_input(connection_t *connection)
break; break;
case 'c': case 'c':
case 's': case 's':
{
/* We're running/stepping, in which case we can /* We're running/stepping, in which case we can
* forward log output until the target is halted */ * forward log output until the target is halted */
log_setCallback(gdb_log_callback, connection); gdb_connection_t *gdb_con = connection->priv;
gdb_step_continue_packet(connection, target, packet, packet_size); gdb_con->frontend_state = TARGET_RUNNING;
log_setCallback(gdb_log_callback, connection);
gdb_step_continue_packet(connection, target, packet, packet_size);
}
break; break;
case 'v': case 'v':
retval = gdb_v_packet(connection, target, packet, packet_size); retval = gdb_v_packet(connection, target, packet, packet_size);
@ -1937,6 +2010,15 @@ int gdb_input(connection_t *connection)
return ERROR_OK; return ERROR_OK;
} }
int gdb_input(connection_t *connection)
{
int retval = gdb_input_inner(connection);
if (retval == ERROR_SERVER_REMOTE_CLOSED)
return retval;
/* we'll recover from any other errors(e.g. temporary timeouts, etc.) */
return ERROR_OK;
}
int gdb_init() int gdb_init()
{ {
gdb_service_t *gdb_service; gdb_service_t *gdb_service;

View File

@ -34,7 +34,8 @@ typedef struct gdb_connection_s
int ctrl_c; int ctrl_c;
enum target_state frontend_state; enum target_state frontend_state;
image_t *vflash_image; image_t *vflash_image;
int output_disable; int closed;
int busy;
} gdb_connection_t; } gdb_connection_t;
typedef struct gdb_service_s typedef struct gdb_service_s
@ -46,5 +47,6 @@ extern int gdb_init();
extern int gdb_register_commands(command_context_t *command_context); extern int gdb_register_commands(command_context_t *command_context);
#define ERROR_GDB_BUFFER_TOO_SMALL (-800) #define ERROR_GDB_BUFFER_TOO_SMALL (-800)
#define ERROR_GDB_TIMEOUT (-801)
#endif /* GDB_SERVER_H */ #endif /* GDB_SERVER_H */