gdb_server: add debug signal reason prints
[openocd.git] / src / server / gdb_server.c
index 82c8ce92b31faf395ee6f933ba147ee8ad1582ac..0baf7553b21c29105180bc25d8191578caf3545b 100644 (file)
@@ -1,3 +1,5 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+
 /***************************************************************************
  *   Copyright (C) 2005 by Dominic Rath                                    *
  *   Dominic.Rath@gmx.de                                                   *
  *                                                                         *
  *   Copyright (C) 2013 Franck Jullien                                     *
  *   elec4fun@gmail.com                                                    *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *                                                                         *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License for more details.                          *
- *                                                                         *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program.  If not, see <http://www.gnu.org/licenses/>. *
  ***************************************************************************/
 
 #ifdef HAVE_CONFIG_H
@@ -128,7 +117,7 @@ static void gdb_sig_halted(struct connection *connection);
 
 /* number of gdb connections, mainly to suppress gdb related debugging spam
  * in helper/log.c when no gdb connections are actually active */
-int gdb_actual_connections;
+static int gdb_actual_connections;
 
 /* set if we are sending a memory map to gdb
  * via qXfer:memory-map:read packet */
@@ -156,6 +145,9 @@ static char gdb_running_type;
 
 static int gdb_last_signal(struct target *target)
 {
+       LOG_TARGET_DEBUG(target, "Debug reason is: %s",
+                       target_debug_reason_str(target->debug_reason));
+
        switch (target->debug_reason) {
                case DBG_REASON_DBGRQ:
                        return 0x2;             /* SIGINT */
@@ -170,8 +162,9 @@ static int gdb_last_signal(struct target *target)
                case DBG_REASON_NOTHALTED:
                        return 0x0;             /* no signal... shouldn't happen */
                default:
-                       LOG_USER("undefined debug reason %d - target needs reset",
-                                       target->debug_reason);
+                       LOG_USER("undefined debug reason %d (%s) - target needs reset",
+                                       target->debug_reason,
+                                       target_debug_reason_str(target->debug_reason));
                        return 0x0;
        }
 }
@@ -809,6 +802,7 @@ static void gdb_signal_reply(struct target *target, struct connection *connectio
                }
 
                if (gdb_connection->ctrl_c) {
+                       LOG_TARGET_DEBUG(target, "Responding with signal 2 (SIGINT) to debugger due to Ctrl-C");
                        signal_var = 0x2;
                } else
                        signal_var = gdb_last_signal(ct);
