rtos/hwthread: add hardware-thread pseudo rtos 99/3999/24
authorMatthias Welwarsky <matthias.welwarsky@sysgo.com>
Fri, 17 Feb 2017 15:16:51 +0000 (16:16 +0100)
committerMatthias Welwarsky <matthias@welwarsky.de>
Fri, 8 Mar 2019 12:54:47 +0000 (12:54 +0000)
This patch adds "hwthread", a pseudo rtos that represents cpu cores
in an SMP system as threads to gdb. This allows to debug SMP
system kernels in a more sensible manner and removes the current
atrocities of switching gdb manually between CPU cores to update
the context.

Change-Id: Ib781c6c34097689d21d9e02011e4d74a4a742379
Signed-off-by: Matthias Welwarsky <matthias.welwarsky@sysgo.com>
Reviewed-on: http://openocd.zylin.com/3999
Tested-by: jenkins
Reviewed-by: Tim Newsome <tim@sifive.com>
Reviewed-by: Matthias Welwarsky <matthias@welwarsky.de>
Reviewed-by: Graham Sanderson <graham.sanderson@gmail.com>
doc/openocd.texi
src/rtos/Makefile.am
src/rtos/hwthread.c [new file with mode: 0644]
src/rtos/rtos.c

index 2e04182b3c7eae7eb034e40da9da43e40f7a8a18..bbb907558950b58bd80f7905c866632f6f82f1fb 100644 (file)
@@ -10199,55 +10199,6 @@ and GDB would require stopping the target to get the prompt back.
 Do not use this mode under an IDE like Eclipse as it caches values of
 previously shown varibles.
 
-@anchor{usingopenocdsmpwithgdb}
-@section Using OpenOCD SMP with GDB
-@cindex SMP
-For SMP support following GDB serial protocol packet have been defined :
-@itemize @bullet
-@item j - smp status request
-@item J - smp set request
-@end itemize
-
-OpenOCD implements :
-@itemize @bullet
-@item @option{jc} packet for reading core id displayed by
-GDB connection. Reply is @option{XXXXXXXX} (8 hex digits giving core id) or
- @option{E01} for target not smp.
-@item @option{JcXXXXXXXX} (8 hex digits) packet for setting core id displayed at next GDB continue
-(core id -1 is reserved for returning to normal resume mode). Reply @option{E01}
-for target not smp or @option{OK} on success.
-@end itemize
-
-Handling of this packet within GDB can be done :
-@itemize @bullet
-@item by the creation of an internal variable (i.e @option{_core}) by mean
-of function allocate_computed_value allowing following GDB command.
-@example
-set $_core 1
-#Jc01 packet is sent
-print $_core
-#jc packet is sent and result is affected in $
-@end example
-
-@item by the usage of GDB maintenance command as described in following example (2 cpus in SMP with
-core id 0 and 1 @pxref{definecputargetsworkinginsmp,,Define CPU targets working in SMP}).
-
-@example
-# toggle0 : force display of coreid 0
-define toggle0
-maint packet Jc0
-continue
-main packet Jc-1
-end
-# toggle1 : force display of coreid 1
-define toggle1
-maint packet Jc1
-continue
-main packet Jc-1
-end
-@end example
-@end itemize
-
 @section RTOS Support
 @cindex RTOS Support
 @anchor{gdbrtossupport}
@@ -10278,12 +10229,11 @@ Currently supported rtos's include:
 @item @option{mqx}
 @item @option{uCOS-III}
 @item @option{nuttx}
+@item @option{hwthread} (This is not an actual RTOS. @xref{usingopenocdsmpwithgdb,,Using OpenOCD SMP with GDB}.)
 @end itemize
 
-@quotation Note
 Before an RTOS can be detected, it must export certain symbols; otherwise, it cannot
 be used by OpenOCD. Below is a list of the required symbols for each supported RTOS.
-@end quotation
 
 @table @code
 @item eCos symbols
