This is an automated email from Gerrit. "Jan Matyas <mat...@codasip.com>" just uploaded a new patch set to Gerrit, which you can find at https://review.openocd.org/c/openocd/+/6880
-- gerrit commit ada5cf8665153c313f83f6894e9dd6b7bc76be44 Author: Jan Matyas <mat...@codasip.com> Date: Fri Mar 18 16:19:08 2022 +0100 target: Add option to disable SW breakpoints Added "my_target configure -allow-sw-breaks on|off". Default: on. This option is important when debugging multiple CPUs /cores with shared memory as separate targets (using separate GDB instances). In such cases, SW breakpoints cannot be reliably used: each debugger only keeps track of SW breaks inserted by itself but is still affected by breaks from other debuggers - due to the shared memory. If where was no option to disable the SW breaks, the following issues would occur during the debugging: - 1) Debugger may get unexpectedly stopped by SW BPs that they do not know about as they were placed by other debuggers. - 2) If multiple breakpoints are placed onto the same address, the breakpoint instruction may get stuck in the memory even when all debuggers removed those SW BPs (if the removal took place in a not favourable order). The option added in this commit allows the user to disable the SW breaks in such scenarios and still be able to run reliable debug sessions. Change-Id: I6857868347260eced8e96429b43209dea25d220e Signed-off-by: Jan Matyas <mat...@codasip.com> diff --git a/doc/openocd.texi b/doc/openocd.texi index 0cd9621ff4..98a7f76a23 100644 --- a/doc/openocd.texi +++ b/doc/openocd.texi @@ -4923,6 +4923,12 @@ where it is a mandatory configuration for the target run control. @xref{armcrosstrigger,,ARM Cross-Trigger Interface}, for instruction on how to declare and control a CTI instance. +@item @code{-allow-sw-breaks} @var{on|off} -- allow to disable SW breakpoints +on a per-target basis. By default, SW breakpoints are enabled (@var{on}) for +all targets. Disabling SW breakpoints may be useful when debugging multiple +targets with shared program memory, in which case the use of SW breakpoints by +multiple debuggers would lead to clashes and unexpected debug behavior. + @anchor{gdbportoverride} @item @code{-gdb-port} @var{number} -- see command @command{gdb_port} for the possible values of the parameter @var{number}, which are not only numeric values. diff --git a/src/target/breakpoints.c b/src/target/breakpoints.c index 8439ff395e..4270502e61 100644 --- a/src/target/breakpoints.c +++ b/src/target/breakpoints.c @@ -217,6 +217,11 @@ int breakpoint_add(struct target *target, uint32_t length, enum breakpoint_type type) { + if (type == BKPT_SOFT && !target->allow_sw_breaks) { + LOG_TARGET_WARNING(target, "can't add breakpoint: SW BPs are disabled"); + return ERROR_TARGET_RESOURCE_NOT_AVAILABLE; + } + if (target->smp) { struct target_list *head; diff --git a/src/target/target.c b/src/target/target.c index 7b8271339c..b7ec3a58cb 100644 --- a/src/target/target.c +++ b/src/target/target.c @@ -5293,6 +5293,7 @@ enum target_cfg_param { TCFG_DEFER_EXAMINE, TCFG_GDB_PORT, TCFG_GDB_MAX_CONNECTIONS, + TCFG_ALLOW_SW_BREAKS, }; static struct jim_nvp nvp_config_opts[] = { @@ -5310,6 +5311,7 @@ static struct jim_nvp nvp_config_opts[] = { { .name = "-defer-examine", .value = TCFG_DEFER_EXAMINE }, { .name = "-gdb-port", .value = TCFG_GDB_PORT }, { .name = "-gdb-max-connections", .value = TCFG_GDB_MAX_CONNECTIONS }, + { .name = "-allow-sw-breaks", .value = TCFG_ALLOW_SW_BREAKS }, { .name = NULL, .value = -1 } }; @@ -5572,6 +5574,7 @@ no_params: Jim_SetResultString(goi->interp, target->tap->dotted_name, -1); /* loop for more e*/ break; + case TCFG_DBGBASE: if (goi->isconfigure) { e = jim_getopt_wide(goi, &w); @@ -5586,6 +5589,7 @@ no_params: Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->dbgbase)); /* loop for more */ break; + case TCFG_RTOS: /* RTOS */ { @@ -5641,12 +5645,41 @@ no_params: goto no_params; } Jim_SetResult(goi->interp, Jim_NewIntObj(goi->interp, target->gdb_max_connections)); + /* loop for more */ break; - } + + case TCFG_ALLOW_SW_BREAKS: + if (goi->isconfigure) { + const char *s; + e = jim_getopt_string(goi, &s, NULL); + if (e != JIM_OK) + return e; + if (strcmp(s, "on") == 0) { + LOG_TARGET_INFO(target, "SW breakpoints enabled by user"); + target->allow_sw_breaks = true; + } + else if (strcmp(s, "off") == 0) { + LOG_TARGET_INFO(target, "SW breakpoints disabled by user"); + target->allow_sw_breaks = false; + } + else { + Jim_SetResultString(goi->interp, + "-allow-sw-breaks expects \"on\" or \"off\" argument", -1); + return JIM_ERR; + } + } else { + if (goi->argc != 0) + goto no_params; + } + Jim_SetResultString(goi->interp, target->allow_sw_breaks ? "on" : "off", -1); + /* loop for more */ + break; + + } /* switch (n->value) */ } /* while (goi->argc) */ - /* done - we return */ + /* done - we return */ return JIM_OK; } @@ -6275,6 +6308,8 @@ static int target_create(struct jim_getopt_info *goi) target->gdb_port_override = NULL; target->gdb_max_connections = 1; + target->allow_sw_breaks = true; + /* Do the rest as "configure" options */ goi->isconfigure = 1; e = target_configure(goi, target); diff --git a/src/target/target.h b/src/target/target.h index 1f1a354207..f386790e88 100644 --- a/src/target/target.h +++ b/src/target/target.h @@ -219,6 +219,17 @@ struct target { /* The semihosting information, extracted from the target. */ struct semihosting *semihosting; + + bool allow_sw_breaks; /* Option to manually disable SW breakpoints on per-target basis. + * When SW breaks are disabled, debuggers will typically + * fall back to HW breakpoints. + * + * Disabling SW breaks is useful when debugging multiple + * cores with symmetric memory view as independent targets. + * SW breakpoints are problematic in this case as they are + * inherently shared via the main memory but no debugger + * is aware of SW breaks placed by other debuggers. */ + }; struct target_list { --