@@ -1334,6 +1328,8 @@ static int gdb_set_registers_packet(struct connection *connection,
        packet_p = packet;
        for (i = 0; i < reg_list_size; i++) {
                uint8_t *bin_buf;
+               if (!reg_list[i] || !reg_list[i]->exist || reg_list[i]->hidden)
+                       continue;
                int chars = (DIV_ROUND_UP(reg_list[i]->size, 8) * 2);
 
                if (packet_p + chars > packet + packet_size)
@@ -1386,7 +1382,8 @@ static int gdb_get_register_packet(struct connection *connection,
        if (retval != ERROR_OK)
                return gdb_error(connection, retval);
 
-       if (reg_list_size <= reg_num) {
+       if ((reg_list_size <= reg_num) || !reg_list[reg_num] ||
+               !reg_list[reg_num]->exist || reg_list[reg_num]->hidden) {
                LOG_ERROR("gdb requested a non-existing register (reg_num=%d)", reg_num);
                return ERROR_SERVER_REMOTE_CLOSED;
        }
@@ -1448,7 +1445,8 @@ static int gdb_set_register_packet(struct connection *connection,
                return gdb_error(connection, retval);
        }
 
-       if (reg_list_size <= reg_num) {
+       if ((reg_list_size <= reg_num) || !reg_list[reg_num] ||
+               !reg_list[reg_num]->exist || reg_list[reg_num]->hidden) {
                LOG_ERROR("gdb requested a non-existing register (reg_num=%d)", reg_num);
                free(bin_buf);
                free(reg_list);
@@ -1781,7 +1779,10 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
                case 1:
                        if (packet[0] == 'Z') {
                                retval = breakpoint_add(target, address, size, bp_type);
-                               if (retval != ERROR_OK) {
+                               if (retval == ERROR_NOT_IMPLEMENTED) {
+                                       /* Send empty reply to report that breakpoints of this type are not supported */
+                                       gdb_put_packet(connection, "", 0);
+                               } else if (retval != ERROR_OK) {
                                        retval = gdb_error(connection, retval);
                                        if (retval != ERROR_OK)
                                                return retval;
@@ -1798,7 +1799,10 @@ static int gdb_breakpoint_watchpoint_packet(struct connection *connection,
                {
                        if (packet[0] == 'Z') {
                                retval = watchpoint_add(target, address, size, wp_type, 0, 0xffffffffu);
-                               if (retval != ERROR_OK) {
+                               if (retval == ERROR_NOT_IMPLEMENTED) {
+                                       /* Send empty reply to report that watchpoints of this type are not supported */
+                                       gdb_put_packet(connection, "", 0);
+                               } else if (retval != ERROR_OK) {
                                        retval = gdb_error(connection, retval);
                                        if (retval != ERROR_OK)
                                                return retval;
@@ -2349,6 +2353,7 @@ static int smp_reg_list_noread(struct target *target,
                                                local_list = realloc(local_list, combined_allocated * sizeof(struct reg *));
                                                if (!local_list) {
                                                        LOG_ERROR("realloc(%zu) failed", combined_allocated * sizeof(struct reg *));
+                                                       free(reg_list);
                                                        return ERROR_FAIL;
                                                }
                                        }
@@ -2750,6 +2755,7 @@ static int gdb_query_packet(struct connection *connection,
 
        if (strncmp(packet, "qRcmd,", 6) == 0) {
                if (packet_size > 6) {
+                       Jim_Interp *interp = cmd_ctx->interp;
                        char *cmd;
                        cmd = malloc((packet_size - 6) / 2 + 1);
                        size_t len = unhexify((uint8_t *)cmd, packet + 6, (packet_size - 6) / 2);
@@ -2761,11 +2767,63 @@ static int gdb_query_packet(struct connection *connection,
                        /* 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);
+
+                       struct target *saved_target_override = cmd_ctx->current_target_override;
+                       cmd_ctx->current_target_override = NULL;
+
+                       struct command_context *old_context = Jim_GetAssocData(interp, "context");
+                       Jim_DeleteAssocData(interp, "context");
+                       int retval = Jim_SetAssocData(interp, "context", NULL, cmd_ctx);
+                       if (retval == JIM_OK) {
+                               retval = Jim_EvalObj(interp, Jim_NewStringObj(interp, cmd, -1));
+                               Jim_DeleteAssocData(interp, "context");
+                       }
+                       int inner_retval = Jim_SetAssocData(interp, "context", NULL, old_context);
+                       if (retval == JIM_OK)
+                               retval = inner_retval;
+
+                       cmd_ctx->current_target_override = saved_target_override;
+
                        current_gdb_connection = NULL;
                        target_call_timer_callbacks_now();
                        gdb_connection->output_flag = GDB_OUTPUT_NO;
                        free(cmd);
+                       if (retval == JIM_RETURN)
+                               retval = interp->returnCode;
+                       int lenmsg;
+                       const char *cretmsg = Jim_GetString(Jim_GetResult(interp), &lenmsg);
+                       char *retmsg;
+                       if (lenmsg && cretmsg[lenmsg - 1] != '\n') {
+                               retmsg = alloc_printf("%s\n", cretmsg);
+                               lenmsg++;
+                       } else {
+                               retmsg = strdup(cretmsg);
+                       }
+                       if (!retmsg)
+                               return ERROR_GDB_BUFFER_TOO_SMALL;
+
+                       if (retval == JIM_OK) {
+                               if (lenmsg) {
+                                       char *hex_buffer = malloc(lenmsg * 2 + 1);
+                                       if (!hex_buffer) {
+                                               free(retmsg);
+                                               return ERROR_GDB_BUFFER_TOO_SMALL;
+                                       }
+
+                                       size_t pkt_len = hexify(hex_buffer, (const uint8_t *)retmsg, lenmsg,
+                                                                                       lenmsg * 2 + 1);
+                                       gdb_put_packet(connection, hex_buffer, pkt_len);
+                                       free(hex_buffer);
+                               } else {
+                                       gdb_put_packet(connection, "OK", 2);
+                               }
+                       } else {
+                               if (lenmsg)
+                                       gdb_output_con(connection, retmsg);
+                               gdb_send_error(connection, retval);
+                       }
+                       free(retmsg);
+                       return ERROR_OK;
                }
                gdb_put_packet(connection, "OK", 2);
                return ERROR_OK;
@@ -2913,13 +2971,19 @@ static int gdb_query_packet(struct connection *connection,
                gdb_connection->noack_mode = 1;
                gdb_put_packet(connection, "OK", 2);
                return ERROR_OK;
+       } else if (target->type->gdb_query_custom) {
+               char *buffer = NULL;
+               int ret = target->type->gdb_query_custom(target, packet, &buffer);
+               gdb_put_packet(connection, buffer, strlen(buffer));
+               return ret;
        }
 
        gdb_put_packet(connection, "", 0);
        return ERROR_OK;
 }
 
-static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet, int packet_size)
+static bool gdb_handle_vcont_packet(struct connection *connection, const char *packet,
+       __attribute__((unused)) int packet_size)
 {
        struct gdb_connection *gdb_connection = connection->priv;
        struct target *target = get_target_from_connection(connection);
@@ -2938,7 +3002,6 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
 
        if (parse[0] == ';') {
                ++parse;
-               --packet_size;
        }
 
        /* simple case, a continue packet */
@@ -2973,128 +3036,123 @@ static bool gdb_handle_vcont_packet(struct connection *connection, const char *p
                gdb_running_type = 's';
                bool fake_step = false;
 
-               if (strncmp(parse, "s:", 2) == 0) {
-                       struct target *ct = target;
-                       int current_pc = 1;
-                       int64_t thread_id;
+               struct target *ct = target;
+               int current_pc = 1;
+               int64_t thread_id;
+               parse++;
+               if (parse[0] == ':') {
                        char *endp;
-
-                       parse += 2;
-                       packet_size -= 2;
-
+                       parse++;
                        thread_id = strtoll(parse, &endp, 16);
                        if (endp) {
-                               packet_size -= endp - parse;
                                parse = endp;
                        }
+               } else {
+                       thread_id = 0;
+               }
 
-                       if (target->rtos) {
-                               /* FIXME: why is this necessary? rtos state should be up-to-date here already! */
-                               rtos_update_threads(target);
+               if (target->rtos) {
+                       /* FIXME: why is this necessary? rtos state should be up-to-date here already! */
+                       rtos_update_threads(target);
 
-                               target->rtos->gdb_target_for_threadid(connection, thread_id, &ct);
+                       target->rtos->gdb_target_for_threadid(connection, thread_id, &ct);
 
-                               /*
-                                * check if the thread to be stepped is the current rtos thread
-                                * if not, we must fake the step
-                                */
-                               if (target->rtos->current_thread != thread_id)
-                                       fake_step = true;
-                       }
+                       /*
+                        * check if the thread to be stepped is the current rtos thread
+                        * if not, we must fake the step
+                        */
+                       if (target->rtos->current_thread != thread_id)
+                               fake_step = true;
+               }
 
-                       if (parse[0] == ';') {
-                               ++parse;
-                               --packet_size;
+               if (parse[0] == ';') {
+                       ++parse;
 
-                               if (parse[0] == 'c') {
+                       if (parse[0] == 'c') {
+                               parse += 1;
+
+                               /* check if thread-id follows */
+                               if (parse[0] == ':') {
+                                       int64_t tid;
                                        parse += 1;
 
-                                       /* check if thread-id follows */
-                                       if (parse[0] == ':') {
-                                               int64_t tid;
-                                               parse += 1;
-
-                                               tid = strtoll(parse, &endp, 16);
-                                               if (tid == thread_id) {
-                                                       /*
-                                                        * Special case: only step a single thread (core),
-                                                        * keep the other threads halted. Currently, only
-                                                        * aarch64 target understands it. Other target types don't
-                                                        * care (nobody checks the actual value of 'current')
-                                                        * and it doesn't really matter. This deserves
-                                                        * a symbolic constant and a formal interface documentation
-                                                        * at a later time.
-                                                        */
-                                                       LOG_DEBUG("request to step current core only");
-                                                       /* uncomment after checking that indeed other targets are safe */
-                                                       /*current_pc = 2;*/
-                                               }
+                                       tid = strtoll(parse, NULL, 16);
+                                       if (tid == thread_id) {
+                                               /*
+                                                * Special case: only step a single thread (core),
+                                                * keep the other threads halted. Currently, only
+                                                * aarch64 target understands it. Other target types don't
+                                                * care (nobody checks the actual value of 'current')
+                                                * and it doesn't really matter. This deserves
+                                                * a symbolic constant and a formal interface documentation
+                                                * at a later time.
+                                                */
+                                               LOG_DEBUG("request to step current core only");
+                                               /* uncomment after checking that indeed other targets are safe */
+                                               /*current_pc = 2;*/
                                        }
                                }
                        }
+               }
 
-                       LOG_DEBUG("target %s single-step thread %"PRIx64, target_name(ct), thread_id);
-                       gdb_connection->output_flag = GDB_OUTPUT_ALL;
-                       target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
-
-                       /*
-                        * work around an annoying gdb behaviour: when the current thread
-                        * is changed in gdb, it assumes that the target can follow and also
-                        * make the thread current. This is an assumption that cannot hold
-                        * for a real target running a multi-threading OS. We just fake
-                        * the step to not trigger an internal error in gdb. See
-                        * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details
-                        */
-                       if (fake_step) {
-                               int sig_reply_len;
-                               char sig_reply[128];
-
-                               LOG_DEBUG("fake step thread %"PRIx64, thread_id);
+               LOG_DEBUG("target %s single-step thread %"PRIx64, target_name(ct), thread_id);
+               gdb_connection->output_flag = GDB_OUTPUT_ALL;
+               target_call_event_callbacks(ct, TARGET_EVENT_GDB_START);
 
-                               sig_reply_len = snprintf(sig_reply, sizeof(sig_reply),
-                                                                                "T05thread:%016"PRIx64";", thread_id);
+               /*
+                * work around an annoying gdb behaviour: when the current thread
+                * is changed in gdb, it assumes that the target can follow and also
+                * make the thread current. This is an assumption that cannot hold
+                * for a real target running a multi-threading OS. We just fake
+                * the step to not trigger an internal error in gdb. See
+                * https://sourceware.org/bugzilla/show_bug.cgi?id=22925 for details
+                */
+               if (fake_step) {
+                       int sig_reply_len;
+                       char sig_reply[128];
 
-                               gdb_put_packet(connection, sig_reply, sig_reply_len);
-                               gdb_connection->output_flag = GDB_OUTPUT_NO;
+                       LOG_DEBUG("fake step thread %"PRIx64, thread_id);
 
-                               return true;
-                       }
+                       sig_reply_len = snprintf(sig_reply, sizeof(sig_reply),
+                                                                       "T05thread:%016"PRIx64";", thread_id);
 
-                       /* support for gdb_sync command */
-                       if (gdb_connection->sync) {
-                               gdb_connection->sync = false;
-                               if (ct->state == TARGET_HALTED) {
-                                       LOG_DEBUG("stepi ignored. GDB will now fetch the register state "
-                                                                       "from the target.");
-                                       gdb_sig_halted(connection);
-                                       gdb_connection->output_flag = GDB_OUTPUT_NO;
-                               } else
-                                       gdb_connection->frontend_state = TARGET_RUNNING;
-                               return true;
-                       }
+                       gdb_put_packet(connection, sig_reply, sig_reply_len);
+                       gdb_connection->output_flag = GDB_OUTPUT_NO;
 
-                       retval = target_step(ct, current_pc, 0, 0);
-                       if (retval == ERROR_TARGET_NOT_HALTED)
-                               LOG_INFO("target %s was not halted when step was requested", target_name(ct));
+                       return true;
+               }
 
-                       /* if step was successful send a reply back to gdb */
-                       if (retval == ERROR_OK) {
-                               retval = target_poll(ct);
-                               if (retval != ERROR_OK)
-                                       LOG_DEBUG("error polling target %s after successful step", target_name(ct));
-                               /* send back signal information */
-                               gdb_signal_reply(ct, connection);
-                               /* stop forwarding log packets! */
+               /* support for gdb_sync command */
+               if (gdb_connection->sync) {
+                       gdb_connection->sync = false;
+                       if (ct->state == TARGET_HALTED) {
+                               LOG_DEBUG("stepi ignored. GDB will now fetch the register state "
+                                                               "from the target.");
+                               gdb_sig_halted(connection);
                                gdb_connection->output_flag = GDB_OUTPUT_NO;
                        } else
                                gdb_connection->frontend_state = TARGET_RUNNING;
-               } else {
-                       LOG_ERROR("Unknown vCont packet");
-                       return false;
+                       return true;
                }
+
+               retval = target_step(ct, current_pc, 0, 0);
+               if (retval == ERROR_TARGET_NOT_HALTED)
+                       LOG_INFO("target %s was not halted when step was requested", target_name(ct));
+
+               /* if step was successful send a reply back to gdb */
+               if (retval == ERROR_OK) {
+                       retval = target_poll(ct);
+                       if (retval != ERROR_OK)
+                               LOG_DEBUG("error polling target %s after successful step", target_name(ct));
+                       /* send back signal information */
+                       gdb_signal_reply(ct, connection);
+                       /* stop forwarding log packets! */
+                       gdb_connection->output_flag = GDB_OUTPUT_NO;
+               } else
+                       gdb_connection->frontend_state = TARGET_RUNNING;
                return true;
        }
-
+       LOG_ERROR("Unknown vCont packet");
        return false;
 }
 
@@ -3616,12 +3674,14 @@ static int gdb_input_inner(struct connection *connection)
                                        break;
 
                                case 'j':
+                                       /* DEPRECATED */
                                        /* packet supported only by smp target i.e cortex_a.c*/
                                        /* handle smp packet replying coreid played to gbd */
                                        gdb_read_smp_packet(connection, packet, packet_size);
                                        break;
 
                                case 'J':
+                                       /* DEPRECATED */
                                        /* packet supported only by smp target i.e cortex_a.c */
                                        /* handle smp packet setting coreid to be played at next
                                         * resume to gdb */
@@ -4050,3 +4110,8 @@ void gdb_service_free(void)
        free(gdb_port);
        free(gdb_port_next);
 }
+
+int gdb_get_actual_connections(void)
+{
+       return gdb_actual_connections;
+}

Linking to existing account procedure

If you already have an account and want to add another login method you MUST first sign in with your existing account and then change URL to read https://review.openocd.org/login/?link to get to this page again but this time it'll work for linking. Thank you.

SSH host keys fingerprints

1024 SHA256:YKx8b7u5ZWdcbp7/4AeXNaqElP49m6QrwfXaqQGJAOk gerrit-code-review@openocd.zylin.com (DSA)
384 SHA256:jHIbSQa4REvwCFG4cq5LBlBLxmxSqelQPem/EXIrxjk gerrit-code-review@openocd.org (ECDSA)
521 SHA256:UAOPYkU9Fjtcao0Ul/Rrlnj/OsQvt+pgdYSZ4jOYdgs gerrit-code-review@openocd.org (ECDSA)
256 SHA256:A13M5QlnozFOvTllybRZH6vm7iSt0XLxbA48yfc2yfY gerrit-code-review@openocd.org (ECDSA)
256 SHA256:spYMBqEYoAOtK7yZBrcwE8ZpYt6b68Cfh9yEVetvbXg gerrit-code-review@openocd.org (ED25519)
+--[ED25519 256]--+
|=..              |
|+o..   .         |
|*.o   . .        |
|+B . . .         |
|Bo. = o S        |
|Oo.+ + =         |
|oB=.* = . o      |
| =+=.+   + E     |
|. .=o   . o      |
+----[SHA256]-----+
2048 SHA256:0Onrb7/PHjpo6iVZ7xQX2riKN83FJ3KGU0TvI0TaFG4 gerrit-code-review@openocd.zylin.com (RSA)