diff options
-rw-r--r-- | src/helper/command.c | 17 | ||||
-rw-r--r-- | src/helper/log.c | 64 | ||||
-rw-r--r-- | src/helper/log.h | 30 | ||||
-rw-r--r-- | src/server/gdb_server.c | 65 | ||||
-rw-r--r-- | src/server/telnet_server.c | 23 |
5 files changed, 158 insertions, 41 deletions
diff --git a/src/helper/command.c b/src/helper/command.c index afd86672..9f756d41 100644 --- a/src/helper/command.c +++ b/src/helper/command.c @@ -268,7 +268,7 @@ void command_print(command_context_t *context, char *format, ...) /* process format string */ /* TODO: possible bug. va_list is undefined after the first call to vsnprintf */ while (!buffer || (n = vsnprintf(buffer, size, format, ap)) >= size) - { + { /* increase buffer until it fits the whole string */ if (!(p = realloc(buffer, size += 4096))) { @@ -359,7 +359,7 @@ int find_and_run_command(command_context_t *context, command_t *commands, char * return ERROR_OK; } -int command_run_line(command_context_t *context, char *line) +static int command_run_line_inner(command_context_t *context, char *line) { int nwords; char *words[128] = {0}; @@ -399,6 +399,17 @@ int command_run_line(command_context_t *context, char *line) return retval; } +int command_run_line(command_context_t *context, char *line) +{ + int retval=command_run_line_inner(context, line); + // we don't want any dangling callbacks! + // + // Capturing output from logging is *very* loosly modeled on C/C++ exceptions. + // the capture must be set up at function entry and + // stops when the function call returns + log_setCallback(NULL, NULL); + return retval; +} int command_run_file(command_context_t *context, FILE *file, enum command_mode mode) { int retval = ERROR_OK; @@ -441,7 +452,7 @@ int command_run_file(command_context_t *context, FILE *file, enum command_mode m break; /* run line */ - if ((retval = command_run_line(context, cmd)) == ERROR_COMMAND_CLOSE_CONNECTION) + if ((retval = command_run_line_inner(context, cmd)) == ERROR_COMMAND_CLOSE_CONNECTION) break; } diff --git a/src/helper/log.c b/src/helper/log.c index 74407078..db0bc0bd 100644 --- a/src/helper/log.c +++ b/src/helper/log.c @@ -33,6 +33,16 @@ int debug_level = -1; static FILE* log_output; + +static void *privData; +static logCallback callback; + +void log_setCallback(logCallback c, void *p) +{ + callback=c; + privData=p; +} + static char *log_strings[4] = { "Error: ", @@ -56,25 +66,18 @@ void log_printf(enum log_levels level, const char *file, int line, const char *f fflush(log_output); va_end(args); -} -void short_log_printf(enum log_levels level, const char *format, ...) + if (callback) { - va_list args; - char buffer[512]; - - if (level > debug_level) - return; - va_start(args, format); - vsnprintf(buffer, 512, format, args); - fprintf(log_output, "%s %s\n", log_strings[level], buffer); - fflush(log_output); + callback(privData, file, line, function, format, args); va_end(args); } +} + /* change the current debug level on the fly * 0: only ERRORS * 1: + WARNINGS @@ -136,3 +139,42 @@ int log_init(struct command_context_s *cmd_ctx) return ERROR_OK; } + +int set_log_output(struct command_context_s *cmd_ctx, FILE *output) +{ + log_output=output; + return ERROR_OK; +} + +/* return allocated string w/printf() result */ +char *allocPrintf(const char *fmt, va_list ap) +{ + char *string=NULL; + int size=0; // start by 0 to exercise all the code paths. Need minimum 2 bytes to fit 1 char and 0 terminator. + int first=1; + for (;;) + { + if ((string==NULL)||(!first)) + { + size=size*2+2; + char *t=string; + string=realloc(string, size); + if (string==NULL) + { + if (t!=NULL) + free(t); + return NULL; + } + } + + int ret; + ret = vsnprintf(string, size, fmt, ap); + // NB! The result of the vsnprintf() might be an *EMPTY* string! + if ((ret>=0)&&((ret+1)<size)) + { + return string; + } + // there was just enough or not enough space, allocate more. + first=0; + } +} diff --git a/src/helper/log.h b/src/helper/log.h index 6e799ad3..34646e70 100644 --- a/src/helper/log.h +++ b/src/helper/log.h @@ -44,6 +44,12 @@ extern void log_printf(enum log_levels level, const char *file, int line, __attribute__ ((format (printf, 5, 6))); extern int log_register_commands(struct command_context_s *cmd_ctx); extern int log_init(struct command_context_s *cmd_ctx); +extern int set_log_output(struct command_context_s *cmd_ctx, FILE *output); + +typedef void (*logCallback)(void *privData, const char *file, int line, + const char *function, const char *format, va_list args); + +void log_setCallback(logCallback callback, void *privData); extern int debug_level; @@ -67,25 +73,6 @@ extern int debug_level; log_printf (LOG_ERROR, __FILE__, __LINE__, __FUNCTION__, expr); \ } while(0) -#define SDEBUG(expr ...) \ - do { \ - short_log_printf (LOG_DEBUG, expr); \ - } while(0) - -#define SINFO(expr ...) \ - do { \ - short_log_printf (LOG_INFO, expr); \ - } while(0) - -#define SWARNING(expr ...) \ - do { \ - short_log_printf (LOG_WARNING, expr); \ - } while(0) - -#define SERROR(expr ...) \ - do { \ - short_log_printf (LOG_ERROR, expr); \ - } while(0) /* general failures * error codes < 100 @@ -95,4 +82,7 @@ extern int debug_level; #define ERROR_NO_CONFIG_FILE (-2) #define ERROR_BUF_TOO_SMALL (-3) -#endif /* ERROR_H */ +char *allocPrintf(const char *fmt, va_list ap); + + +#endif /* LOG_H */ diff --git a/src/server/gdb_server.c b/src/server/gdb_server.c index d48101f4..b15c29d3 100644 --- a/src/server/gdb_server.c +++ b/src/server/gdb_server.c @@ -44,6 +44,10 @@ static unsigned short gdb_port; +static void gdb_log_callback(void *privData, const char *file, int line, + const char *function, const char *format, va_list args); + + enum gdb_detach_mode { GDB_DETACH_RESUME, @@ -180,7 +184,7 @@ int gdb_putback_char(connection_t *connection, int last_char) return ERROR_OK; } -int gdb_put_packet(connection_t *connection, char *buffer, int len) +int gdb_put_packet_inner(connection_t *connection, char *buffer, int len) { int i; unsigned char my_checksum = 0; @@ -245,10 +249,19 @@ int gdb_put_packet(connection_t *connection, char *buffer, int len) return ERROR_OK; } -int gdb_get_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->output_disable=1; + int retval=gdb_put_packet_inner(connection, buffer, len); + gdb_connection->output_disable=0; + return retval; +} + +int gdb_get_packet_inner(connection_t *connection, char *buffer, int *len) { int character; - int count; + int count = 0; int retval; char checksum[3]; unsigned char my_checksum = 0; @@ -286,8 +299,9 @@ int gdb_get_packet(connection_t *connection, char *buffer, int *len) } while (character != '$'); my_checksum = 0; - count = 0; + count=0; + gdb_connection_t *gdb_con = connection->priv; for (;;) { /* The common case is that we have an entire packet with no escape chars. @@ -391,11 +405,18 @@ int gdb_get_packet(connection_t *connection, char *buffer, int *len) return ERROR_OK; } -int gdb_output(struct command_context_s *context, char* line) +int gdb_get_packet(connection_t *connection, char *buffer, int *len) { - connection_t *connection = context->output_handler_priv; gdb_connection_t *gdb_connection = connection->priv; + gdb_connection->output_disable=1; + int retval=gdb_get_packet_inner(connection, buffer, len); + gdb_connection->output_disable=0; + return retval; +} +int gdb_output_con(connection_t *connection, char* line) +{ + gdb_connection_t *gdb_connection = connection->priv; char *hex_buffer; int i, bin_size; @@ -422,6 +443,12 @@ int gdb_output(struct command_context_s *context, char* line) return ERROR_OK; } +int gdb_output(struct command_context_s *context, char* line) +{ + connection_t *connection = context->output_handler_priv; + return gdb_output_con(connection, line); +} + int gdb_program_handler(struct target_s *target, enum target_event event, void *priv) { FILE *script; @@ -458,6 +485,9 @@ int gdb_target_callback_event_handler(struct target_s *target, enum target_event case TARGET_EVENT_HALTED: if (gdb_connection->frontend_state == TARGET_RUNNING) { + // stop forwarding log packets! + log_setCallback(NULL, NULL); + if (gdb_connection->ctrl_c) { signal = 0x2; @@ -895,7 +925,6 @@ int gdb_memory_packet_error(connection_t *connection, int retval) case ERROR_TARGET_NOT_HALTED: ERROR("gdb tried to read memory but we're not halted, dropping connection"); return ERROR_SERVER_REMOTE_CLOSED; - break; case ERROR_TARGET_DATA_ABORT: gdb_send_error(connection, EIO); break; @@ -1398,6 +1427,9 @@ int gdb_query_packet(connection_t *connection, target_t *target, char *packet, i cmd[i] = tmp; } cmd[(packet_size - 6)/2] = 0x0; + + /* We want to print all debug output to GDB connection */ + log_setCallback(gdb_log_callback, connection); target_call_timer_callbacks(); command_run_line(cmd_ctx, cmd); free(cmd); @@ -1759,6 +1791,22 @@ int gdb_detach(connection_t *connection, target_t *target) return ERROR_OK; } + + +static void gdb_log_callback(void *privData, const char *file, int line, + const char *function, const char *format, va_list args) +{ + connection_t *connection=(connection_t *)privData; + + char *t=allocPrintf(format, args); + if (t==NULL) + return; + + gdb_output_con(connection, t); + + free(t); +} + int gdb_input(connection_t *connection) { gdb_service_t *gdb_service = connection->service->priv; @@ -1833,6 +1881,9 @@ int gdb_input(connection_t *connection) break; case 'c': case 's': + /* We're running/stepping, in which case we can + * forward log output until the target is halted */ + log_setCallback(gdb_log_callback, connection); gdb_step_continue_packet(connection, target, packet, packet_size); break; case 'v': diff --git a/src/server/telnet_server.c b/src/server/telnet_server.c index 3a74f5ac..af49050a 100644 --- a/src/server/telnet_server.c +++ b/src/server/telnet_server.c @@ -57,6 +57,12 @@ void telnet_prompt(connection_t *connection) write_socket(connection->fd, t_con->prompt, strlen(t_con->prompt)); } +int telnet_outputline(connection_t *connection, char* line) +{ + write_socket(connection->fd, line, strlen(line)); + return write_socket(connection->fd, "\r\n\0", 3); +} + int telnet_output(struct command_context_s *cmd_ctx, char* line) { connection_t *connection = cmd_ctx->output_handler_priv; @@ -67,6 +73,19 @@ int telnet_output(struct command_context_s *cmd_ctx, char* line) return ERROR_OK; } +void telnet_log_callback(void *privData, const char *file, int line, + const char *function, const char *format, va_list args) +{ + connection_t *connection=(connection_t *)privData; + char *t=allocPrintf(format, args); + if (t==NULL) + return; + + telnet_outputline(connection, t); + + free(t); +} + int telnet_target_callback_event_handler(struct target_s *target, enum target_event event, void *priv) { struct command_context_s *cmd_ctx = priv; @@ -244,6 +263,10 @@ int telnet_input(connection_t *connection) continue; } + + + log_setCallback(telnet_log_callback, connection); + if ((retval = command_run_line(command_context, t_con->line)) != ERROR_OK) { if (retval == ERROR_COMMAND_CLOSE_CONNECTION) |