#define _DEBUG_GDB_IO_
#endif
+static gdb_connection_t *current_gdb_connection;
+
static int gdb_breakpoint_override;
static enum breakpoint_type gdb_breakpoint_override_type;
static void gdb_log_callback(void *priv, const char *file, int line,
const char *function, const char *string);
-enum gdb_detach_mode
-{
- GDB_DETACH_RESUME,
- GDB_DETACH_RESET,
- GDB_DETACH_HALT,
- GDB_DETACH_NOTHING
-};
-
-/* target behaviour on gdb detach */
-enum gdb_detach_mode detach_mode = GDB_DETACH_RESUME;
-
/* number of gdb connections, mainly to supress gdb related debugging spam
* in helper/log.c when no gdb connections are actually active */
int gdb_actual_connections;
target_handle_event(target, event);
switch (event)
{
- case TARGET_EVENT_EARLY_HALTED:
+ case TARGET_EVENT_GDB_HALT:
gdb_frontend_halted(target, connection);
break;
case TARGET_EVENT_HALTED:
gdb_connection->closed = 0;
gdb_connection->busy = 0;
gdb_connection->noack_mode = 0;
+ gdb_connection->sync = true;
/* send ACK to GDB for debug request */
gdb_write(connection, "+", 1);
/* register callback to be informed about target events */
target_register_event_callback(gdb_target_callback_event_handler, connection);
- /* a gdb session just attached, try to put the target in halt mode.
- *
- * DANGER!!!!
- *
- * If the halt fails(e.g. target needs a reset, JTAG communication not
- * working, etc.), then the GDB connect will succeed as
- * the get_gdb_reg_list() will lie and return a register list with
- * dummy values.
- *
- * This allows GDB monitor commands to be run from a GDB init script to
- * initialize the target
- *
- * Also, since the halt() is asynchronous target connect will be
- * instantaneous and thus avoiding annoying timeout problems during
- * connect.
- */
- target_halt(gdb_service->target);
- /* FIX!!!! could extended-remote work better here?
- *
- * wait a tiny bit for halted state or we just continue. The
- * GDB register packet will then contain garbage
- */
- target_wait_state(gdb_service->target, TARGET_HALTED, 500);
-
/* remove the initial ACK from the incoming buffer */
if ((retval = gdb_get_char(connection, &initial_ack)) != ERROR_OK)
return retval;
gdb_service_t *gdb_service = connection->service->priv;
gdb_connection_t *gdb_connection = connection->priv;
+ /* we're done forwarding messages. Tear down callback before
+ * cleaning up connection.
+ */
+ log_remove_callback(gdb_log_callback, connection);
+
gdb_actual_connections--;
- LOG_DEBUG("GDB Close, Target: %s, state: %s, gdb_actual_connections=%d",
+ LOG_DEBUG("GDB Close, Target: %s, state: %s, gdb_actual_connections=%d",
gdb_service->target->cmd_name,
target_state_name(gdb_service->target),
gdb_actual_connections);
LOG_ERROR("BUG: connection->priv == NULL");
}
+
target_unregister_event_callback(gdb_target_callback_event_handler, connection);
+
target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_END);
- log_remove_callback(gdb_log_callback, connection);
target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_DETACH);
/* We want to print all debug output to GDB connection */
log_add_callback(gdb_log_callback, connection);
target_call_timer_callbacks_now();
+ /* some commands need to know the GDB connection, make note of current
+ * GDB connection. */
+ current_gdb_connection = gdb_connection;
command_run_line(cmd_ctx, cmd);
+ current_gdb_connection = NULL;
target_call_timer_callbacks_now();
log_remove_callback(gdb_log_callback, connection);
free(cmd);
int gdb_detach(connection_t *connection, target_t *target)
{
+ gdb_service_t *gdb_service = connection->service->priv;
- switch (detach_mode)
- {
- case GDB_DETACH_RESUME:
- target_handle_event(target, TARGET_EVENT_OLD_pre_resume);
- target_resume(target, 1, 0, 1, 0);
- break;
-
- case GDB_DETACH_RESET:
- /* FIX?? make this configurable?? */
- target_process_reset(connection->cmd_ctx, RESET_HALT);
- break;
-
- case GDB_DETACH_HALT:
- target_halt(target);
- break;
-
- case GDB_DETACH_NOTHING:
- break;
- }
+ target_call_event_callbacks(gdb_service->target, TARGET_EVENT_GDB_DETACH);
- gdb_put_packet(connection, "OK", 2);
- return ERROR_OK;
+ return gdb_put_packet(connection, "OK", 2);
}
static void gdb_log_callback(void *priv, const char *file, int line,
case 'c':
case 's':
{
- if (target->state != TARGET_HALTED)
+ int retval = ERROR_OK;
+
+ gdb_connection_t *gdb_con = connection->priv;
+ log_add_callback(gdb_log_callback, connection);
+
+ bool nostep = false;
+ if (target->state == TARGET_RUNNING)
+ {
+ LOG_WARNING("The target is already running. Halt target before stepi/continue.");
+ retval = target_halt(target);
+ if (retval == ERROR_OK)
+ retval = target_wait_state(target, TARGET_HALTED, 100);
+ } else if (target->state != TARGET_HALTED)
+ {
+ LOG_WARNING("The target is not in the halted nor running stated, stepi/continue ignored.");
+ nostep = true;
+ } else if ((packet[0] == 's') && gdb_con->sync)
{
- /* If the target isn't in the halted state, then we can't
+ /* Hmm..... when you issue a continue in GDB, then a "stepi" is
+ * sent by GDB first to OpenOCD, thus defeating the check to
+ * make only the single stepping have the sync feature...
+ */
+ nostep = true;
+ LOG_WARNING("stepi ignored. GDB will now fetch the register state from the target.");
+ }
+ gdb_con->sync = false;
+
+ if ((retval!=ERROR_OK) || nostep)
+ {
+ /* Either the target isn't in the halted state, then we can't
* step/continue. This might be early setup, etc.
+ *
+ * Or we want to allow GDB to pick up a fresh set of
+ * register values without modifying the target state.
+ *
*/
gdb_sig_halted(connection);
+
+ /* stop forwarding log packets! */
+ log_remove_callback(gdb_log_callback, connection);
} else
{
/* We're running/stepping, in which case we can
* forward log output until the target is halted
*/
- gdb_connection_t *gdb_con = connection->priv;
gdb_con->frontend_state = TARGET_RUNNING;
- log_add_callback(gdb_log_callback, connection);
target_call_event_callbacks(target, TARGET_EVENT_GDB_START);
int retval = gdb_step_continue_packet(connection, target, packet, packet_size);
if (retval != ERROR_OK)
/* handle extended restart packet */
breakpoint_clear_target(gdb_service->target);
watchpoint_clear_target(gdb_service->target);
- command_run_linef(connection->cmd_ctx, "ocd_gdb_restart %d", get_num_by_target(target));
+ command_run_linef(connection->cmd_ctx,
+ "ocd_gdb_restart %s",
+ target->cmd_name);
break;
default:
/* ignore unkown packets */
{
if (target->state == TARGET_RUNNING)
{
- target_halt(target);
+ retval = target_halt(target);
+ if (retval != ERROR_OK)
+ {
+ target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
+ }
gdb_con->ctrl_c = 0;
+ } else
+ {
+ LOG_INFO("The target is not running when halt was requested, stopping GDB.");
+ target_call_event_callbacks(target, TARGET_EVENT_GDB_HALT);
}
}
}
else
{
+ unsigned short port = gdb_port;
+
while (target)
{
gdb_service = malloc(sizeof(gdb_service_t));
gdb_service->target = target;
- add_service("gdb", CONNECTION_TCP, gdb_port + target->target_number, 1, gdb_new_connection, gdb_input, gdb_connection_closed, gdb_service);
+ add_service("gdb", CONNECTION_TCP,
+ port, 1,
+ gdb_new_connection, gdb_input,
+ gdb_connection_closed, gdb_service);
- LOG_DEBUG("gdb service for target %s at port %i",
+ LOG_DEBUG("gdb service for target %s at TCP port %i",
target_get_name(target),
- gdb_port + target->target_number);
+ port);
target = target->next;
+ port++;
}
}
return ERROR_OK;
}
-/* daemon configuration command gdb_port */
-int handle_gdb_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+static int handle_gdb_sync_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
{
- if (argc == 0)
+ if (argc != 0)
{
- command_print(cmd_ctx, "%d", gdb_port);
- return ERROR_OK;
+ return ERROR_COMMAND_SYNTAX_ERROR;
}
- gdb_port = strtoul(args[0], NULL, 0);
+ if (current_gdb_connection == NULL)
+ {
+ command_print(cmd_ctx,
+ "gdb_sync command can only be run from within gdb using \"monitor gdb_sync\"");
+ return ERROR_FAIL;
+ }
+
+ current_gdb_connection->sync = true;
return ERROR_OK;
}
-int handle_gdb_detach_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+/* daemon configuration command gdb_port */
+static int handle_gdb_port_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
{
- if (argc == 1)
- {
- if (strcmp(args[0], "resume") == 0)
- {
- detach_mode = GDB_DETACH_RESUME;
- return ERROR_OK;
- }
- else if (strcmp(args[0], "reset") == 0)
- {
- detach_mode = GDB_DETACH_RESET;
- return ERROR_OK;
- }
- else if (strcmp(args[0], "halt") == 0)
- {
- detach_mode = GDB_DETACH_HALT;
- return ERROR_OK;
- }
- else if (strcmp(args[0], "nothing") == 0)
- {
- detach_mode = GDB_DETACH_NOTHING;
- return ERROR_OK;
- }
- else
- LOG_WARNING("invalid gdb_detach configuration directive: %s", args[0]);
- }
-
- return ERROR_COMMAND_SYNTAX_ERROR;
+ return server_port_command(cmd_ctx, cmd, args, argc, &gdb_port);
}
-int handle_gdb_memory_map_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+static int handle_gdb_memory_map_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
{
if (argc == 1)
{
return ERROR_COMMAND_SYNTAX_ERROR;
}
-int handle_gdb_flash_program_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+static int handle_gdb_flash_program_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
{
if (argc == 1)
{
return ERROR_COMMAND_SYNTAX_ERROR;
}
-int handle_gdb_report_data_abort_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+static int handle_gdb_report_data_abort_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
{
if (argc == 1)
{
}
/* gdb_breakpoint_override */
-int handle_gdb_breakpoint_override_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
+static int handle_gdb_breakpoint_override_command(struct command_context_s *cmd_ctx, char *cmd, char **args, int argc)
{
if (argc == 0)
{
int gdb_register_commands(command_context_t *command_context)
{
- register_command(command_context, NULL, "gdb_port", handle_gdb_port_command,
- COMMAND_ANY, "daemon configuration command gdb_port");
- register_command(command_context, NULL, "gdb_detach", handle_gdb_detach_command,
- COMMAND_CONFIG, "resume/reset/halt/nothing - "
- "specify behavior when GDB detaches from the target");
- register_command(command_context, NULL, "gdb_memory_map", handle_gdb_memory_map_command,
- COMMAND_CONFIG, "enable or disable memory map");
- register_command(command_context, NULL, "gdb_flash_program", handle_gdb_flash_program_command,
- COMMAND_CONFIG, "enable or disable flash program");
- register_command(command_context, NULL, "gdb_report_data_abort", handle_gdb_report_data_abort_command,
- COMMAND_CONFIG, "enable or disable reporting data aborts");
- register_command(command_context, NULL, "gdb_breakpoint_override", handle_gdb_breakpoint_override_command,
- COMMAND_EXEC, "hard/soft/disable - force breakpoint type for gdb 'break' commands.");
+ register_command(command_context, NULL, "gdb_sync",
+ handle_gdb_sync_command, COMMAND_ANY,
+ "next stepi will return immediately allowing GDB to "
+ "fetch register state without affecting target state");
+ register_command(command_context, NULL, "gdb_port",
+ handle_gdb_port_command, COMMAND_ANY,
+ "daemon configuration command gdb_port");
+ register_command(command_context, NULL, "gdb_memory_map",
+ handle_gdb_memory_map_command, COMMAND_CONFIG,
+ "enable or disable memory map");
+ register_command(command_context, NULL, "gdb_flash_program",
+ handle_gdb_flash_program_command, COMMAND_CONFIG,
+ "enable or disable flash program");
+ register_command(command_context, NULL, "gdb_report_data_abort",
+ handle_gdb_report_data_abort_command, COMMAND_CONFIG,
+ "enable or disable reporting data aborts");
+ register_command(command_context, NULL, "gdb_breakpoint_override",
+ handle_gdb_breakpoint_override_command, COMMAND_EXEC,
+ "hard/soft/disable - force type of breakpoint "
+ "used by gdb 'break' commands.");
return ERROR_OK;
}