@@ -10330,6 +10280,72 @@ contrib/rtos-helpers/FreeRTOS-openocd.c
 contrib/rtos-helpers/uCOS-III-openocd.c
 @end table
 
+@anchor{usingopenocdsmpwithgdb}
+@section Using OpenOCD SMP with GDB
+@cindex SMP
+@cindex RTOS
+@cindex hwthread
+OpenOCD includes a pseudo RTOS called @emph{hwthread} that presents CPU cores
+("hardware threads") in an SMP system as threads to GDB. With this extension,
+GDB can be used to inspect the state of an SMP system in a natural way.
+After halting the system, using the GDB command @command{info threads} will
+list the context of each active CPU core in the system. GDB's @command{thread}
+command can be used to switch the view to a different CPU core.
+The @command{step} and @command{stepi} commands can be used to step a specific core
+while other cores are free-running or remain halted, depending on the
+scheduler-locking mode configured in GDB.
+
+@section Legacy SMP core switching support
+@quotation Note
+This method is deprecated in favor of the @emph{hwthread} pseudo RTOS.
+@end quotation
+
+For SMP support following GDB serial protocol packet have been defined :
+@itemize @bullet
+@item j - smp status request
+@item J - smp set request
+@end itemize
+
+OpenOCD implements :
+@itemize @bullet
+@item @option{jc} packet for reading core id displayed by
+GDB connection. Reply is @option{XXXXXXXX} (8 hex digits giving core id) or
+ @option{E01} for target not smp.
+@item @option{JcXXXXXXXX} (8 hex digits) packet for setting core id displayed at next GDB continue
+(core id -1 is reserved for returning to normal resume mode). Reply @option{E01}
+for target not smp or @option{OK} on success.
+@end itemize
+
+Handling of this packet within GDB can be done :
+@itemize @bullet
+@item by the creation of an internal variable (i.e @option{_core}) by mean
+of function allocate_computed_value allowing following GDB command.
+@example
+set $_core 1
+#Jc01 packet is sent
+print $_core
+#jc packet is sent and result is affected in $
+@end example
+
+@item by the usage of GDB maintenance command as described in following example (2 cpus in SMP with
+core id 0 and 1 @pxref{definecputargetsworkinginsmp,,Define CPU targets working in SMP}).
+
+@example
+# toggle0 : force display of coreid 0
+define toggle0
+maint packet Jc0
+continue
+main packet Jc-1
+end
+# toggle1 : force display of coreid 1
+define toggle1
+maint packet Jc1
+continue
+main packet Jc-1
+end
+@end example
+@end itemize
+
 @node Tcl Scripting API
 @chapter Tcl Scripting API
 @cindex Tcl Scripting API
index bbf66a63497671fdc98b6801b01e15d45db33ca5..38adb6e923660c4735703a71e0bf5b9e791ba12a 100644 (file)
@@ -17,6 +17,7 @@ noinst_LTLIBRARIES += %D%/librtos.la
        %D%/mqx.c \
        %D%/uCOS-III.c \
        %D%/nuttx.c \
+       %D%/hwthread.c \
        %D%/rtos.h \
        %D%/rtos_standard_stackings.h \
        %D%/rtos_ecos_stackings.h \
diff --git a/src/rtos/hwthread.c b/src/rtos/hwthread.c
new file mode 100644 (file)
index 0000000..2d9e42f
--- /dev/null
@@ -0,0 +1,347 @@
+/***************************************************************************
+ *                                                                         *
+ *   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
+#include "config.h"
+#endif
+
+#include <helper/time_support.h>
+#include <jtag/jtag.h>
+#include "target/target.h"
+#include "target/target_type.h"
+#include "target/register.h"
+#include "rtos.h"
+#include "helper/log.h"
+#include "helper/types.h"
+#include "server/gdb_server.h"
+
+static bool hwthread_detect_rtos(struct target *target);
+static int hwthread_create(struct target *target);
+static int hwthread_update_threads(struct rtos *rtos);
+static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
+                                       struct rtos_reg **reg_list, int *num_regs);
+static int hwthread_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[]);
+static int hwthread_smp_init(struct target *target);
+
+#define HW_THREAD_NAME_STR_SIZE (32)
+
+extern int rtos_thread_packet(struct connection *connection, const char *packet, int packet_size);
+
+static inline threadid_t threadid_from_target(const struct target *target)
+{
+       return target->coreid + 1;
+}
+
+const struct rtos_type hwthread_rtos = {
+       .name = "hwthread",
+       .detect_rtos = hwthread_detect_rtos,
+       .create = hwthread_create,
+       .update_threads = hwthread_update_threads,
+       .get_thread_reg_list = hwthread_get_thread_reg_list,
+       .get_symbol_list_to_lookup = hwthread_get_symbol_list_to_lookup,
+       .smp_init = hwthread_smp_init,
+};
+
+struct hwthread_params {
+       int dummy_param;
+};
+
+static int hwthread_fill_thread(struct rtos *rtos, struct target *curr, int thread_num)
+{
+       char tmp_str[HW_THREAD_NAME_STR_SIZE];
+       threadid_t tid = threadid_from_target(curr);
+
+       memset(tmp_str, 0, HW_THREAD_NAME_STR_SIZE);
+
+       /* thread-id is the core-id of this core inside the SMP group plus 1 */
+       rtos->thread_details[thread_num].threadid = tid;
+       /* create the thread name */
+       rtos->thread_details[thread_num].exists = true;
+       rtos->thread_details[thread_num].thread_name_str = strdup(target_name(curr));
+       snprintf(tmp_str, HW_THREAD_NAME_STR_SIZE-1, "state: %s", debug_reason_name(curr));
+       rtos->thread_details[thread_num].extra_info_str = strdup(tmp_str);
+
+       return ERROR_OK;
+}
+
+static int hwthread_update_threads(struct rtos *rtos)
+{
+       int threads_found = 0;
+       int thread_list_size = 0;
+       struct target_list *head;
+       struct target *target;
+       int64_t current_thread = 0;
+       enum target_debug_reason current_reason = DBG_REASON_UNDEFINED;
+
+       if (rtos == NULL)
+               return -1;
+
+       target = rtos->target;
+
+       /* wipe out previous thread details if any */
+       rtos_free_threadlist(rtos);
+
+       /* determine the number of "threads" */
+       if (target->smp) {
+               for (head = target->head; head != NULL; head = head->next) {
+                       struct target *curr = head->target;
+
+                       if (!target_was_examined(curr))
+                               continue;
+
+                       ++thread_list_size;
+               }
+       } else
+               thread_list_size = 1;
+
+       /* create space for new thread details */
+       rtos->thread_details = malloc(sizeof(struct thread_detail) * thread_list_size);
+
+       if (target->smp) {
+               /* loop over all threads */
+               for (head = target->head; head != NULL; head = head->next) {
+                       struct target *curr = head->target;
+
+                       if (!target_was_examined(curr))
+                               continue;
+
+                       threadid_t tid = threadid_from_target(curr);
+
+                       hwthread_fill_thread(rtos, curr, threads_found);
+
+                       /* find an interesting thread to set as current */
+                       switch (current_reason) {
+                       case DBG_REASON_UNDEFINED:
+                               current_reason = curr->debug_reason;
+                               current_thread = tid;
+                               break;
+                       case DBG_REASON_SINGLESTEP:
+                               /* single-step can only be overridden by itself */
+                               if (curr->debug_reason == DBG_REASON_SINGLESTEP) {
+                                       if (tid == rtos->current_threadid)
+                                               current_thread = tid;
+                               }
+                               break;
+                       case DBG_REASON_BREAKPOINT:
+                               /* single-step overrides breakpoint */
+                               if (curr->debug_reason == DBG_REASON_SINGLESTEP) {
+                                       current_reason = curr->debug_reason;
+                                       current_thread = tid;
+                               } else
+                               /* multiple breakpoints, prefer gdbs' threadid */
+                               if (curr->debug_reason == DBG_REASON_BREAKPOINT) {
+                                       if (tid == rtos->current_threadid)
+                                               current_thread = tid;
+                               }
+                               break;
+                       case DBG_REASON_WATCHPOINT:
+                               /* breakpoint and single-step override watchpoint */
+                               if (curr->debug_reason == DBG_REASON_SINGLESTEP ||
+                                               curr->debug_reason == DBG_REASON_BREAKPOINT) {
+                                       current_reason = curr->debug_reason;
+                                       current_thread = tid;
+                               }
+                               break;
+                       case DBG_REASON_DBGRQ:
+                               /* all other reasons override debug-request */
+                               if (curr->debug_reason == DBG_REASON_SINGLESTEP ||
+                                               curr->debug_reason == DBG_REASON_WATCHPOINT ||
+                                               curr->debug_reason == DBG_REASON_BREAKPOINT) {
+                                       current_reason = curr->debug_reason;
+                                       current_thread = tid;
+                               } else
+                               if (curr->debug_reason == DBG_REASON_DBGRQ) {
+                                       if (tid == rtos->current_threadid)
+                                               current_thread = tid;
+                               }
+
+                               break;
+
+                       default:
+                               break;
+                       }
+
+                       threads_found++;
+               }
+       } else {
+               hwthread_fill_thread(rtos, target, threads_found);
+               current_thread = threadid_from_target(target);
+               threads_found++;
+       }
+
+       rtos->thread_count = threads_found;
+
+       /* we found an interesting thread, set it as current */
+       if (current_thread != 0)
+               rtos->current_thread = current_thread;
+       else if (rtos->current_threadid != 0)
+               rtos->current_thread = rtos->current_threadid;
+       else
+               rtos->current_thread = threadid_from_target(target);
+
+       LOG_DEBUG("%s current_thread=%i", __func__, (int)rtos->current_thread);
+       return 0;
+}
+
+static int hwthread_smp_init(struct target *target)
+{
+       return hwthread_update_threads(target->rtos);
+}
+
+static int hwthread_get_thread_reg_list(struct rtos *rtos, int64_t thread_id,
+                                       struct rtos_reg **rtos_reg_list, int *num_regs)
+{
+       struct target_list *head;
+       struct target *target;
+       struct target *curr;
+       struct reg **reg_list;
+       int retval;
+
+       if (rtos == NULL)
+               return ERROR_FAIL;
+
+       target = rtos->target;
+
+       /* Find the thread with that thread_id */
+       if (target->smp) {
+               curr = NULL;
+               for (head = target->head; head != NULL; head = head->next) {
+                       curr = head->target;
+
+                       if (thread_id == threadid_from_target(curr))
+                               break;
+               }
+
+               if (head == NULL)
+                       return ERROR_FAIL;
+       } else {
+               curr = target;
+               if (thread_id != threadid_from_target(curr))
+                       return ERROR_FAIL;
+
+       }
+
+       if (!target_was_examined(curr))
+               return ERROR_FAIL;
+
+       retval = target_get_gdb_reg_list(curr, &reg_list, num_regs,
+                       REG_CLASS_GENERAL);
+       if (retval != ERROR_OK)
+               return retval;
+
+       *rtos_reg_list = calloc(*num_regs, sizeof(struct rtos_reg));
+       if (*rtos_reg_list == NULL) {
+               free(reg_list);
+               return ERROR_FAIL;
+       }
+
+       for (int i = 0; i < *num_regs; i++) {
+               (*rtos_reg_list)[i].number = (*reg_list)[i].number;
+               (*rtos_reg_list)[i].size = (*reg_list)[i].size;
+               memcpy((*rtos_reg_list)[i].value, (*reg_list)[i].value,
+                      ((*reg_list)[i].size + 7) / 8);
+       }
+
+       free(reg_list);
+
+       return ERROR_OK;
+
+}
+
+static int hwthread_get_symbol_list_to_lookup(symbol_table_elem_t *symbol_list[])
+{
+       /* return an empty list, we don't have any symbols to look up */
+       *symbol_list = calloc(1, sizeof(symbol_table_elem_t));
+       (*symbol_list)[0].symbol_name = NULL;
+       return 0;
+}
+
+static int hwthread_target_for_threadid(struct connection *connection, int64_t thread_id, struct target **p_target)
+{
+       struct target *target = get_target_from_connection(connection);
+       struct target_list *head;
+       struct target *curr;
+
+       if (target->smp) {
+               /* Find the thread with that thread_id */
+               curr = NULL;
+               for (head = target->head; head != NULL; head = head->next) {
+                       curr = head->target;
+
+                       if (thread_id == threadid_from_target(curr))
+                               break;
+               }
+
+               if (head == NULL)
+                       return ERROR_FAIL;
+       } else {
+               curr = target;
+               if (thread_id != threadid_from_target(curr))
+                       return ERROR_FAIL;
+       }
+
+       *p_target = curr;
+
+       return ERROR_OK;
+}
+
+static bool hwthread_detect_rtos(struct target *target)
+{
+       /* always return 0, avoid auto-detection */
+       return false;
+}
+
+static int hwthread_thread_packet(struct connection *connection, const char *packet, int packet_size)
+{
+       struct target *target = get_target_from_connection(connection);
+
+       struct target *curr = NULL;
+       int64_t current_threadid;
+
+       if (packet[0] == 'H' && packet[1] == 'g') {
+               sscanf(packet, "Hg%16" SCNx64, &current_threadid);
+
+               if (current_threadid > 0) {
+                       if (hwthread_target_for_threadid(connection, current_threadid, &curr) != ERROR_OK) {
+                               LOG_ERROR("hwthread: cannot find thread id %"PRId64, current_threadid);
+                               gdb_put_packet(connection, "E01", 3);
+                               return ERROR_FAIL;
+                       }
+                       target->rtos->current_thread = current_threadid;
+               } else
+               if (current_threadid == 0 || current_threadid == -1)
+                       target->rtos->current_thread = threadid_from_target(target);
+
+               target->rtos->current_threadid = current_threadid;
+
+               gdb_put_packet(connection, "OK", 2);
+               return ERROR_OK;
+       }
+
+       return rtos_thread_packet(connection, packet, packet_size);
+}
+
+static int hwthread_create(struct target *target)
+{
+       LOG_INFO("Hardware thread awareness created");
+
+       target->rtos->rtos_specific_params = NULL;
+       target->rtos->current_thread = 0;
+       target->rtos->thread_details = NULL;
+       target->rtos->gdb_target_for_threadid = hwthread_target_for_threadid;
+       target->rtos->gdb_thread_packet = hwthread_thread_packet;
+       return 0;
+}
index cd2e271f68beea23b37c445f29a1beede3e53a64..da0a5036295bdbc3f12d9ca2e44d86b225bf64b9 100644 (file)
@@ -37,6 +37,7 @@ extern struct rtos_type embKernel_rtos;
 extern struct rtos_type mqx_rtos;
 extern struct rtos_type uCOS_III_rtos;
 extern struct rtos_type nuttx_rtos;
+extern struct rtos_type hwthread_rtos;
 
 static struct rtos_type *rtos_types[] = {
        &ThreadX_rtos,
@@ -49,6 +50,7 @@ static struct rtos_type *rtos_types[] = {
        &mqx_rtos,
        &uCOS_III_rtos,
        &nuttx_rtos,
+       &hwthread_rtos,
        NULL
 };
 

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)