public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH v6 0/7] extend branch tracing to use ARM CoreSight traces
@ 2021-05-31 21:33 Zied Guermazi
  2021-05-31 21:33 ` [PATCH v6 1/7] configure gdb build system for supporting btrace on arm processors Zied Guermazi
                   ` (6 more replies)
  0 siblings, 7 replies; 35+ messages in thread
From: Zied Guermazi @ 2021-05-31 21:33 UTC (permalink / raw)
  To: gdb-patches, markus.t.metzger; +Cc: Zied Guermazi

 This patch set adds support for branch tracing in GDB on arm platforms
 using coresight etm traces
 Branch tracing offers instructions and functions histories as well as
 execution record and replay (forwards and reverse debugging)
 this patch set was tested using the gdb.btrace testsuite on ARMv7 and ARMv8
 cortex A processors running linux operating system with coresight drivers.
 
 test results of gdb.btrace testsuite on ARMv8, variation unix
 # of expected passes		595
 # of unexpected failures	1
 # of unsupported tests	1

 test results of gdb.btrace testsuite on ARMv8 variation native-gdbserver
 # of expected passes		582
 # of unexpected failures	4
 # of unsupported tests	2
 
 test results of gdb.btrace testsuite  on ARMv7, variation unix
 # of expected passes		588
 # of unexpected failures	7
 # of untested testcases	1
 # of unsupported tests	1

 
 test results of gdb.btrace testsuite on ARMv7 variation native-gdbserver
 # of expected passes		583
 # of unexpected failures	4
 # of unsupported tests	2

 
 known limitations:
 for arm v7 only
- some registers needed for breakpoint address calculation in forwards
  executions may be missing.
 for arm v7 and arm v8
- sometimes trace buffers are not flushed properly or flushed in
  the buffer of a different thread. this was observed
  in test scripts only.
- sometimes, in non-stop mode, while executing "stepi n" for two threads
  in parallel, a thread is hitting breakpoints intended for stepping
  the other one.
  Gdb recognizes that it is intended for the second thread,
  but decreases the step counter. leading to a stepping less than "n"
  times.
 patches are:

Zied Guermazi (7):
  configure gdb build system for supporting btrace on arm processors
  add btrace coresight related commands
  start/stop btrace with coresight etm and parse etm buffer. nat
    independant
  start/stop btrace with coresight etm and collect etm buffer on linux
    os
  fix issue: gdb hangs in the command following a commad returning with
    TARGET_WAITKIND_NO_HISTORY
  add support for coresight btrace via remote protocol
  adapt btrace testcases for arm target

 gdb/Makefile.in                               |   5 +-
 gdb/NEWS                                      |  27 +
 gdb/aarch64-linux-nat.c                       |  68 ++
 gdb/arch/arm.h                                |  33 +
 gdb/arm-linux-nat.c                           |  68 ++
 gdb/btrace.c                                  | 986 +++++++++++++++++-
 gdb/btrace.h                                  |  16 +-
 gdb/config.in                                 |   3 +
 gdb/configure                                 | 549 ++++++++++
 gdb/configure.nat                             |   4 +-
 gdb/doc/gdb.texinfo                           | 114 +-
 gdb/features/btrace-conf.dtd                  |  10 +-
 gdb/features/btrace.dtd                       |  38 +-
 gdb/infrun.c                                  |   3 +-
 gdb/nat/linux-btrace.c                        | 395 ++++++-
 gdb/nat/linux-btrace.h                        |  19 +
 gdb/record-btrace.c                           | 174 +++-
 gdb/record.c                                  |   2 +
 gdb/remote.c                                  |  66 +-
 .../gdb.btrace/aarch64-instruction_history.S  |  31 +
 .../gdb.btrace/aarch64-record_goto.S          | 399 +++++++
 .../gdb.btrace/aarch64-tailcall-only.S        | 516 +++++++++
 gdb/testsuite/gdb.btrace/aarch64-tailcall.S   | 408 ++++++++
 .../gdb.btrace/arm-instruction_history.S      |  31 +
 gdb/testsuite/gdb.btrace/arm-record_goto.S    | 432 ++++++++
 gdb/testsuite/gdb.btrace/arm-tailcall-only.S  | 503 +++++++++
 gdb/testsuite/gdb.btrace/arm-tailcall.S       | 390 +++++++
 gdb/testsuite/gdb.btrace/buffer-size.exp      |  15 +-
 .../gdb.btrace/instruction_history.exp        | 106 +-
 gdb/testsuite/gdb.btrace/non-stop.exp         |  60 +-
 gdb/testsuite/gdb.btrace/record_goto.exp      | 253 +++--
 gdb/testsuite/gdb.btrace/stepi.exp            | 107 +-
 gdb/testsuite/gdb.btrace/tailcall-only.exp    |  20 +-
 gdb/testsuite/gdb.btrace/tailcall.exp         |  58 +-
 ...on_history.S => x86-instruction_history.S} |   0
 gdb/testsuite/lib/gdb.exp                     |   2 +-
 gdb/top.c                                     |  10 +-
 gdbserver/config.in                           |   3 +
 gdbserver/configure                           | 545 ++++++++++
 gdbserver/configure.srv                       |   4 +
 gdbserver/linux-low.cc                        | 100 +-
 gdbserver/server.cc                           |  34 +-
 gdbsupport/Makefile.in                        |   3 +
 gdbsupport/btrace-common.cc                   |  45 +
 gdbsupport/btrace-common.h                    | 128 ++-
 gdbsupport/common.m4                          |  39 +
 gdbsupport/config.in                          |   3 +
 gdbsupport/configure                          | 545 ++++++++++
 48 files changed, 7192 insertions(+), 178 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/aarch64-instruction_history.S
 create mode 100644 gdb/testsuite/gdb.btrace/aarch64-record_goto.S
 create mode 100644 gdb/testsuite/gdb.btrace/aarch64-tailcall-only.S
 create mode 100644 gdb/testsuite/gdb.btrace/aarch64-tailcall.S
 create mode 100644 gdb/testsuite/gdb.btrace/arm-instruction_history.S
 create mode 100644 gdb/testsuite/gdb.btrace/arm-record_goto.S
 create mode 100644 gdb/testsuite/gdb.btrace/arm-tailcall-only.S
 create mode 100644 gdb/testsuite/gdb.btrace/arm-tailcall.S
 rename gdb/testsuite/gdb.btrace/{instruction_history.S => x86-instruction_history.S} (100%)

-- 
2.25.1


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v6 1/7] configure gdb build system for supporting btrace on arm processors
  2021-05-31 21:33 [PATCH v6 0/7] extend branch tracing to use ARM CoreSight traces Zied Guermazi
@ 2021-05-31 21:33 ` Zied Guermazi
  2021-06-30 12:17   ` Luis Machado
  2021-05-31 21:33 ` [PATCH v6 2/7] add btrace coresight related commands Zied Guermazi
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 35+ messages in thread
From: Zied Guermazi @ 2021-05-31 21:33 UTC (permalink / raw)
  To: gdb-patches, markus.t.metzger; +Cc: Zied Guermazi

This patch adds the possibility to build GDB with support for branch tracing
using ARM CoreSight traces.
New flag is --with-arm-cs

gdb/ChangeLog

	* Makefile.in LIBOPENCSD_C_API: Regenerated.
	* config.in LIBOPENCSD_C_API: Regenerated.
	* configure: Regenerated.
	* configure.nat: add nat/linux-btrace.o to the build
	for aarch64 and arm
	* top.c (print_gdb_configuration): add --with-arm-cs
	or --without-arm-cs according to the configuration.

gdbserver/ChangeLog
	* gdbserver/configure: Regenerated.
	* gdbserver/config.in: Regenerated.

gdbsupport/ChangeLog

	* gdbsupport/common.m4: check --with-arm-cs configuration
	flag, perf_event and opencsd_c_api to set compilation flags.
	* gdbsupport/config.in: Regenerated.
	* gdbsupport/Makefile.in: Regenerated.
	* gdbsupport/configure: Regenerated.
---
 gdb/Makefile.in        |   5 +-
 gdb/config.in          |   3 +
 gdb/configure          | 549 +++++++++++++++++++++++++++++++++++++++++
 gdb/configure.nat      |   4 +-
 gdb/top.c              |  10 +-
 gdbserver/config.in    |   3 +
 gdbserver/configure    | 545 ++++++++++++++++++++++++++++++++++++++++
 gdbsupport/Makefile.in |   3 +
 gdbsupport/common.m4   |  39 +++
 gdbsupport/config.in   |   3 +
 gdbsupport/configure   | 545 ++++++++++++++++++++++++++++++++++++++++
 11 files changed, 1705 insertions(+), 4 deletions(-)

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index bb6c5dfa784..04680a22e5c 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -203,6 +203,9 @@ LIBXXHASH = @LIBXXHASH@
 # Where is libipt?  This will be empty if libipt was not available.
 LIBIPT = @LIBIPT@
 
+#where is libopencsd? this will be empty if libopencsd was not available
+LIBOPENCSD_C_API = @LIBOPENCSD_C_API@
+
 # Where is libgmp?
 LIBGMP = @LIBGMP@
 
@@ -636,7 +639,7 @@ CLIBS = $(SIM) $(READLINE) $(OPCODES) $(LIBCTF) $(BFD) $(ZLIB) \
         $(LIBSUPPORT) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \
 	$(XM_CLIBS) $(GDBTKLIBS) \
 	@LIBS@ @GUILE_LIBS@ @PYTHON_LIBS@ \
-	$(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) $(LIBIPT) \
+	$(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) $(LIBIPT) $(LIBOPENCSD_C_API) \
 	$(WIN32LIBS) $(LIBGNU) $(LIBGNU_EXTRA_LIBS) $(LIBICONV) \
 	$(LIBMPFR) $(LIBGMP) $(SRCHIGH_LIBS) $(LIBXXHASH) $(PTHREAD_LIBS) \
 	$(DEBUGINFOD_LIBS)
diff --git a/gdb/config.in b/gdb/config.in
index 99c924f9ba0..0373689a3ce 100644
--- a/gdb/config.in
+++ b/gdb/config.in
@@ -246,6 +246,9 @@
 /* Define if you have the mpfr library. */
 #undef HAVE_LIBMPFR
 
+/* Define if you have the opencsd_c_api library. */
+#undef HAVE_LIBOPENCSD_C_API
+
 /* Define to 1 if you have the <libunwind-ia64.h> header file. */
 #undef HAVE_LIBUNWIND_IA64_H
 
diff --git a/gdb/configure b/gdb/configure
index cdc112e10dc..26b075a7c61 100755
--- a/gdb/configure
+++ b/gdb/configure
@@ -705,6 +705,9 @@ SYSTEM_GDBINIT
 TARGET_SYSTEM_ROOT
 CONFIG_LDFLAGS
 RDYNAMIC
+LTLIBOPENCSD_C_API
+LIBOPENCSD_C_API
+HAVE_LIBOPENCSD_C_API
 LTLIBIPT
 LIBIPT
 HAVE_LIBIPT
@@ -911,6 +914,9 @@ enable_source_highlight
 with_intel_pt
 with_libipt_prefix
 with_libipt_type
+with_arm_cs
+with_libopencsd_c_api_prefix
+with_libopencsd_c_api_type
 with_sysroot
 with_system_gdbinit
 with_system_gdbinit_dir
@@ -1660,6 +1666,15 @@ Optional Packages:
   --with-libipt-prefix[=DIR]  search for libipt in DIR/include and DIR/lib
   --without-libipt-prefix     don't search for libipt in includedir and libdir
   --with-libipt-type=TYPE     type of library to search for (auto/static/shared)
+  --with-arm-cs           include ARM CoreSight Processor Trace support
+                          (auto/yes/no)
+  --with-libopencsd_c_api-prefix[=DIR]  search for libopencsd_c_api in DIR/include and DIR/lib
+  --without-libopencsd_c_api-prefix     don't search for libopencsd_c_api in includedir and libdir
+  --with-libopencsd_c_api-type=TYPE     type of library to search for (auto/static/shared)
+  --without-included-regex
+                          don't use included regex; this is the default on
+                          systems with version 2 of the GNU C library (use
+                          with caution on other system)
   --with-sysroot[=DIR]    search for usr/lib et al within DIR
   --with-system-gdbinit=PATH
                           automatically load a system-wide gdbinit file
@@ -15238,6 +15253,540 @@ fi
     fi
   fi
 
+  # ARM CoreSight trace       #
+
+
+# Check whether --with-arm_cs was given.
+if test "${with_arm_cs+set}" = set; then :
+  withval=$with_arm_cs;
+else
+  with_arm_cs=auto
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use ARM CoreSight Processor Trace " >&5
+$as_echo_n "checking whether to use ARM CoreSight Processor Trace ... " >&6; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_arm_cs" >&5
+$as_echo "$with_arm_cs" >&6; }
+
+  if test "${with_arm_cs}" = no; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&5
+$as_echo "$as_me: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&2;}
+    HAVE_LIBOPENCSD=no
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+  #include <linux/perf_event.h>
+  #ifndef PERF_ATTR_SIZE_VER5
+  # error
+  #endif
+
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  perf_event=yes
+else
+  perf_event=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+    if test "$perf_event" != yes; then
+      if test "$with_arm_cs" = yes; then
+        as_fn_error $? "linux/perf_event.h missing or too old" "$LINENO" 5
+      else
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&5
+$as_echo "$as_me: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&2;}
+      fi
+    fi
+
+
+
+
+
+
+
+
+
+    use_additional=yes
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+
+    eval additional_includedir=\"$includedir\"
+    eval additional_libdir=\"$libdir\"
+
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+
+# Check whether --with-libopencsd_c_api-prefix was given.
+if test "${with_libopencsd_c_api_prefix+set}" = set; then :
+  withval=$with_libopencsd_c_api_prefix;
+    if test "X$withval" = "Xno"; then
+      use_additional=no
+    else
+      if test "X$withval" = "X"; then
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+
+          eval additional_includedir=\"$includedir\"
+          eval additional_libdir=\"$libdir\"
+
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+      else
+        additional_includedir="$withval/include"
+        additional_libdir="$withval/lib"
+      fi
+    fi
+
+fi
+
+
+# Check whether --with-libopencsd_c_api-type was given.
+if test "${with_libopencsd_c_api_type+set}" = set; then :
+  withval=$with_libopencsd_c_api_type;  with_libopencsd_c_api_type=$withval
+else
+   with_libopencsd_c_api_type=auto
+fi
+
+  lib_type=`eval echo \$with_libopencsd_c_api_type`
+
+      LIBOPENCSD_C_API=
+  LTLIBOPENCSD_C_API=
+  INCOPENCSD_C_API=
+  rpathdirs=
+  ltrpathdirs=
+  names_already_handled=
+  names_next_round='opencsd_c_api '
+  while test -n "$names_next_round"; do
+    names_this_round="$names_next_round"
+    names_next_round=
+    for name in $names_this_round; do
+      already_handled=
+      for n in $names_already_handled; do
+        if test "$n" = "$name"; then
+          already_handled=yes
+          break
+        fi
+      done
+      if test -z "$already_handled"; then
+        names_already_handled="$names_already_handled $name"
+                        uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+        eval value=\"\$HAVE_LIB$uppername\"
+        if test -n "$value"; then
+          if test "$value" = yes; then
+            eval value=\"\$LIB$uppername\"
+            test -z "$value" || LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$value"
+            eval value=\"\$LTLIB$uppername\"
+            test -z "$value" || LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$value"
+          else
+                                    :
+          fi
+        else
+                              found_dir=
+          found_la=
+          found_so=
+          found_a=
+          if test $use_additional = yes; then
+            if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+              found_dir="$additional_libdir"
+              found_so="$additional_libdir/lib$name.$shlibext"
+              if test -f "$additional_libdir/lib$name.la"; then
+                found_la="$additional_libdir/lib$name.la"
+              fi
+            elif test x$lib_type != xshared; then
+              if test -f "$additional_libdir/lib$name.$libext"; then
+                found_dir="$additional_libdir"
+                found_a="$additional_libdir/lib$name.$libext"
+                if test -f "$additional_libdir/lib$name.la"; then
+                  found_la="$additional_libdir/lib$name.la"
+                fi
+              fi
+            fi
+          fi
+          if test "X$found_dir" = "X"; then
+            for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+              case "$x" in
+                -L*)
+                  dir=`echo "X$x" | sed -e 's/^X-L//'`
+                  if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+                    found_dir="$dir"
+                    found_so="$dir/lib$name.$shlibext"
+                    if test -f "$dir/lib$name.la"; then
+                      found_la="$dir/lib$name.la"
+                    fi
+                  elif test x$lib_type != xshared; then
+                    if test -f "$dir/lib$name.$libext"; then
+                      found_dir="$dir"
+                      found_a="$dir/lib$name.$libext"
+                      if test -f "$dir/lib$name.la"; then
+                        found_la="$dir/lib$name.la"
+                      fi
+                    fi
+                  fi
+                  ;;
+              esac
+              if test "X$found_dir" != "X"; then
+                break
+              fi
+            done
+          fi
+          if test "X$found_dir" != "X"; then
+                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$found_dir -l$name"
+            if test "X$found_so" != "X"; then
+                                                        if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
+                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+              else
+                                                                                haveit=
+                for x in $ltrpathdirs; do
+                  if test "X$x" = "X$found_dir"; then
+                    haveit=yes
+                    break
+                  fi
+                done
+                if test -z "$haveit"; then
+                  ltrpathdirs="$ltrpathdirs $found_dir"
+                fi
+                                if test "$hardcode_direct" = yes; then
+                                                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+                else
+                  if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+                                                            LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+                                                            haveit=
+                    for x in $rpathdirs; do
+                      if test "X$x" = "X$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      rpathdirs="$rpathdirs $found_dir"
+                    fi
+                  else
+                                                                                haveit=
+                    for x in $LDFLAGS $LIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                      if test "X$x" = "X-L$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir"
+                    fi
+                    if test "$hardcode_minus_L" != no; then
+                                                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+                    else
+                                                                                                                                                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
+                    fi
+                  fi
+                fi
+              fi
+            else
+              if test "X$found_a" != "X"; then
+                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_a"
+              else
+                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir -l$name"
+              fi
+            fi
+                        additional_includedir=
+            case "$found_dir" in
+              */lib | */lib/)
+                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
+                additional_includedir="$basedir/include"
+                ;;
+            esac
+            if test "X$additional_includedir" != "X"; then
+                                                                                                                if test "X$additional_includedir" != "X/usr/include"; then
+                haveit=
+                if test "X$additional_includedir" = "X/usr/local/include"; then
+                  if test -n "$GCC"; then
+                    case $host_os in
+                      linux*) haveit=yes;;
+                    esac
+                  fi
+                fi
+                if test -z "$haveit"; then
+                  for x in $CPPFLAGS $INCOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                    if test "X$x" = "X-I$additional_includedir"; then
+                      haveit=yes
+                      break
+                    fi
+                  done
+                  if test -z "$haveit"; then
+                    if test -d "$additional_includedir"; then
+                                            INCOPENCSD_C_API="${INCOPENCSD_C_API}${INCOPENCSD_C_API:+ }-I$additional_includedir"
+                    fi
+                  fi
+                fi
+              fi
+            fi
+                        if test -n "$found_la"; then
+                                                        save_libdir="$libdir"
+              case "$found_la" in
+                */* | *\\*) . "$found_la" ;;
+                *) . "./$found_la" ;;
+              esac
+              libdir="$save_libdir"
+                            for dep in $dependency_libs; do
+                case "$dep" in
+                  -L*)
+                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+                                                                                                                                                                if test "X$additional_libdir" != "X/usr/lib"; then
+                      haveit=
+                      if test "X$additional_libdir" = "X/usr/local/lib"; then
+                        if test -n "$GCC"; then
+                          case $host_os in
+                            linux*) haveit=yes;;
+                          esac
+                        fi
+                      fi
+                      if test -z "$haveit"; then
+                        haveit=
+                        for x in $LDFLAGS $LIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$additional_libdir"
+                          fi
+                        fi
+                        haveit=
+                        for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                                                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$additional_libdir"
+                          fi
+                        fi
+                      fi
+                    fi
+                    ;;
+                  -R*)
+                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
+                    if test "$enable_rpath" != no; then
+                                                                  haveit=
+                      for x in $rpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        rpathdirs="$rpathdirs $dir"
+                      fi
+                                                                  haveit=
+                      for x in $ltrpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        ltrpathdirs="$ltrpathdirs $dir"
+                      fi
+                    fi
+                    ;;
+                  -l*)
+                                        names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+                    ;;
+                  *.la)
+                                                                                names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+                    ;;
+                  *)
+                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$dep"
+                    LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$dep"
+                    ;;
+                esac
+              done
+            fi
+          else
+                                                            if test "x$lib_type" = "xauto" || test "x$lib_type" = "xshared"; then
+              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
+              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l$name"
+            else
+              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l:lib$name.$libext"
+              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l:lib$name.$libext"
+            fi
+          fi
+        fi
+      fi
+    done
+  done
+  if test "X$rpathdirs" != "X"; then
+    if test -n "$hardcode_libdir_separator"; then
+                        alldirs=
+      for found_dir in $rpathdirs; do
+        alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+      done
+            acl_save_libdir="$libdir"
+      libdir="$alldirs"
+      eval flag=\"$hardcode_libdir_flag_spec\"
+      libdir="$acl_save_libdir"
+      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
+    else
+            for found_dir in $rpathdirs; do
+        acl_save_libdir="$libdir"
+        libdir="$found_dir"
+        eval flag=\"$hardcode_libdir_flag_spec\"
+        libdir="$acl_save_libdir"
+        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
+      done
+    fi
+  fi
+  if test "X$ltrpathdirs" != "X"; then
+            for found_dir in $ltrpathdirs; do
+      LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-R$found_dir"
+    done
+  fi
+
+
+        ac_save_CPPFLAGS="$CPPFLAGS"
+
+  for element in $INCOPENCSD_C_API; do
+    haveit=
+    for x in $CPPFLAGS; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+      if test "X$x" = "X$element"; then
+        haveit=yes
+        break
+      fi
+    done
+    if test -z "$haveit"; then
+      CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+    fi
+  done
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libopencsd_c_api" >&5
+$as_echo_n "checking for libopencsd_c_api... " >&6; }
+if ${ac_cv_libopencsd_c_api+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    ac_save_LIBS="$LIBS"
+    LIBS="$LIBS $LIBOPENCSD_C_API"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include "opencsd/c_api/opencsd_c_api.h"
+int
+main ()
+{
+ocsd_get_version();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_libopencsd_c_api=yes
+else
+  ac_cv_libopencsd_c_api=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+    LIBS="$ac_save_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libopencsd_c_api" >&5
+$as_echo "$ac_cv_libopencsd_c_api" >&6; }
+  if test "$ac_cv_libopencsd_c_api" = yes; then
+    HAVE_LIBOPENCSD_C_API=yes
+
+$as_echo "#define HAVE_LIBOPENCSD_C_API 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libopencsd_c_api" >&5
+$as_echo_n "checking how to link with libopencsd_c_api... " >&6; }
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBOPENCSD_C_API" >&5
+$as_echo "$LIBOPENCSD_C_API" >&6; }
+  else
+    HAVE_LIBOPENCSD_C_API=no
+            CPPFLAGS="$ac_save_CPPFLAGS"
+    LIBOPENCSD_C_API=
+    LTLIBOPENCSD_C_API=
+  fi
+
+
+
+
+
+
+    if test "$HAVE_LIBOPENCSD_C_API" != yes; then
+      if test "$with_arm_cs" = yes; then
+        as_fn_error $? "libopencsd_c_api is missing or unusable" "$LINENO" 5
+      else
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&5
+$as_echo "$as_me: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&2;}
+      fi
+    else
+      LIBS="$LIBS $LIBOPENCSD_C_API"
+    fi
+  fi
+
+
 
 $as_echo "#define _STRUCTURED_PROC 1" >>confdefs.h
 
diff --git a/gdb/configure.nat b/gdb/configure.nat
index e34cccffd98..135041bd031 100644
--- a/gdb/configure.nat
+++ b/gdb/configure.nat
@@ -235,7 +235,7 @@ case ${gdb_host} in
 		#  Host: AArch64 based machine running GNU/Linux
 		NATDEPFILES="${NATDEPFILES} aarch64-linux-nat.o \
 		aarch32-linux-nat.o nat/aarch64-linux-hw-point.o \
-		nat/aarch64-linux.o \
+		nat/aarch64-linux.o nat/linux-btrace.o \
 		nat/aarch64-sve-linux-ptrace.o \
 		nat/aarch64-mte-linux-ptrace.o"
 		;;
@@ -246,7 +246,7 @@ case ${gdb_host} in
 	    arm)
 		# Host: ARM based machine running GNU/Linux
 		NATDEPFILES="${NATDEPFILES} arm-linux-nat.o \
-		aarch32-linux-nat.o"
+		aarch32-linux-nat.o nat/linux-btrace.o"
 		;;
 	    i386)
 		# Host: Intel 386 running GNU/Linux.
diff --git a/gdb/top.c b/gdb/top.c
index 6e0f43d2fd9..53f04ee29af 100644
--- a/gdb/top.c
+++ b/gdb/top.c
@@ -1538,7 +1538,15 @@ This GDB was configured as follows:\n\
 	     --without-intel-pt\n\
 "));
 #endif
-
+#if HAVE_LIBOPENCSD_C_API
+    fprintf_filtered (stream, _("\
+             --with-arm-cs\n\
+"));
+#else
+    fprintf_filtered (stream, _("\
+             --without-arm-cs\n\
+"));
+#endif
 #if HAVE_LIBMPFR
   fprintf_filtered (stream, _("\
 	     --with-mpfr\n\
diff --git a/gdbserver/config.in b/gdbserver/config.in
index 611bfd7aa76..fbcdf896ef7 100644
--- a/gdbserver/config.in
+++ b/gdbserver/config.in
@@ -139,6 +139,9 @@
 /* Define if you have the ipt library. */
 #undef HAVE_LIBIPT
 
+/* Define if you have the opencsd_c_api library. */
+#undef HAVE_LIBOPENCSD_C_API
+
 /* Define if the target supports branch tracing. */
 #undef HAVE_LINUX_BTRACE
 
diff --git a/gdbserver/configure b/gdbserver/configure
index aab72c0b8c5..e8265116c8c 100755
--- a/gdbserver/configure
+++ b/gdbserver/configure
@@ -656,6 +656,9 @@ am__leading_dot
 host_noncanonical
 target_noncanonical
 WIN32APILIBS
+LTLIBOPENCSD_C_API
+LIBOPENCSD_C_API
+HAVE_LIBOPENCSD_C_API
 LTLIBIPT
 LIBIPT
 HAVE_LIBIPT
@@ -751,6 +754,9 @@ with_gnu_ld
 enable_rpath
 with_libipt_prefix
 with_libipt_type
+with_arm_cs
+with_libopencsd_c_api_prefix
+with_libopencsd_c_api_type
 enable_unit_tests
 with_ust
 with_ust_include
@@ -1415,6 +1421,11 @@ Optional Packages:
   --with-libipt-prefix[=DIR]  search for libipt in DIR/include and DIR/lib
   --without-libipt-prefix     don't search for libipt in includedir and libdir
   --with-libipt-type=TYPE     type of library to search for (auto/static/shared)
+  --with-arm-cs           include ARM CoreSight Processor Trace support
+                          (auto/yes/no)
+  --with-libopencsd_c_api-prefix[=DIR]  search for libopencsd_c_api in DIR/include and DIR/lib
+  --without-libopencsd_c_api-prefix     don't search for libopencsd_c_api in includedir and libdir
+  --with-libopencsd_c_api-type=TYPE     type of library to search for (auto/static/shared)
   --with-ust=PATH       Specify prefix directory for the installed UST package
                           Equivalent to --with-ust-include=PATH/include
                           plus --with-ust-lib=PATH/lib
@@ -8549,6 +8560,540 @@ fi
     fi
   fi
 
+  # ARM CoreSight trace       #
+
+
+# Check whether --with-arm_cs was given.
+if test "${with_arm_cs+set}" = set; then :
+  withval=$with_arm_cs;
+else
+  with_arm_cs=auto
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use ARM CoreSight Processor Trace " >&5
+$as_echo_n "checking whether to use ARM CoreSight Processor Trace ... " >&6; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_arm_cs" >&5
+$as_echo "$with_arm_cs" >&6; }
+
+  if test "${with_arm_cs}" = no; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&5
+$as_echo "$as_me: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&2;}
+    HAVE_LIBOPENCSD=no
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+  #include <linux/perf_event.h>
+  #ifndef PERF_ATTR_SIZE_VER5
+  # error
+  #endif
+
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  perf_event=yes
+else
+  perf_event=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+    if test "$perf_event" != yes; then
+      if test "$with_arm_cs" = yes; then
+        as_fn_error $? "linux/perf_event.h missing or too old" "$LINENO" 5
+      else
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&5
+$as_echo "$as_me: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&2;}
+      fi
+    fi
+
+
+
+
+
+
+
+
+
+    use_additional=yes
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+
+    eval additional_includedir=\"$includedir\"
+    eval additional_libdir=\"$libdir\"
+
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+
+# Check whether --with-libopencsd_c_api-prefix was given.
+if test "${with_libopencsd_c_api_prefix+set}" = set; then :
+  withval=$with_libopencsd_c_api_prefix;
+    if test "X$withval" = "Xno"; then
+      use_additional=no
+    else
+      if test "X$withval" = "X"; then
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+
+          eval additional_includedir=\"$includedir\"
+          eval additional_libdir=\"$libdir\"
+
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+      else
+        additional_includedir="$withval/include"
+        additional_libdir="$withval/lib"
+      fi
+    fi
+
+fi
+
+
+# Check whether --with-libopencsd_c_api-type was given.
+if test "${with_libopencsd_c_api_type+set}" = set; then :
+  withval=$with_libopencsd_c_api_type;  with_libopencsd_c_api_type=$withval
+else
+   with_libopencsd_c_api_type=auto
+fi
+
+  lib_type=`eval echo \$with_libopencsd_c_api_type`
+
+      LIBOPENCSD_C_API=
+  LTLIBOPENCSD_C_API=
+  INCOPENCSD_C_API=
+  rpathdirs=
+  ltrpathdirs=
+  names_already_handled=
+  names_next_round='opencsd_c_api '
+  while test -n "$names_next_round"; do
+    names_this_round="$names_next_round"
+    names_next_round=
+    for name in $names_this_round; do
+      already_handled=
+      for n in $names_already_handled; do
+        if test "$n" = "$name"; then
+          already_handled=yes
+          break
+        fi
+      done
+      if test -z "$already_handled"; then
+        names_already_handled="$names_already_handled $name"
+                        uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+        eval value=\"\$HAVE_LIB$uppername\"
+        if test -n "$value"; then
+          if test "$value" = yes; then
+            eval value=\"\$LIB$uppername\"
+            test -z "$value" || LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$value"
+            eval value=\"\$LTLIB$uppername\"
+            test -z "$value" || LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$value"
+          else
+                                    :
+          fi
+        else
+                              found_dir=
+          found_la=
+          found_so=
+          found_a=
+          if test $use_additional = yes; then
+            if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+              found_dir="$additional_libdir"
+              found_so="$additional_libdir/lib$name.$shlibext"
+              if test -f "$additional_libdir/lib$name.la"; then
+                found_la="$additional_libdir/lib$name.la"
+              fi
+            elif test x$lib_type != xshared; then
+              if test -f "$additional_libdir/lib$name.$libext"; then
+                found_dir="$additional_libdir"
+                found_a="$additional_libdir/lib$name.$libext"
+                if test -f "$additional_libdir/lib$name.la"; then
+                  found_la="$additional_libdir/lib$name.la"
+                fi
+              fi
+            fi
+          fi
+          if test "X$found_dir" = "X"; then
+            for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+              case "$x" in
+                -L*)
+                  dir=`echo "X$x" | sed -e 's/^X-L//'`
+                  if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+                    found_dir="$dir"
+                    found_so="$dir/lib$name.$shlibext"
+                    if test -f "$dir/lib$name.la"; then
+                      found_la="$dir/lib$name.la"
+                    fi
+                  elif test x$lib_type != xshared; then
+                    if test -f "$dir/lib$name.$libext"; then
+                      found_dir="$dir"
+                      found_a="$dir/lib$name.$libext"
+                      if test -f "$dir/lib$name.la"; then
+                        found_la="$dir/lib$name.la"
+                      fi
+                    fi
+                  fi
+                  ;;
+              esac
+              if test "X$found_dir" != "X"; then
+                break
+              fi
+            done
+          fi
+          if test "X$found_dir" != "X"; then
+                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$found_dir -l$name"
+            if test "X$found_so" != "X"; then
+                                                        if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
+                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+              else
+                                                                                haveit=
+                for x in $ltrpathdirs; do
+                  if test "X$x" = "X$found_dir"; then
+                    haveit=yes
+                    break
+                  fi
+                done
+                if test -z "$haveit"; then
+                  ltrpathdirs="$ltrpathdirs $found_dir"
+                fi
+                                if test "$hardcode_direct" = yes; then
+                                                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+                else
+                  if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+                                                            LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+                                                            haveit=
+                    for x in $rpathdirs; do
+                      if test "X$x" = "X$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      rpathdirs="$rpathdirs $found_dir"
+                    fi
+                  else
+                                                                                haveit=
+                    for x in $LDFLAGS $LIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                      if test "X$x" = "X-L$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir"
+                    fi
+                    if test "$hardcode_minus_L" != no; then
+                                                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+                    else
+                                                                                                                                                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
+                    fi
+                  fi
+                fi
+              fi
+            else
+              if test "X$found_a" != "X"; then
+                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_a"
+              else
+                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir -l$name"
+              fi
+            fi
+                        additional_includedir=
+            case "$found_dir" in
+              */lib | */lib/)
+                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
+                additional_includedir="$basedir/include"
+                ;;
+            esac
+            if test "X$additional_includedir" != "X"; then
+                                                                                                                if test "X$additional_includedir" != "X/usr/include"; then
+                haveit=
+                if test "X$additional_includedir" = "X/usr/local/include"; then
+                  if test -n "$GCC"; then
+                    case $host_os in
+                      linux*) haveit=yes;;
+                    esac
+                  fi
+                fi
+                if test -z "$haveit"; then
+                  for x in $CPPFLAGS $INCOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                    if test "X$x" = "X-I$additional_includedir"; then
+                      haveit=yes
+                      break
+                    fi
+                  done
+                  if test -z "$haveit"; then
+                    if test -d "$additional_includedir"; then
+                                            INCOPENCSD_C_API="${INCOPENCSD_C_API}${INCOPENCSD_C_API:+ }-I$additional_includedir"
+                    fi
+                  fi
+                fi
+              fi
+            fi
+                        if test -n "$found_la"; then
+                                                        save_libdir="$libdir"
+              case "$found_la" in
+                */* | *\\*) . "$found_la" ;;
+                *) . "./$found_la" ;;
+              esac
+              libdir="$save_libdir"
+                            for dep in $dependency_libs; do
+                case "$dep" in
+                  -L*)
+                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+                                                                                                                                                                if test "X$additional_libdir" != "X/usr/lib"; then
+                      haveit=
+                      if test "X$additional_libdir" = "X/usr/local/lib"; then
+                        if test -n "$GCC"; then
+                          case $host_os in
+                            linux*) haveit=yes;;
+                          esac
+                        fi
+                      fi
+                      if test -z "$haveit"; then
+                        haveit=
+                        for x in $LDFLAGS $LIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$additional_libdir"
+                          fi
+                        fi
+                        haveit=
+                        for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                                                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$additional_libdir"
+                          fi
+                        fi
+                      fi
+                    fi
+                    ;;
+                  -R*)
+                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
+                    if test "$enable_rpath" != no; then
+                                                                  haveit=
+                      for x in $rpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        rpathdirs="$rpathdirs $dir"
+                      fi
+                                                                  haveit=
+                      for x in $ltrpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        ltrpathdirs="$ltrpathdirs $dir"
+                      fi
+                    fi
+                    ;;
+                  -l*)
+                                        names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+                    ;;
+                  *.la)
+                                                                                names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+                    ;;
+                  *)
+                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$dep"
+                    LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$dep"
+                    ;;
+                esac
+              done
+            fi
+          else
+                                                            if test "x$lib_type" = "xauto" || test "x$lib_type" = "xshared"; then
+              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
+              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l$name"
+            else
+              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l:lib$name.$libext"
+              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l:lib$name.$libext"
+            fi
+          fi
+        fi
+      fi
+    done
+  done
+  if test "X$rpathdirs" != "X"; then
+    if test -n "$hardcode_libdir_separator"; then
+                        alldirs=
+      for found_dir in $rpathdirs; do
+        alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+      done
+            acl_save_libdir="$libdir"
+      libdir="$alldirs"
+      eval flag=\"$hardcode_libdir_flag_spec\"
+      libdir="$acl_save_libdir"
+      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
+    else
+            for found_dir in $rpathdirs; do
+        acl_save_libdir="$libdir"
+        libdir="$found_dir"
+        eval flag=\"$hardcode_libdir_flag_spec\"
+        libdir="$acl_save_libdir"
+        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
+      done
+    fi
+  fi
+  if test "X$ltrpathdirs" != "X"; then
+            for found_dir in $ltrpathdirs; do
+      LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-R$found_dir"
+    done
+  fi
+
+
+        ac_save_CPPFLAGS="$CPPFLAGS"
+
+  for element in $INCOPENCSD_C_API; do
+    haveit=
+    for x in $CPPFLAGS; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+      if test "X$x" = "X$element"; then
+        haveit=yes
+        break
+      fi
+    done
+    if test -z "$haveit"; then
+      CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+    fi
+  done
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libopencsd_c_api" >&5
+$as_echo_n "checking for libopencsd_c_api... " >&6; }
+if ${ac_cv_libopencsd_c_api+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    ac_save_LIBS="$LIBS"
+    LIBS="$LIBS $LIBOPENCSD_C_API"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include "opencsd/c_api/opencsd_c_api.h"
+int
+main ()
+{
+ocsd_get_version();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_libopencsd_c_api=yes
+else
+  ac_cv_libopencsd_c_api=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+    LIBS="$ac_save_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libopencsd_c_api" >&5
+$as_echo "$ac_cv_libopencsd_c_api" >&6; }
+  if test "$ac_cv_libopencsd_c_api" = yes; then
+    HAVE_LIBOPENCSD_C_API=yes
+
+$as_echo "#define HAVE_LIBOPENCSD_C_API 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libopencsd_c_api" >&5
+$as_echo_n "checking how to link with libopencsd_c_api... " >&6; }
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBOPENCSD_C_API" >&5
+$as_echo "$LIBOPENCSD_C_API" >&6; }
+  else
+    HAVE_LIBOPENCSD_C_API=no
+            CPPFLAGS="$ac_save_CPPFLAGS"
+    LIBOPENCSD_C_API=
+    LTLIBOPENCSD_C_API=
+  fi
+
+
+
+
+
+
+    if test "$HAVE_LIBOPENCSD_C_API" != yes; then
+      if test "$with_arm_cs" = yes; then
+        as_fn_error $? "libopencsd_c_api is missing or unusable" "$LINENO" 5
+      else
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&5
+$as_echo "$as_me: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&2;}
+      fi
+    else
+      LIBS="$LIBS $LIBOPENCSD_C_API"
+    fi
+  fi
+
+
 
 $as_echo "#define _STRUCTURED_PROC 1" >>confdefs.h
 
diff --git a/gdbsupport/Makefile.in b/gdbsupport/Makefile.in
index d7f2d4914b0..cdb7ba2f0ac 100644
--- a/gdbsupport/Makefile.in
+++ b/gdbsupport/Makefile.in
@@ -250,6 +250,7 @@ GMSGFMT = @GMSGFMT@
 GREP = @GREP@
 HAVE_CXX11 = @HAVE_CXX11@
 HAVE_LIBIPT = @HAVE_LIBIPT@
+HAVE_LIBOPENCSD_C_API = @HAVE_LIBOPENCSD_C_API@
 INCINTL = @INCINTL@
 INSTALL = @INSTALL@
 INSTALL_DATA = @INSTALL_DATA@
@@ -263,9 +264,11 @@ LIBINTL = @LIBINTL@
 LIBINTL_DEP = @LIBINTL_DEP@
 LIBIPT = @LIBIPT@
 LIBOBJS = @LIBOBJS@
+LIBOPENCSD_C_API = @LIBOPENCSD_C_API@
 LIBS = @LIBS@
 LTLIBIPT = @LTLIBIPT@
 LTLIBOBJS = @LTLIBOBJS@
+LTLIBOPENCSD_C_API = @LTLIBOPENCSD_C_API@
 MAINT = @MAINT@
 MAKEINFO = @MAKEINFO@
 MKDIR_P = @MKDIR_P@
diff --git a/gdbsupport/common.m4 b/gdbsupport/common.m4
index 2e709dbbdbb..856fb9bc31b 100644
--- a/gdbsupport/common.m4
+++ b/gdbsupport/common.m4
@@ -163,6 +163,45 @@ AC_DEFUN([GDB_AC_COMMON], [
     fi
   fi
 
+  # ARM CoreSight trace       #
+
+  AC_ARG_WITH(arm_cs,
+    AS_HELP_STRING([--with-arm-cs], [include ARM CoreSight Processor Trace support (auto/yes/no)]),
+    [], [with_arm_cs=auto])
+  AC_MSG_CHECKING([whether to use ARM CoreSight Processor Trace ])
+  AC_MSG_RESULT([$with_arm_cs])
+
+  if test "${with_arm_cs}" = no; then
+    AC_MSG_WARN([ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable.])
+    HAVE_LIBOPENCSD=no
+  else
+    AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
+  #include <linux/perf_event.h>
+  #ifndef PERF_ATTR_SIZE_VER5
+  # error
+  #endif
+    ]])], [perf_event=yes], [perf_event=no])
+    if test "$perf_event" != yes; then
+      if test "$with_arm_cs" = yes; then
+        AC_MSG_ERROR([linux/perf_event.h missing or too old])
+      else
+        AC_MSG_WARN([linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable.])
+      fi
+    fi
+
+    AC_LIB_HAVE_LINKFLAGS([opencsd_c_api], [], [#include "opencsd/c_api/opencsd_c_api.h"], [ocsd_get_version();])
+    if test "$HAVE_LIBOPENCSD_C_API" != yes; then
+      if test "$with_arm_cs" = yes; then
+        AC_MSG_ERROR([libopencsd_c_api is missing or unusable])
+      else
+        AC_MSG_WARN([libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable.])
+      fi
+    else
+      LIBS="$LIBS $LIBOPENCSD_C_API"
+    fi
+  fi
+
+
   BFD_SYS_PROCFS_H
   if test "$ac_cv_header_sys_procfs_h" = yes; then
     BFD_HAVE_SYS_PROCFS_TYPE(gregset_t)
diff --git a/gdbsupport/config.in b/gdbsupport/config.in
index c44a2a1e5de..2259ada7fdb 100644
--- a/gdbsupport/config.in
+++ b/gdbsupport/config.in
@@ -117,6 +117,9 @@
 /* Define if you have the ipt library. */
 #undef HAVE_LIBIPT
 
+/* Define if you have the opencsd_c_api library. */
+#undef HAVE_LIBOPENCSD_C_API
+
 /* Define to 1 if you have the <linux/elf.h> header file. */
 #undef HAVE_LINUX_ELF_H
 
diff --git a/gdbsupport/configure b/gdbsupport/configure
index 60643c80b5a..08bc0e6a86d 100755
--- a/gdbsupport/configure
+++ b/gdbsupport/configure
@@ -628,6 +628,9 @@ WERROR_CFLAGS
 WARN_CFLAGS
 SELFTEST_FALSE
 SELFTEST_TRUE
+LTLIBOPENCSD_C_API
+LIBOPENCSD_C_API
+HAVE_LIBOPENCSD_C_API
 LTLIBIPT
 LIBIPT
 HAVE_LIBIPT
@@ -772,6 +775,9 @@ with_gnu_ld
 enable_rpath
 with_libipt_prefix
 with_libipt_type
+with_arm_cs
+with_libopencsd_c_api_prefix
+with_libopencsd_c_api_type
 enable_unit_tests
 enable_werror
 enable_build_warnings
@@ -1436,6 +1442,11 @@ Optional Packages:
   --with-libipt-prefix[=DIR]  search for libipt in DIR/include and DIR/lib
   --without-libipt-prefix     don't search for libipt in includedir and libdir
   --with-libipt-type=TYPE     type of library to search for (auto/static/shared)
+  --with-arm-cs           include ARM CoreSight Processor Trace support
+                          (auto/yes/no)
+  --with-libopencsd_c_api-prefix[=DIR]  search for libopencsd_c_api in DIR/include and DIR/lib
+  --without-libopencsd_c_api-prefix     don't search for libopencsd_c_api in includedir and libdir
+  --with-libopencsd_c_api-type=TYPE     type of library to search for (auto/static/shared)
 
 Some influential environment variables:
   CC          C compiler command
@@ -9563,6 +9574,540 @@ fi
     fi
   fi
 
+  # ARM CoreSight trace       #
+
+
+# Check whether --with-arm_cs was given.
+if test "${with_arm_cs+set}" = set; then :
+  withval=$with_arm_cs;
+else
+  with_arm_cs=auto
+fi
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use ARM CoreSight Processor Trace " >&5
+$as_echo_n "checking whether to use ARM CoreSight Processor Trace ... " >&6; }
+  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_arm_cs" >&5
+$as_echo "$with_arm_cs" >&6; }
+
+  if test "${with_arm_cs}" = no; then
+    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&5
+$as_echo "$as_me: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&2;}
+    HAVE_LIBOPENCSD=no
+  else
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+  #include <linux/perf_event.h>
+  #ifndef PERF_ATTR_SIZE_VER5
+  # error
+  #endif
+
+_ACEOF
+if ac_fn_c_try_cpp "$LINENO"; then :
+  perf_event=yes
+else
+  perf_event=no
+fi
+rm -f conftest.err conftest.i conftest.$ac_ext
+    if test "$perf_event" != yes; then
+      if test "$with_arm_cs" = yes; then
+        as_fn_error $? "linux/perf_event.h missing or too old" "$LINENO" 5
+      else
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&5
+$as_echo "$as_me: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&2;}
+      fi
+    fi
+
+
+
+
+
+
+
+
+
+    use_additional=yes
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+
+    eval additional_includedir=\"$includedir\"
+    eval additional_libdir=\"$libdir\"
+
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+
+# Check whether --with-libopencsd_c_api-prefix was given.
+if test "${with_libopencsd_c_api_prefix+set}" = set; then :
+  withval=$with_libopencsd_c_api_prefix;
+    if test "X$withval" = "Xno"; then
+      use_additional=no
+    else
+      if test "X$withval" = "X"; then
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+
+          eval additional_includedir=\"$includedir\"
+          eval additional_libdir=\"$libdir\"
+
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+      else
+        additional_includedir="$withval/include"
+        additional_libdir="$withval/lib"
+      fi
+    fi
+
+fi
+
+
+# Check whether --with-libopencsd_c_api-type was given.
+if test "${with_libopencsd_c_api_type+set}" = set; then :
+  withval=$with_libopencsd_c_api_type;  with_libopencsd_c_api_type=$withval
+else
+   with_libopencsd_c_api_type=auto
+fi
+
+  lib_type=`eval echo \$with_libopencsd_c_api_type`
+
+      LIBOPENCSD_C_API=
+  LTLIBOPENCSD_C_API=
+  INCOPENCSD_C_API=
+  rpathdirs=
+  ltrpathdirs=
+  names_already_handled=
+  names_next_round='opencsd_c_api '
+  while test -n "$names_next_round"; do
+    names_this_round="$names_next_round"
+    names_next_round=
+    for name in $names_this_round; do
+      already_handled=
+      for n in $names_already_handled; do
+        if test "$n" = "$name"; then
+          already_handled=yes
+          break
+        fi
+      done
+      if test -z "$already_handled"; then
+        names_already_handled="$names_already_handled $name"
+                        uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
+        eval value=\"\$HAVE_LIB$uppername\"
+        if test -n "$value"; then
+          if test "$value" = yes; then
+            eval value=\"\$LIB$uppername\"
+            test -z "$value" || LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$value"
+            eval value=\"\$LTLIB$uppername\"
+            test -z "$value" || LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$value"
+          else
+                                    :
+          fi
+        else
+                              found_dir=
+          found_la=
+          found_so=
+          found_a=
+          if test $use_additional = yes; then
+            if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+              found_dir="$additional_libdir"
+              found_so="$additional_libdir/lib$name.$shlibext"
+              if test -f "$additional_libdir/lib$name.la"; then
+                found_la="$additional_libdir/lib$name.la"
+              fi
+            elif test x$lib_type != xshared; then
+              if test -f "$additional_libdir/lib$name.$libext"; then
+                found_dir="$additional_libdir"
+                found_a="$additional_libdir/lib$name.$libext"
+                if test -f "$additional_libdir/lib$name.la"; then
+                  found_la="$additional_libdir/lib$name.la"
+                fi
+              fi
+            fi
+          fi
+          if test "X$found_dir" = "X"; then
+            for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+              case "$x" in
+                -L*)
+                  dir=`echo "X$x" | sed -e 's/^X-L//'`
+                  if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext" && test x$lib_type != xstatic; then
+                    found_dir="$dir"
+                    found_so="$dir/lib$name.$shlibext"
+                    if test -f "$dir/lib$name.la"; then
+                      found_la="$dir/lib$name.la"
+                    fi
+                  elif test x$lib_type != xshared; then
+                    if test -f "$dir/lib$name.$libext"; then
+                      found_dir="$dir"
+                      found_a="$dir/lib$name.$libext"
+                      if test -f "$dir/lib$name.la"; then
+                        found_la="$dir/lib$name.la"
+                      fi
+                    fi
+                  fi
+                  ;;
+              esac
+              if test "X$found_dir" != "X"; then
+                break
+              fi
+            done
+          fi
+          if test "X$found_dir" != "X"; then
+                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$found_dir -l$name"
+            if test "X$found_so" != "X"; then
+                                                        if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
+                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+              else
+                                                                                haveit=
+                for x in $ltrpathdirs; do
+                  if test "X$x" = "X$found_dir"; then
+                    haveit=yes
+                    break
+                  fi
+                done
+                if test -z "$haveit"; then
+                  ltrpathdirs="$ltrpathdirs $found_dir"
+                fi
+                                if test "$hardcode_direct" = yes; then
+                                                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+                else
+                  if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
+                                                            LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+                                                            haveit=
+                    for x in $rpathdirs; do
+                      if test "X$x" = "X$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      rpathdirs="$rpathdirs $found_dir"
+                    fi
+                  else
+                                                                                haveit=
+                    for x in $LDFLAGS $LIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                      if test "X$x" = "X-L$found_dir"; then
+                        haveit=yes
+                        break
+                      fi
+                    done
+                    if test -z "$haveit"; then
+                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir"
+                    fi
+                    if test "$hardcode_minus_L" != no; then
+                                                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
+                    else
+                                                                                                                                                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
+                    fi
+                  fi
+                fi
+              fi
+            else
+              if test "X$found_a" != "X"; then
+                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_a"
+              else
+                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir -l$name"
+              fi
+            fi
+                        additional_includedir=
+            case "$found_dir" in
+              */lib | */lib/)
+                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
+                additional_includedir="$basedir/include"
+                ;;
+            esac
+            if test "X$additional_includedir" != "X"; then
+                                                                                                                if test "X$additional_includedir" != "X/usr/include"; then
+                haveit=
+                if test "X$additional_includedir" = "X/usr/local/include"; then
+                  if test -n "$GCC"; then
+                    case $host_os in
+                      linux*) haveit=yes;;
+                    esac
+                  fi
+                fi
+                if test -z "$haveit"; then
+                  for x in $CPPFLAGS $INCOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                    if test "X$x" = "X-I$additional_includedir"; then
+                      haveit=yes
+                      break
+                    fi
+                  done
+                  if test -z "$haveit"; then
+                    if test -d "$additional_includedir"; then
+                                            INCOPENCSD_C_API="${INCOPENCSD_C_API}${INCOPENCSD_C_API:+ }-I$additional_includedir"
+                    fi
+                  fi
+                fi
+              fi
+            fi
+                        if test -n "$found_la"; then
+                                                        save_libdir="$libdir"
+              case "$found_la" in
+                */* | *\\*) . "$found_la" ;;
+                *) . "./$found_la" ;;
+              esac
+              libdir="$save_libdir"
+                            for dep in $dependency_libs; do
+                case "$dep" in
+                  -L*)
+                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
+                                                                                                                                                                if test "X$additional_libdir" != "X/usr/lib"; then
+                      haveit=
+                      if test "X$additional_libdir" = "X/usr/local/lib"; then
+                        if test -n "$GCC"; then
+                          case $host_os in
+                            linux*) haveit=yes;;
+                          esac
+                        fi
+                      fi
+                      if test -z "$haveit"; then
+                        haveit=
+                        for x in $LDFLAGS $LIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$additional_libdir"
+                          fi
+                        fi
+                        haveit=
+                        for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+                          if test "X$x" = "X-L$additional_libdir"; then
+                            haveit=yes
+                            break
+                          fi
+                        done
+                        if test -z "$haveit"; then
+                          if test -d "$additional_libdir"; then
+                                                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$additional_libdir"
+                          fi
+                        fi
+                      fi
+                    fi
+                    ;;
+                  -R*)
+                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
+                    if test "$enable_rpath" != no; then
+                                                                  haveit=
+                      for x in $rpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        rpathdirs="$rpathdirs $dir"
+                      fi
+                                                                  haveit=
+                      for x in $ltrpathdirs; do
+                        if test "X$x" = "X$dir"; then
+                          haveit=yes
+                          break
+                        fi
+                      done
+                      if test -z "$haveit"; then
+                        ltrpathdirs="$ltrpathdirs $dir"
+                      fi
+                    fi
+                    ;;
+                  -l*)
+                                        names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
+                    ;;
+                  *.la)
+                                                                                names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
+                    ;;
+                  *)
+                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$dep"
+                    LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$dep"
+                    ;;
+                esac
+              done
+            fi
+          else
+                                                            if test "x$lib_type" = "xauto" || test "x$lib_type" = "xshared"; then
+              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
+              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l$name"
+            else
+              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l:lib$name.$libext"
+              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l:lib$name.$libext"
+            fi
+          fi
+        fi
+      fi
+    done
+  done
+  if test "X$rpathdirs" != "X"; then
+    if test -n "$hardcode_libdir_separator"; then
+                        alldirs=
+      for found_dir in $rpathdirs; do
+        alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
+      done
+            acl_save_libdir="$libdir"
+      libdir="$alldirs"
+      eval flag=\"$hardcode_libdir_flag_spec\"
+      libdir="$acl_save_libdir"
+      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
+    else
+            for found_dir in $rpathdirs; do
+        acl_save_libdir="$libdir"
+        libdir="$found_dir"
+        eval flag=\"$hardcode_libdir_flag_spec\"
+        libdir="$acl_save_libdir"
+        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
+      done
+    fi
+  fi
+  if test "X$ltrpathdirs" != "X"; then
+            for found_dir in $ltrpathdirs; do
+      LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-R$found_dir"
+    done
+  fi
+
+
+        ac_save_CPPFLAGS="$CPPFLAGS"
+
+  for element in $INCOPENCSD_C_API; do
+    haveit=
+    for x in $CPPFLAGS; do
+
+  acl_save_prefix="$prefix"
+  prefix="$acl_final_prefix"
+  acl_save_exec_prefix="$exec_prefix"
+  exec_prefix="$acl_final_exec_prefix"
+  eval x=\"$x\"
+  exec_prefix="$acl_save_exec_prefix"
+  prefix="$acl_save_prefix"
+
+      if test "X$x" = "X$element"; then
+        haveit=yes
+        break
+      fi
+    done
+    if test -z "$haveit"; then
+      CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
+    fi
+  done
+
+
+  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libopencsd_c_api" >&5
+$as_echo_n "checking for libopencsd_c_api... " >&6; }
+if ${ac_cv_libopencsd_c_api+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+
+    ac_save_LIBS="$LIBS"
+    LIBS="$LIBS $LIBOPENCSD_C_API"
+    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+#include "opencsd/c_api/opencsd_c_api.h"
+int
+main ()
+{
+ocsd_get_version();
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_link "$LINENO"; then :
+  ac_cv_libopencsd_c_api=yes
+else
+  ac_cv_libopencsd_c_api=no
+fi
+rm -f core conftest.err conftest.$ac_objext \
+    conftest$ac_exeext conftest.$ac_ext
+    LIBS="$ac_save_LIBS"
+
+fi
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libopencsd_c_api" >&5
+$as_echo "$ac_cv_libopencsd_c_api" >&6; }
+  if test "$ac_cv_libopencsd_c_api" = yes; then
+    HAVE_LIBOPENCSD_C_API=yes
+
+$as_echo "#define HAVE_LIBOPENCSD_C_API 1" >>confdefs.h
+
+    { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libopencsd_c_api" >&5
+$as_echo_n "checking how to link with libopencsd_c_api... " >&6; }
+    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBOPENCSD_C_API" >&5
+$as_echo "$LIBOPENCSD_C_API" >&6; }
+  else
+    HAVE_LIBOPENCSD_C_API=no
+            CPPFLAGS="$ac_save_CPPFLAGS"
+    LIBOPENCSD_C_API=
+    LTLIBOPENCSD_C_API=
+  fi
+
+
+
+
+
+
+    if test "$HAVE_LIBOPENCSD_C_API" != yes; then
+      if test "$with_arm_cs" = yes; then
+        as_fn_error $? "libopencsd_c_api is missing or unusable" "$LINENO" 5
+      else
+        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&5
+$as_echo "$as_me: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&2;}
+      fi
+    else
+      LIBS="$LIBS $LIBOPENCSD_C_API"
+    fi
+  fi
+
+
 
 $as_echo "#define _STRUCTURED_PROC 1" >>confdefs.h
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v6 2/7] add btrace coresight related commands
  2021-05-31 21:33 [PATCH v6 0/7] extend branch tracing to use ARM CoreSight traces Zied Guermazi
  2021-05-31 21:33 ` [PATCH v6 1/7] configure gdb build system for supporting btrace on arm processors Zied Guermazi
@ 2021-05-31 21:33 ` Zied Guermazi
  2021-06-01 12:07   ` Eli Zaretskii
  2021-06-30 12:26   ` Luis Machado
  2021-05-31 21:33 ` [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant Zied Guermazi
                   ` (4 subsequent siblings)
  6 siblings, 2 replies; 35+ messages in thread
From: Zied Guermazi @ 2021-05-31 21:33 UTC (permalink / raw)
  To: gdb-patches, markus.t.metzger; +Cc: Zied Guermazi

This patch extends the commands needed for using branch tracing
with ARM CoreSight traces.
Those commands are:
set record btrace etm sink
set record btrace etm buffer-size
record btrace etm

gdb/ChangeLog

	* NEWS: list new commands for extending btrace
	to support using ARM CoreSight Traces.
	* record-btrace.c (record_btrace_print_etm_conf): New.
	(record_btrace_print_conf): handle BTRACE_FORMAT_ETM.
	(cmd_record_btrace_etm_start): New.
	(cmd_record_btrace_start): handle starting ETM tracing.
	(cmd_show_record_btrace_cpu): extend for ARM cpus.
	(show_record_etm_buffer_size_value): New.
	(_initialize_record_btrace): add commands for ETM traces.
	(record_start): add starting ETM traces.

gdb/doc/ChangeLog

	* gdb.texinfo (Process Record and Replay): Document extending
	GDB btrace commands to support using ARM CoreSight traces.

gdbsupport/ChangeLog

	* btrace-common.h (btrace_format): add BTRACE_FORMAT_ETM
	to the enum.
	(btrace_config_etm): new struct.
	(btrace_config): add btrace_config_etm etm.
	* btrace-common.cc (btrace_format_string): add BTRACE_FORMAT_ETM.
	(btrace_format_short_string): add BTRACE_FORMAT_ETM.
---
 gdb/NEWS                    |  16 +++++
 gdb/doc/gdb.texinfo         |  51 +++++++++++++++-
 gdb/record-btrace.c         | 117 +++++++++++++++++++++++++++++++++++-
 gdb/record.c                |   2 +
 gdbsupport/btrace-common.cc |   6 ++
 gdbsupport/btrace-common.h  |  22 ++++++-
 6 files changed, 210 insertions(+), 4 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index ab678acec8b..02035901f6e 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -2,6 +2,7 @@
 	     (Organized release by release)
 
 *** Changes since GDB 10
+* Record btrace now  supports using ARM CoreSight ETM traces.
 
 * GDB now supports general memory tagging functionality if the underlying
   architecture supports the proper primitives and hooks.  Currently this is
@@ -77,6 +78,21 @@
 
 * New commands
 
+record btrace etm
+record etm
+  Start branch trace recording using ARM CoreSight trace format (ETM).
+
+set|show record btrace etm buffer-size
+  Set and show the size of the ring buffer used for branch tracing in
+  ETM format.
+  The obtained size may differ from the requested size.  Use "info
+  record" to see the obtained buffer size.
+
+set|show record btrace etm sink
+  Set and show the trace sink used for branch tracing in
+  ETM format.
+  Use "default" to reset it to default sink.
+
 set debug event-loop
 show debug event-loop
   Control the display of debug output about GDB's event loop.
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 90d827a50e7..b9b09ff97f2 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -7479,15 +7479,19 @@ For architecture environments that support process record and replay,
 @kindex record btrace
 @kindex record btrace bts
 @kindex record btrace pt
+@kindex record btrace etm
 @kindex record bts
 @kindex record pt
+@kindex record etm
 @kindex rec
 @kindex rec full
 @kindex rec btrace
 @kindex rec btrace bts
 @kindex rec btrace pt
+@kindex rec btrace etm
 @kindex rec bts
 @kindex rec pt
+@kindex rec etm
 @item record @var{method}
 This command starts the process record and replay target.  The
 recording method can be specified as parameter.  Without a parameter
@@ -7501,7 +7505,7 @@ replay implementation.  This method allows replaying and reverse
 execution.
 
 @item btrace @var{format}
-Hardware-supported instruction recording, supported on Intel
+Hardware-supported instruction recording, supported on Intel and ARM
 processors.  This method does not record data.  Further, the data is
 collected in a ring buffer so old data will be overwritten when the
 buffer is full.  It allows limited reverse execution.  Variables and
@@ -7535,6 +7539,13 @@ Decoding the recorded execution trace, on the other hand, is more
 expensive than decoding @acronym{BTS} trace.  This is mostly due to the
 increased number of instructions to process.  You should increase the
 buffer-size with care.
+
+@item etm
+@cindex ARM CoreSight Trace
+Use the @dfn{ARM CoreSight Trace} recording format.  In this
+format, @acronym{ETM, Extended Trace Macrocell} stores the processor
+execution trace in a compressed form that is afterwards
+decoded by @value{GDBN}.
 @end table
 
 Not all recording formats may be available on all processors.
@@ -7682,6 +7693,12 @@ and to read-write memory.  Beware that the accessed memory corresponds
 to the live target and not necessarily to the current replay
 position.
 
+@item set record btrace etm sink @var{sink}
+Set ARM CoreSight ETM sink to collect traces.
+On @sc{gnu}/Linux systems, possible values for @var{sink} are the name of the
+files in the directory @file{/sys/bus/event_source/devices/cs_etm/sinks/}.
+Use the value @kbd{default} to reset it to default sink.
+
 @item set record btrace cpu @var{identifier}
 Set the processor to be used for enabling workarounds for processor
 errata when decoding the trace.
@@ -7745,6 +7762,9 @@ Recorded 84872 instructions in 3189 functions (0 gaps) for thread 1 (...).
 @item show record btrace replay-memory-access
 Show the current setting of @code{replay-memory-access}.
 
+@item show record btrace etm sink
+Show ARM CoreSight ETM sink.
+
 @item show record btrace cpu
 Show the processor to be used for enabling trace decode errata
 workarounds.
@@ -7796,6 +7816,29 @@ also need longer to process the branch trace data before it can be used.
 Show the current setting of the requested ring buffer size for branch
 tracing in Intel Processor Trace format.
 
+@kindex set record btrace etm
+@item set record btrace etm buffer-size @var{size}
+@itemx set record btrace etm buffer-size unlimited
+Set the requested ring buffer size for branch tracing in ARM
+CoreSight ETM Trace format.  Default is 16KB.
+
+If @var{size} is a positive number, then @value{GDBN} will try to
+allocate a buffer of at least @var{size} bytes for each new thread
+that uses the btrace recording method and the ARM CoreSight ETM
+format.  The actually obtained buffer size may differ from the
+requested @var{size}.  Use the @code{info record} command to see the
+actual buffer size for each thread.
+
+If @var{limit} is @code{unlimited} or zero, @value{GDBN} will try to
+allocate a buffer of 4MB.
+
+Bigger buffers mean longer traces.  On the other hand, @value{GDBN} will
+also need longer to process the branch trace data before it can be used.
+
+@item show record btrace etm buffer-size @var{size}
+Show the current setting of the requested ring buffer size for branch
+tracing in ARM CoreSight ETM Trace format.
+
 @kindex info record
 @item info record
 Show various statistics about the recording depending on the recording
@@ -7847,6 +7890,12 @@ For the @code{pt} recording format, it also shows:
 @item
 Size of the perf ring buffer.
 @end itemize
+
+For the @code{etm} recording format, it also shows:
+@itemize @bullet
+@item
+Size of the perf ring buffer.
+@end itemize
 @end table
 
 @kindex record delete
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 00affb85d22..16ffb76272b 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -201,6 +201,10 @@ static struct cmd_list_element *show_record_btrace_bts_cmdlist;
 static struct cmd_list_element *set_record_btrace_pt_cmdlist;
 static struct cmd_list_element *show_record_btrace_pt_cmdlist;
 
+/* Command lists for "set/show record btrace etm".  */
+static struct cmd_list_element *set_record_btrace_etm_cmdlist;
+static struct cmd_list_element *show_record_btrace_etm_cmdlist;
+
 /* Command list for "set record btrace cpu".  */
 static struct cmd_list_element *set_record_btrace_cpu_cmdlist;
 
@@ -526,6 +530,22 @@ record_btrace_print_pt_conf (const struct btrace_config_pt *conf)
     }
 }
 
+/* Print an ARM Processor Trace configuration.  */
+
+static void
+record_btrace_print_etm_conf (const struct btrace_config_etm *conf)
+{
+  unsigned int size;
+
+  size = conf->size;
+  if (size > 0)
+    {
+      const char *suffix;
+      suffix = record_btrace_adjust_size (&size);
+      printf_unfiltered (_("Buffer size: %u%s.\n"), size, suffix);
+    }
+}
+
 /* Print a branch tracing configuration.  */
 
 static void
@@ -546,6 +566,10 @@ record_btrace_print_conf (const struct btrace_config *conf)
     case BTRACE_FORMAT_PT:
       record_btrace_print_pt_conf (&conf->pt);
       return;
+
+    case BTRACE_FORMAT_ETM:
+      record_btrace_print_etm_conf (&conf->etm);
+      return;
     }
 
   internal_error (__FILE__, __LINE__, _("Unknown branch trace format."));
@@ -2676,7 +2700,7 @@ record_btrace_target::stop (ptid_t ptid)
 	  tp->btrace.flags |= BTHR_STOP;
 	}
     }
- }
+}
 
 /* The can_execute_reverse method of target record-btrace.  */
 
@@ -2937,6 +2961,27 @@ cmd_record_btrace_pt_start (const char *args, int from_tty)
     }
 }
 
+/* Start recording in ARM CoreSight ETM Trace format.  */
+
+static void
+cmd_record_btrace_etm_start (const char *args, int from_tty)
+{
+  if (args != nullptr && *args != 0)
+    error (_("Invalid argument."));
+
+  record_btrace_conf.format = BTRACE_FORMAT_ETM;
+
+  try
+    {
+      execute_command ("target record-btrace", from_tty);
+    }
+  catch (const gdb_exception &exception)
+    {
+      record_btrace_conf.format = BTRACE_FORMAT_NONE;
+      throw;
+    }
+}
+
 /* Alias for "target record".  */
 
 static void
@@ -2951,10 +2996,18 @@ cmd_record_btrace_start (const char *args, int from_tty)
     {
       execute_command ("target record-btrace", from_tty);
     }
-  catch (const gdb_exception &exception)
+  catch (const gdb_exception &exception_pt)
     {
       record_btrace_conf.format = BTRACE_FORMAT_BTS;
 
+      try
+	{
+	  execute_command ("target record-btrace", from_tty);
+	}
+      catch (const gdb_exception &exception_bts)
+	{
+	  record_btrace_conf.format = BTRACE_FORMAT_ETM;
+
 	  try
 	    {
 	      execute_command ("target record-btrace", from_tty);
@@ -2965,6 +3018,7 @@ cmd_record_btrace_start (const char *args, int from_tty)
 	      throw;
 	    }
 	}
+    }
 }
 
 /* The "show record btrace replay-memory-access" command.  */
@@ -3103,6 +3157,17 @@ show_record_pt_buffer_size_value (struct ui_file *file, int from_tty,
 		    value);
 }
 
+/* The "record etm buffer-size" show value function.  */
+
+static void
+show_record_etm_buffer_size_value (struct ui_file *file, int from_tty,
+				   struct cmd_list_element *c,
+				   const char *value)
+{
+  fprintf_filtered (file, _("The record/replay etm buffer size is %s.\n"),
+		    value);
+}
+
 /* Initialize btrace commands.  */
 
 void _initialize_record_btrace ();
@@ -3170,6 +3235,15 @@ When set to \"none\", errata workarounds are disabled."),
 		  1,
 		  &set_record_btrace_cmdlist);
 
+
+  cmd_list_element *record_btrace_etm_cmd
+    = add_cmd ("etm", class_obscure, cmd_record_btrace_etm_start,
+	       _("\
+Start branch trace recording in ARM CoreSight ETM Trace format.\n\n\
+This format may not be available on all processors."),
+	     &record_btrace_cmdlist);
+  add_alias_cmd ("etm", record_btrace_etm_cmd, class_obscure, 1, &record_cmdlist);
+
   add_cmd ("auto", class_support, cmd_set_record_btrace_cpu_auto, _("\
 Automatically determine the cpu to be used for trace decode."),
 	   &set_record_btrace_cpu_cmdlist);
@@ -3231,6 +3305,43 @@ to see the actual buffer size."), NULL, show_record_pt_buffer_size_value,
 			    &set_record_btrace_pt_cmdlist,
 			    &show_record_btrace_pt_cmdlist);
 
+  add_basic_prefix_cmd ("etm", class_support,
+			_("Set record btrace etm options."),
+			&set_record_btrace_etm_cmdlist,
+			0,
+			&set_record_btrace_cmdlist);
+
+  add_show_prefix_cmd ("etm", class_support,
+		       _("Show record btrace etm options."),
+		       &show_record_btrace_etm_cmdlist,
+		       0,
+		       &show_record_btrace_cmdlist);
+
+  add_setshow_uinteger_cmd ("buffer-size", no_class,
+			    &record_btrace_conf.etm.size,
+			    _("Set the record/replay etm buffer size."),
+			    _("Show the record/replay etm buffer size."), _("\
+Bigger buffers allow longer recording but also take more time to process \
+the recorded execution.\n\
+The actual buffer size may differ from the requested size.  Use \"info record\"\
+ to see the actual buffer size."), NULL, show_record_etm_buffer_size_value,
+		  &set_record_btrace_etm_cmdlist,
+		  &show_record_btrace_etm_cmdlist);
+
+  add_setshow_string_cmd ("sink", no_class,
+			  &record_btrace_conf.etm.sink,
+			  _("Set the record/replay ETM sink device."),
+			  _("Show the record/replay ETM sink device."),
+			  _("\
+Sink device is the device that intercepts ETM traces and collects or routes \
+them out of the System on Chip.\n\
+The list of available sinks on linux targets corresponds to the files in \
+the directory \"/sys/bus/event_source/devices/cs_etm/sinks/\".\n\
+value \"default\" reset it to default sink"),
+			  NULL, NULL,
+			  &set_record_btrace_etm_cmdlist,
+			  &show_record_btrace_etm_cmdlist);
+
   add_target (record_btrace_target_info, record_btrace_target_open);
 
   bfcache = htab_create_alloc (50, bfcache_hash, bfcache_eq, NULL,
@@ -3238,4 +3349,6 @@ to see the actual buffer size."), NULL, show_record_pt_buffer_size_value,
 
   record_btrace_conf.bts.size = 64 * 1024;
   record_btrace_conf.pt.size = 16 * 1024;
+#define DEFAULT_ETM_BUFFER_SIZE (8 * 1024)
+  record_btrace_conf.etm.size = DEFAULT_ETM_BUFFER_SIZE;
 }
diff --git a/gdb/record.c b/gdb/record.c
index 6968c30d930..32492fb53c4 100644
--- a/gdb/record.c
+++ b/gdb/record.c
@@ -118,6 +118,8 @@ record_start (const char *method, const char *format, int from_tty)
 	execute_command_to_string ("record btrace bts", from_tty, false);
       else if (strcmp (format, "pt") == 0)
 	execute_command_to_string ("record btrace pt", from_tty, false);
+      else if (strcmp (format, "etm") == 0)
+	execute_command_to_string ("record btrace etm", from_tty, false);
       else
 	error (_("Invalid format."));
     }
diff --git a/gdbsupport/btrace-common.cc b/gdbsupport/btrace-common.cc
index 4f9ef855e74..79ff21de44b 100644
--- a/gdbsupport/btrace-common.cc
+++ b/gdbsupport/btrace-common.cc
@@ -36,6 +36,9 @@ btrace_format_string (enum btrace_format format)
 
     case BTRACE_FORMAT_PT:
       return _("Intel Processor Trace");
+
+    case BTRACE_FORMAT_ETM:
+      return _("ARM Processor CoreSight ETM Trace");
     }
 
   internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
@@ -56,6 +59,9 @@ btrace_format_short_string (enum btrace_format format)
 
     case BTRACE_FORMAT_PT:
       return "pt";
+
+    case BTRACE_FORMAT_ETM:
+      return "etm";
     }
 
   internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
diff --git a/gdbsupport/btrace-common.h b/gdbsupport/btrace-common.h
index 26d26ec957f..153b977723a 100644
--- a/gdbsupport/btrace-common.h
+++ b/gdbsupport/btrace-common.h
@@ -63,7 +63,10 @@ enum btrace_format
   BTRACE_FORMAT_BTS,
 
   /* Branch trace is in Intel Processor Trace format.  */
-  BTRACE_FORMAT_PT
+  BTRACE_FORMAT_PT,
+
+  /* Branch trace is ARM CoreSight ETM format.  */
+  BTRACE_FORMAT_ETM
 };
 
 /* An enumeration of cpu vendors.  */
@@ -119,6 +122,20 @@ struct btrace_config_pt
   unsigned int size;
 };
 
+/* An ARM CoreSight ETM Trace configuration.  */
+
+struct btrace_config_etm
+{
+  /* The size of the branch trace buffer in bytes.
+
+     This is unsigned int and not size_t since it is registered as
+     control variable for "set record btrace etm buffer-size".  */
+  unsigned int size;
+
+  /* The sink used to collect the traces.  */
+  char *sink;
+};
+
 /* A branch tracing configuration.
 
    This describes the requested configuration as well as the actually
@@ -136,6 +153,9 @@ struct btrace_config
 
   /* The Intel Processor Trace format configuration.  */
   struct btrace_config_pt pt;
+
+  /* The ARM CoreSight ETM Trace configuration.  */
+  struct btrace_config_etm etm;
 };
 
 /* Branch trace in BTS format.  */
-- 
2.25.1


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant
  2021-05-31 21:33 [PATCH v6 0/7] extend branch tracing to use ARM CoreSight traces Zied Guermazi
  2021-05-31 21:33 ` [PATCH v6 1/7] configure gdb build system for supporting btrace on arm processors Zied Guermazi
  2021-05-31 21:33 ` [PATCH v6 2/7] add btrace coresight related commands Zied Guermazi
@ 2021-05-31 21:33 ` Zied Guermazi
  2021-06-22 14:59   ` Metzger, Markus T
  2021-06-30 12:54   ` Luis Machado
  2021-05-31 21:33 ` [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os Zied Guermazi
                   ` (3 subsequent siblings)
  6 siblings, 2 replies; 35+ messages in thread
From: Zied Guermazi @ 2021-05-31 21:33 UTC (permalink / raw)
  To: gdb-patches, markus.t.metzger; +Cc: Zied Guermazi

This patch extend branch tracing by adding the functions needed
to collect parameters for decoding ETM traces and decoding them.

gdb/ChangeLog

	* arch/arm.h: Add defines for exception numbers as encoded
	in the ETM traces.
	* btrace.c (ftrace_new_function): log new function.
	(ftrace_remove_last_insn): New.
	(cs_etm_get_etmv3_config): New.
	(cs_etm_get_etmv4_config): New.
	(cs_etm_get_isa_flag): New.
	(cs_etm_get_instruction_class): New.
	(cs_etm_update_btrace_with_inst_range): New.
	(cs_etm_update_btrace_with_exception): New.
	(cs_etm_update_btrace_with_trace_on): New.
	(cs_etm_trace_element_callback): New.
	(cs_etm_free_decoder): New.
	(cs_etm_create_decoder): New.
	(btrace_etm_readmem_callback): New.
	(cs_etm_add_mem_access_callback): New.
	(cs_etm_process_data_block): New.
	(btrace_print_all): New.
	(btrace_compute_ftrace_etm): New.
	(btrace_compute_ftrace_1): add handling of CoreSight traces.
	(btrace_enable): add error message if ETM unavailable.
	(btrace_stitch_trace): add handling of CoreSight traces.
	(maint_info_btrace_cmd): add handling of CoreSight trace format.
	* btrace.h (btrace_insn_flag): add ARM ISA flags.
	* record-btrace.c (cs_etm_reconstruct_cpsr_iset_state): New.
	(record_btrace_target::fetch_registers): fetch cpsr register
	from insn->flags when applicable.

gdbsupport/ChangeLog

	* btrace-common.h (cs_etmv3_trace_params): New
	(cs_etmv4_trace_params): New.
	(cs_etm_trace_params): New.
	(cs_etm_decoder_params): New
	(btrace_data_etm_config): New.
	(btrace_data_etm): New.
	(btrace_data): add a btrace_data_etm.
	* btrace-common.cc (btrace_data::fini): handle CoreSight traces.
	(btrace_data::empty): handle CoreSight traces.
	(btrace_data_append): handle CoreSight traces.
---
 gdb/arch/arm.h              |  33 ++
 gdb/btrace.c                | 618 ++++++++++++++++++++++++++++++++++++
 gdb/btrace.h                |  16 +-
 gdb/record-btrace.c         |  57 +++-
 gdbsupport/btrace-common.cc |  39 +++
 gdbsupport/btrace-common.h  | 106 +++++++
 6 files changed, 864 insertions(+), 5 deletions(-)

diff --git a/gdb/arch/arm.h b/gdb/arch/arm.h
index fa589fd0582..4d520fe906a 100644
--- a/gdb/arch/arm.h
+++ b/gdb/arch/arm.h
@@ -150,6 +150,39 @@ enum arm_m_profile_type {
 #define BranchDest(addr,instr) \
   ((CORE_ADDR) (((unsigned long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
 
+/* Exception numbers as encoded in ETMv3.4 for ARMv7-M processors.
+   See IHI0014Q_etm_architecture_spec table 7.11.  */
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_USAGE_FAULT    		0x009
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_NMI     			0x00a
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_SVC     			0x00b
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_DEBUG_MONITOR  		0x00c
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_MEM_USAGE      		0x00d
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_PEND_SV 			0x00e
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_SYS_TICK			0x00f
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_PROCESSOR_RESET		0x011
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_HARD_FAULT     		0x013
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_BUS_FAULT      		0x015
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_IRQ(n) \
+  (n == 0 ? 0x008 : n < 8 ? n : n + 0x010)
+#define CS_ETMV3_4_CORTEX_M_EXCEPTION_IS_IRQ(n) \
+  ((n > 0x000 && n < 0x009) || (n > 0x017 && n < 0x200))
+
+/* Exception numbers as encoded in ETMv3.4 for non ARMv7-M processors.
+   See IHI0014Q_etm_architecture_spec table 7.12.  */
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_HALTING_DEBUG		0x01
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_SECURE_MONITOR_CALL  	0x02
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_ENTRY_TO_HYP_MODE    	0x03
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_ASYNCHRONOUS_DATA_ABORT      0x04
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_JAZELLE_THUMBEE_CHECK	0x05
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_PROCESSOR_RESET      	0x08
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_UNDEFINED_INSTRUCTION	0x09
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_SUPERVISOR_CALL      	0x0a
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_PREFETCH_ABORT_SW_BKPT	0x0b
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_SYNCHRONOUS_DATA_ABORT_SW_WP 0x0c
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_GENERIC      		0x0d
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_IRQ   			0x0e
+#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_FIQ   			0x0f
+
 /* Forward declaration.  */
 struct regcache;
 
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 5e689c11d4b..2676389b63e 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -35,6 +35,7 @@
 #include "gdbcmd.h"
 #include "cli/cli-utils.h"
 #include "gdbarch.h"
+#include "arch/arm.h"
 
 /* For maintenance commands.  */
 #include "record-btrace.h"
@@ -671,6 +672,38 @@ ftrace_update_insns (struct btrace_function *bfun, const btrace_insn &insn)
     ftrace_debug (bfun, "update insn");
 }
 
+#if defined (HAVE_LIBOPENCSD_C_API)
+/* Remove last instruction from BFUN's list.
+   This function is not generic and does not undo functions chaining.
+   When adding an instruction after using it, the caller must ensure
+   that the instruction produces the same chaining.
+   An example of good case, is when the same removed instruction
+   is added later.  */
+
+static void
+ftrace_remove_last_insn (struct btrace_thread_info *btinfo)
+{
+  /* If we didn't have a function, we return.  */
+  if (btinfo->functions.empty ())
+    return;
+
+  struct btrace_function *bfun = &btinfo->functions.back ();
+  /* If we had a gap before, we return.  */
+  if (bfun->errcode != 0)
+    return;
+
+  if (!bfun->insn.empty ())
+    bfun->insn.pop_back ();
+  else
+    {
+      /* A valid function must have at least one instruction.  */
+      internal_error (__FILE__, __LINE__,
+		       _("Attempt to remove last instruction"
+			 "from an empty function"));
+    }
+}
+#endif /* defined (HAVE_LIBOPENCSD_C_API) */
+
 /* Classify the instruction at PC.  */
 
 static enum btrace_insn_class
@@ -1505,6 +1538,574 @@ btrace_compute_ftrace_pt (struct thread_info *tp,
 
 #endif /* defined (HAVE_LIBIPT)  */
 
+#if defined (HAVE_LIBOPENCSD_C_API)
+
+/* This structure holds the status and the context for decoding ETM traces.
+   It is also used in the ETM trace element callback to get the context.  */
+struct cs_etm_decoder
+{
+  /* The tree representing CoreSight architecture in the SoC.  */
+  dcd_tree_handle_t dcd_tree;
+
+  /* Callback function to allow the decoder to access program memory.  */
+  Fn_MemAcc_CB mem_access;
+
+  /* thread_info of traced thread.  */
+  struct thread_info *t_info;
+
+  /* Returned value of previously processed block.  */
+  ocsd_datapath_resp_t prev_return;
+
+  /* ARM architecture version of associated core.  */
+  ocsd_arch_version_t arch_version;
+
+  /* List of gaps in the execution record.  */
+  std::vector<unsigned int> &gaps;
+};
+
+/* Fills a ocsd_etmv3_cfg from a cs_etm_trace_params.  */
+
+static void
+cs_etm_get_etmv3_config (const struct cs_etm_trace_params *params,
+			  ocsd_etmv3_cfg *config)
+{
+  config->reg_idr = params->etmv3.reg_idr;
+  config->reg_ctrl = params->etmv3.reg_ctrl;
+  config->reg_ccer = params->etmv3.reg_ccer;
+  config->reg_trc_id = params->etmv3.reg_trc_id;
+  config->arch_ver = (ocsd_arch_version_t)params->arch_ver;
+  config->core_prof = (ocsd_core_profile_t)params->core_profile;
+}
+
+/* Fills a ocsd_etmv4_cfg from a cs_etm_trace_params.  */
+
+static void
+cs_etm_get_etmv4_config (const struct cs_etm_trace_params *params,
+			  ocsd_etmv4_cfg *config)
+{
+  config->reg_configr = params->etmv4.reg_configr;
+  config->reg_traceidr = params->etmv4.reg_traceidr;
+  config->reg_idr0 = params->etmv4.reg_idr0;
+  config->reg_idr1 = params->etmv4.reg_idr1;
+  config->reg_idr2 = params->etmv4.reg_idr2;
+  config->reg_idr8 = params->etmv4.reg_idr8;
+  config->reg_idr9 = 0;
+  config->reg_idr10 = 0;
+  config->reg_idr11 = 0;
+  config->reg_idr12 = 0;
+  config->reg_idr13 = 0;
+  config->arch_ver = (ocsd_arch_version_t)params->arch_ver;
+  config->core_prof = (ocsd_core_profile_t)params->core_profile;
+}
+
+/* Set the instruction flag to track ISA mode of ARM processor.  */
+
+static btrace_insn_flags
+cs_etm_get_isa_flag (const ocsd_generic_trace_elem *elem)
+{
+  switch (elem->isa)
+    {
+    case ocsd_isa_arm:
+      return BTRACE_INSN_FLAG_ISA_ARM;
+
+    case ocsd_isa_thumb2:
+      return BTRACE_INSN_FLAG_ISA_THUMB2;
+
+    case ocsd_isa_aarch64:
+      return BTRACE_INSN_FLAG_ISA_AARCH64;
+
+    case ocsd_isa_tee:
+      return BTRACE_INSN_FLAG_ISA_TEE;
+
+    case ocsd_isa_jazelle:
+      return BTRACE_INSN_FLAG_ISA_JAZELLE;
+
+    case ocsd_isa_custom:
+      return BTRACE_INSN_FLAG_ISA_CUSTOM;
+
+    case ocsd_isa_unknown:
+      return BTRACE_INSN_FLAG_ISA_UNKNOWN;
+
+    default:
+      internal_error (__FILE__, __LINE__,
+		       _("Undefined elem->isa value returned by OpenCsd."));
+    }
+}
+
+/* Returns the instruction class.  */
+
+static enum btrace_insn_class
+cs_etm_get_instruction_class (const ocsd_generic_trace_elem *elem)
+{
+  switch (elem->last_i_type)
+    {
+    case OCSD_INSTR_BR:
+    case OCSD_INSTR_BR_INDIRECT:
+      switch (elem->last_i_subtype)
+	{
+	case OCSD_S_INSTR_V8_RET:
+	case OCSD_S_INSTR_V8_ERET:
+	case OCSD_S_INSTR_V7_IMPLIED_RET:
+	  return BTRACE_INSN_RETURN;
+
+	case OCSD_S_INSTR_BR_LINK:
+	  return BTRACE_INSN_CALL;
+
+	case OCSD_S_INSTR_NONE:
+	  return BTRACE_INSN_JUMP;
+	}
+      return BTRACE_INSN_OTHER;
+
+    case OCSD_INSTR_ISB:
+    case OCSD_INSTR_DSB_DMB:
+    case OCSD_INSTR_WFI_WFE:
+    case OCSD_INSTR_OTHER:
+    default:
+      return BTRACE_INSN_OTHER;
+    }
+}
+
+/* Update btrace in the case of an instruction range.  */
+
+static void
+cs_etm_update_btrace_with_inst_range (const struct cs_etm_decoder *etm_decoder,
+				       const ocsd_generic_trace_elem *elem)
+{
+  gdb_assert (elem->elem_type == OCSD_GEN_TRC_ELEM_INSTR_RANGE);
+
+  struct thread_info *tp = etm_decoder->t_info;
+  struct btrace_thread_info *btinfo = &tp->btrace;
+  struct gdbarch *gdbarch = target_gdbarch ();
+  struct btrace_insn insn;
+
+  insn.iclass = BTRACE_INSN_OTHER;
+  insn.pc = elem->st_addr;
+  for (int i = 0; i< elem->num_instr_range; i++)
+    {
+      try
+	{
+	  insn.size = gdb_insn_length (gdbarch, insn.pc);
+	}
+      catch (const gdb_exception_error &err)
+	{
+	  error (_("Failed to get the size of the instruction."));
+	}
+
+      struct btrace_function *bfun = ftrace_update_function (btinfo, insn.pc);
+      if (etm_decoder->arch_version == ARCH_V7)
+	insn.flags = cs_etm_get_isa_flag (elem);
+
+      if (i == elem->num_instr_range -1)
+	insn.iclass = cs_etm_get_instruction_class (elem);
+
+      ftrace_update_insns (bfun, insn);
+      insn.pc = insn.pc + insn.size;
+    }
+}
+
+/* Update btrace in the case of an exception.  */
+
+static void
+cs_etm_update_btrace_with_exception (const struct cs_etm_decoder *etm_decoder,
+				      const ocsd_generic_trace_elem *elem)
+{
+  gdb_assert (elem->elem_type == OCSD_GEN_TRC_ELEM_EXCEPTION);
+
+  struct thread_info *tp = etm_decoder->t_info;
+  struct btrace_thread_info *btinfo = &tp->btrace;
+
+  /* Handle the implementation of breakpoints in gdb for arm (v7) architecture
+     using undefined instructions.  */
+  if (etm_decoder->arch_version == ARCH_V7)
+    {
+      if (elem->exception_number
+	   == CS_ETMV3_4_CORTEX_A_R_EXCEPTION_UNDEFINED_INSTRUCTION)
+	{
+	  DEBUG ("handle breakpoints implementation in gdb for ARMv7");
+	  ftrace_remove_last_insn (btinfo);
+	}
+    }
+}
+
+/* Update btrace in the case of a trace on.  */
+
+static void
+cs_etm_update_btrace_with_trace_on (const struct cs_etm_decoder *etm_decoder,
+				     const ocsd_generic_trace_elem *elem)
+{
+  gdb_assert (elem->elem_type == OCSD_GEN_TRC_ELEM_TRACE_ON);
+
+  if (elem->trace_on_reason != TRACE_ON_NORMAL)
+    {
+      struct thread_info *tp = etm_decoder->t_info;
+      struct btrace_thread_info *btinfo = &tp->btrace;
+      ftrace_new_gap (btinfo, elem->trace_on_reason, etm_decoder->gaps);
+    }
+}
+
+/* Callback function when a ocsd_generic_trace_elem is emitted.  */
+
+static ocsd_datapath_resp_t
+cs_etm_trace_element_callback (const void *context,
+				const ocsd_trc_index_t index,
+				const uint8_t trace_chan_id,
+				const ocsd_generic_trace_elem *elem)
+{
+  if (record_debug != 0)
+    {
+      char str_buffer[128];
+      if (ocsd_gen_elem_str (elem, str_buffer, 128) == OCSD_OK)
+	DEBUG ("ETM trace_element: index= %s, channel= 0x%x, %s",
+	       pulongest (index), trace_chan_id, str_buffer);
+    }
+
+  const struct cs_etm_decoder *etm_decoder = (struct cs_etm_decoder *)context;
+  gdb_assert (etm_decoder->t_info != nullptr);
+
+  switch (elem->elem_type)
+    {
+    case OCSD_GEN_TRC_ELEM_TRACE_ON:
+      cs_etm_update_btrace_with_trace_on (etm_decoder, elem);
+      break;
+
+    case OCSD_GEN_TRC_ELEM_INSTR_RANGE:
+      cs_etm_update_btrace_with_inst_range (etm_decoder, elem);
+      break;
+
+    case OCSD_GEN_TRC_ELEM_EXCEPTION:
+      cs_etm_update_btrace_with_exception (etm_decoder, elem);
+      break;
+
+    /* Not yet handled types, but may be supported in the future.  */
+    case OCSD_GEN_TRC_ELEM_UNKNOWN:
+    case OCSD_GEN_TRC_ELEM_EO_TRACE:
+    case OCSD_GEN_TRC_ELEM_NO_SYNC:
+    case OCSD_GEN_TRC_ELEM_EXCEPTION_RET:
+    case OCSD_GEN_TRC_ELEM_TIMESTAMP:
+    case OCSD_GEN_TRC_ELEM_PE_CONTEXT:
+    case OCSD_GEN_TRC_ELEM_ADDR_NACC:
+    case OCSD_GEN_TRC_ELEM_CYCLE_COUNT:
+    case OCSD_GEN_TRC_ELEM_ADDR_UNKNOWN:
+    case OCSD_GEN_TRC_ELEM_EVENT:
+    case OCSD_GEN_TRC_ELEM_SWTRACE:
+    case OCSD_GEN_TRC_ELEM_CUSTOM:
+    default:
+      break;
+    }
+  return OCSD_RESP_CONT;
+}
+
+/* Callback to print error log.  */
+
+static void cs_etm_print_error_log (const void *p_context, const char *psz_msg_str, const int str_len)
+{
+    char string_buffer[128];
+    memcpy (string_buffer, psz_msg_str, std::min(str_len, 127));
+    if (str_len >127)
+      string_buffer[127] = 0;
+    else
+      string_buffer[str_len] = 0;
+    warning (_("Processing ETM traces failed with error: %s"), psz_msg_str);
+}
+
+/* Create a cs_etm_decoder and initialize it.  */
+
+static ocsd_err_t
+cs_etm_create_decoder (struct cs_etm_trace_params *t_params,
+			struct cs_etm_decoder *decoder)
+{
+  ocsd_err_t errcode;
+  const char *decoder_name;
+  ocsd_etmv3_cfg config_etmv3;
+  ocsd_etmv4_cfg config_etmv4;
+  void *trace_config;
+  switch (t_params->protocol)
+    {
+    case OCSD_PROTOCOL_ETMV3:
+    case OCSD_PROTOCOL_PTM:
+      cs_etm_get_etmv3_config (t_params, &config_etmv3);
+      decoder_name = (t_params->protocol == OCSD_PROTOCOL_ETMV3)
+		      ? OCSD_BUILTIN_DCD_ETMV3 : OCSD_BUILTIN_DCD_PTM;
+      trace_config = &config_etmv3;
+      decoder->arch_version = ARCH_V7;
+      break;
+
+    case OCSD_PROTOCOL_ETMV4I:
+      cs_etm_get_etmv4_config (t_params, &config_etmv4);
+      decoder_name = OCSD_BUILTIN_DCD_ETMV4I;
+      trace_config = &config_etmv4;
+      decoder->arch_version = ARCH_V8;
+      break;
+
+    default:
+      decoder->arch_version = ARCH_UNKNOWN;
+      warning (_("cs_etm_create_decoder: Unknown architecture version"));
+      return OCSD_ERR_NOT_INIT;
+
+  }
+  uint8_t csid;
+  errcode = ocsd_dt_create_decoder (decoder->dcd_tree, decoder_name,
+			      OCSD_CREATE_FLG_FULL_DECODER,
+			      trace_config, &csid);
+  if (errcode != OCSD_OK)
+    {
+      warning (_("ocsd_dt_create_decoder failed with error: %d"), errcode);
+      return errcode;
+    }
+
+  errcode = ocsd_dt_set_gen_elem_outfn (decoder->dcd_tree,
+					 cs_etm_trace_element_callback,
+					 decoder);
+  if (errcode != OCSD_OK)
+    {
+      warning (_("ocsd_dt_set_gen_elem_outfn failed failed with error: %d"),
+		   errcode);
+      return errcode;
+    }
+
+  errcode = ocsd_def_errlog_init (OCSD_ERR_SEV_ERROR, 1);
+  if (errcode != OCSD_OK)
+    {
+      warning (_("ocsd_def_errlog_init failed failed with error: %d"),
+		   errcode);
+      return errcode;
+    }
+
+  /* Initialize error printer.  */
+  errcode = ocsd_def_errlog_config_output (C_API_MSGLOGOUT_FLG_NONE, nullptr);
+  if (errcode != OCSD_OK)
+    {
+      warning (_("ocsd_def_errlog_init failed failed with error: %d"),
+		   errcode);
+      return errcode;
+    }
+
+  errcode = ocsd_def_errlog_set_strprint_cb (decoder->dcd_tree, nullptr,
+					      cs_etm_print_error_log);
+  if (errcode != OCSD_OK)
+    {
+      warning (_("ocsd_def_errlog_set_strprint_cb failed failed with error: %d"),
+		   errcode);
+      return errcode;
+    }
+
+  decoder->prev_return = OCSD_RESP_CONT;
+  return OCSD_OK;
+}
+
+/* Free a cs_etm_decoder.  */
+
+static void
+cs_etm_free_decoder (struct cs_etm_decoder *decoder)
+{
+  if (decoder == nullptr)
+    return;
+
+  ocsd_destroy_dcd_tree (decoder->dcd_tree);
+  decoder->dcd_tree = nullptr;
+  decoder->t_info = nullptr;
+  xfree (decoder);
+}
+
+/* Allocate a cs_etm_decoder and initialize it.  */
+
+static struct cs_etm_decoder *
+cs_etm_alloc_decoder (struct thread_info *tp, int cpu_count,
+		      const struct cs_etm_decoder_params * d_params,
+		      std::vector<cs_etm_trace_params> * t_params)
+{
+  ocsd_dcd_tree_src_t src_type = OCSD_TRC_SRC_SINGLE;
+  uint32_t deformatterCfgFlags = 0;
+
+  gdb_assert (d_params != nullptr);
+
+  if (d_params->formatted)
+    src_type = OCSD_TRC_SRC_FRAME_FORMATTED;
+  if (d_params->frame_aligned)
+    deformatterCfgFlags |= OCSD_DFRMTR_FRAME_MEM_ALIGN;
+  if (d_params->fsyncs)
+    deformatterCfgFlags |= OCSD_DFRMTR_HAS_FSYNCS;
+  if (d_params->hsyncs)
+    deformatterCfgFlags |= OCSD_DFRMTR_HAS_HSYNCS;
+  if (d_params->reset_on_4x_sync)
+    deformatterCfgFlags |= OCSD_DFRMTR_RESET_ON_4X_FSYNC;
+
+  dcd_tree_handle_t dcdtree_handle;
+  dcdtree_handle = ocsd_create_dcd_tree (src_type, deformatterCfgFlags);
+
+  if (dcdtree_handle == C_API_INVALID_TREE_HANDLE)
+    {
+      DEBUG ("ocsd_create_dcd_tree failed");
+      return nullptr;
+    }
+  struct cs_etm_decoder *decoder;
+
+  decoder = (struct cs_etm_decoder*) xmalloc (sizeof (struct cs_etm_decoder));
+  decoder->dcd_tree = dcdtree_handle;
+
+  for (int i = 0; i < cpu_count; i++)
+    {
+      ocsd_err_t errcode = cs_etm_create_decoder (&(t_params->at (i)), decoder);
+      if (errcode != OCSD_OK)
+	{
+	  cs_etm_free_decoder (decoder);
+	  return nullptr;
+	}
+    }
+
+  decoder->t_info = tp;
+  return decoder;
+}
+
+/* A callback function to allow the trace decoder to read the inferior's
+   memory.
+   Returns the number of bytes actually read, or 0 for access error  */
+
+static uint32_t
+btrace_etm_readmem_callback (const void *p_context, const ocsd_vaddr_t address,
+			      const ocsd_mem_space_acc_t mem_space,
+			      const uint32_t reqBytes, uint8_t *byteBuffer)
+{
+  int result = (int) reqBytes;
+  try
+  {
+      int errcode = target_read_code ((CORE_ADDR) address, byteBuffer, reqBytes);
+      if (errcode != 0)
+	result = 0;
+  }
+  catch (const gdb_exception_error &error)
+  {
+      result = 0;
+  }
+
+  return result;
+}
+
+/* Add memory access callback to the decoder.  */
+
+static ocsd_err_t
+cs_etm_add_mem_access_callback (struct cs_etm_decoder *decoder,
+				 CORE_ADDR start, CORE_ADDR end,
+				 Fn_MemAcc_CB p_cb_func)
+{
+  ocsd_err_t error;
+  error = ocsd_dt_add_callback_mem_acc (decoder->dcd_tree,
+					 (ocsd_vaddr_t) start,
+					 (ocsd_vaddr_t) end,
+					 OCSD_MEM_SPACE_ANY,
+					 p_cb_func, decoder);
+  if (error == OCSD_OK)
+    decoder->mem_access = p_cb_func;
+
+  return error;
+}
+
+/* Process an etm traces data block.
+   In case of an error it resets the decoder and tries to process further.  */
+
+static void
+cs_etm_process_data_block (struct btrace_thread_info *btinfo,
+			   struct cs_etm_decoder *decoder,
+			   uint64_t index, const uint8_t *buf,
+			   size_t len, size_t *consumed)
+{
+  ocsd_datapath_resp_t data_path_return = OCSD_RESP_CONT;
+  size_t processed = 0;
+  uint32_t count;
+
+  while (processed < len)
+    {
+      if (OCSD_DATA_RESP_IS_CONT (data_path_return))
+	{
+	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
+					OCSD_OP_DATA,
+					index + processed, len - processed,
+					&buf[processed], &count);
+	  processed += count;
+	  
+	}
+      else if (OCSD_DATA_RESP_IS_WAIT (data_path_return))
+	{
+	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
+					OCSD_OP_FLUSH,
+					0, 0, nullptr, nullptr);
+	}
+      else
+	{
+	  warning (_("error %d in ocsd_dt_process_data after processing %zu"),
+		      data_path_return, processed);
+	  ftrace_new_gap (btinfo, data_path_return, decoder->gaps);
+	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
+					OCSD_OP_RESET,
+					0, 0, nullptr, nullptr);
+	  //todo: shall we increase processed? by 1, or 4 do we need to manually align to 16 bytes?
+	}
+      DEBUG_FTRACE ("ocsd_dt_process_data returned with error code %d."
+			 " Consumed = 0x%zx",
+			 data_path_return,
+			 processed);
+    }
+  *consumed = processed;
+}
+
+/* Print all functions in a btrace.  */
+
+static void
+btrace_print_all (struct btrace_thread_info *btinfo)
+{
+  for (const btrace_function &function : btinfo->functions)
+    ftrace_debug (&function, "");
+}
+
+static void
+btrace_compute_ftrace_etm (struct thread_info *tp,
+			   const struct btrace_data_etm *btrace,
+			   std::vector<unsigned int> &gaps)
+{
+  DEBUG_FTRACE ("btrace->size is 0x%x for thread %s",
+		(unsigned int)(btrace->size), print_thread_id (tp));
+  if (btrace->size == 0)
+    return;
+
+  struct btrace_thread_info *btinfo = &tp->btrace;
+  if (btinfo->functions.empty ())
+    btinfo->level = 0;
+
+  struct cs_etm_decoder *decoder
+   = cs_etm_alloc_decoder (tp, btrace->config.cpu_count,
+			    &(btrace->config.etm_decoder_params),
+			    btrace->config.etm_trace_params);
+  if (decoder == nullptr)
+    error (_("Failed to allocate ARM CoreSight ETM Trace decoder."));
+
+  ocsd_err_t ocsd_error
+   = cs_etm_add_mem_access_callback (decoder,
+				      (CORE_ADDR) 0x0L, (CORE_ADDR) -1L,
+				      btrace_etm_readmem_callback);
+  if (ocsd_error != OCSD_OK)
+    error (_("Failed to add CoreSight Trace decoder memory access callback."));
+
+  size_t consumed;
+  cs_etm_process_data_block (btinfo, decoder, 0, btrace->data, btrace->size,
+				 &consumed);
+  DEBUG_FTRACE ("CoreSight Trace processed. Consumed 0x%zx", consumed);
+
+  ftrace_compute_global_level_offset (btinfo);
+  btrace_add_pc (tp);
+  btrace_print_all (btinfo);
+  cs_etm_free_decoder (decoder);
+}
+#else /*    defined (HAVE_LIBOPENCSD_C_API)    */
+
+static void
+btrace_compute_ftrace_etm (struct thread_info *tp,
+			   const struct btrace_data_etm *btrace,
+			   std::vector<unsigned int> &gaps)
+{
+  internal_error (__FILE__, __LINE__, _("Unexpected branch trace format."));
+}
+#endif /*    defined (HAVE_LIBOPENCSD_C_API)    */
+
 /* Compute the function branch trace from a block branch trace BTRACE for
    a thread given by BTINFO.  If CPU is not NULL, overwrite the cpu in the
    branch trace configuration.  This is currently only used for the PT
@@ -1534,6 +2135,10 @@ btrace_compute_ftrace_1 (struct thread_info *tp,
 
       btrace_compute_ftrace_pt (tp, &btrace->variant.pt, gaps);
       return;
+
+    case BTRACE_FORMAT_ETM:
+      btrace_compute_ftrace_etm (tp, &btrace->variant.etm, gaps);
+      return;
     }
 
   internal_error (__FILE__, __LINE__, _("Unknown branch trace format."));
@@ -1602,6 +2207,10 @@ btrace_enable (struct thread_info *tp, const struct btrace_config *conf)
   if (conf->format == BTRACE_FORMAT_PT)
     error (_("Intel Processor Trace support was disabled at compile time."));
 #endif /* !defined (HAVE_LIBIPT) */
+#if !defined (HAVE_LIBOPENCSD_C_API)
+  if (conf->format == BTRACE_FORMAT_ETM)
+    error (_("ARM CoreSight Trace support was disabled at compile time."));
+#endif /* !defined (HAVE_LIBOPENCSD_C_API) */
 
   DEBUG ("enable thread %s (%s)", print_thread_id (tp),
 	 target_pid_to_str (tp->ptid).c_str ());
@@ -1794,6 +2403,10 @@ btrace_stitch_trace (struct btrace_data *btrace, struct thread_info *tp)
     case BTRACE_FORMAT_PT:
       /* Delta reads are not supported.  */
       return -1;
+
+    case BTRACE_FORMAT_ETM:
+      /* Delta reads are not supported.  */
+      return -1;
     }
 
   internal_error (__FILE__, __LINE__, _("Unknown branch trace format."));
@@ -3416,6 +4029,11 @@ maint_info_btrace_cmd (const char *args, int from_tty)
       }
       break;
 #endif /* defined (HAVE_LIBIPT)  */
+#if defined (HAVE_LIBOPENCSD_C_API)
+    case BTRACE_FORMAT_ETM:
+      printf_unfiltered (_("Version: %s.\n"), ocsd_get_version_str ());
+      break;
+#endif /* defined (HAVE_LIBOPENCSD_C_API) */
     }
 }
 
diff --git a/gdb/btrace.h b/gdb/btrace.h
index 8f6ce32103d..92e14b6aadf 100644
--- a/gdb/btrace.h
+++ b/gdb/btrace.h
@@ -34,6 +34,12 @@
 #  include <intel-pt.h>
 #endif
 
+#if defined (HAVE_LIBOPENCSD_C_API)
+#  include <opencsd/c_api/opencsd_c_api.h>
+#  include <opencsd/etmv4/trc_pkt_types_etmv4.h>
+#  include <opencsd/ocsd_if_types.h>
+#endif
+
 #include <vector>
 
 struct thread_info;
@@ -59,7 +65,15 @@ enum btrace_insn_class
 enum btrace_insn_flag
 {
   /* The instruction has been executed speculatively.  */
-  BTRACE_INSN_FLAG_SPECULATIVE = (1 << 0)
+  BTRACE_INSN_FLAG_SPECULATIVE =	(1 << 0),
+  BTRACE_INSN_FLAG_ISA_ARM =		(1 << 24),
+  BTRACE_INSN_FLAG_ISA_THUMB2 =	(2 << 24),
+  BTRACE_INSN_FLAG_ISA_AARCH64 =	(3 << 24),
+  BTRACE_INSN_FLAG_ISA_TEE =		(4 << 24),
+  BTRACE_INSN_FLAG_ISA_JAZELLE =	(5 << 24),
+  BTRACE_INSN_FLAG_ISA_CUSTOM =	(6 << 24),
+  BTRACE_INSN_FLAG_ISA_UNKNOWN =	(7 << 24),
+  BTRACE_INSN_FLAG_ISA_MASK =		(15 << 24)
 };
 DEF_ENUM_FLAGS_TYPE (enum btrace_insn_flag, btrace_insn_flags);
 
diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
index 16ffb76272b..57b9c8ec487 100644
--- a/gdb/record-btrace.c
+++ b/gdb/record-btrace.c
@@ -44,6 +44,7 @@
 #include "cli/cli-style.h"
 #include "async-event.h"
 #include <forward_list>
+#include "arch/arm.h"
 
 static const target_info record_btrace_target_info = {
   "record-btrace",
@@ -1557,6 +1558,38 @@ record_btrace_target::remove_breakpoint (struct gdbarch *gdbarch,
   return ret;
 }
 
+/* Reconstruct the instruction set state bits of CPSR register
+   according to instruction flags.
+   See Table A2-1 in DDI0406B_arm_architecture_reference_manual
+   for more details.  */
+
+static unsigned int
+cs_etm_reconstruct_cpsr_iset_state (const struct btrace_insn *insn)
+{
+  switch (insn->flags & BTRACE_INSN_FLAG_ISA_MASK)
+    {
+    case BTRACE_INSN_FLAG_ISA_ARM:
+      /* ARM state: J and T bits are not set.  */
+      return 0;
+
+    case BTRACE_INSN_FLAG_ISA_THUMB2:
+      /* THUMB state: J bit is not set, T bit is set.  */
+      return 0x20;
+
+    case BTRACE_INSN_FLAG_ISA_TEE:
+      /* THUMB EE state: J and T bits are set.  */
+      return 0x1000020;
+
+    case BTRACE_INSN_FLAG_ISA_JAZELLE:
+      /* JAZELLE state: J bit is set, T bit is not set.  */
+      return 0x1000000;
+
+    default:
+      /* Default is ARM mode.  */
+      return 0;
+    }
+}
+
 /* The fetch_registers method of target record-btrace.  */
 
 void
@@ -1581,16 +1614,32 @@ record_btrace_target::fetch_registers (struct regcache *regcache, int regno)
       pcreg = gdbarch_pc_regnum (gdbarch);
       if (pcreg < 0)
 	return;
-
-      /* We can only provide the PC register.  */
-      if (regno >= 0 && regno != pcreg)
+      /* We can only provide the PC or CPSR registers here.  */
+      if (regno >= 0 && !(regno == pcreg || regno == ARM_PS_REGNUM))
 	return;
 
       insn = btrace_insn_get (replay);
-      gdb_assert (insn != NULL);
+      gdb_assert (insn != nullptr);
 
+      if ((regno < 0) || (regno == pcreg))
+	{
 	  regcache->raw_supply (regno, &insn->pc);
 	}
+      if ((regno < 0) || (regno == ARM_PS_REGNUM))
+	{
+	  /*  Provide CPSR register in the case of an armv7 target.  */
+	  const struct target_desc *tdesc = gdbarch_target_desc (gdbarch);
+
+	  const char *tdesc_name = tdesc_architecture_name (tdesc);
+	  if (strcmp (tdesc_name, "arm") == 0)
+	    {
+	      int cpsr;
+	      cpsr = cs_etm_reconstruct_cpsr_iset_state (insn);
+	      regcache->raw_supply (regno, &cpsr);
+	    }
+	}
+      return;
+    }
   else
     this->beneath ()->fetch_registers (regcache, regno);
 }
diff --git a/gdbsupport/btrace-common.cc b/gdbsupport/btrace-common.cc
index 79ff21de44b..6005b0d15e4 100644
--- a/gdbsupport/btrace-common.cc
+++ b/gdbsupport/btrace-common.cc
@@ -86,6 +86,12 @@ btrace_data::fini ()
     case BTRACE_FORMAT_PT:
       xfree (variant.pt.data);
       return;
+
+    case BTRACE_FORMAT_ETM:
+      delete variant.etm.config.etm_trace_params;
+      variant.etm.config.etm_trace_params = nullptr;
+      xfree (variant.etm.data);
+      return;
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
@@ -106,6 +112,9 @@ btrace_data::empty () const
 
     case BTRACE_FORMAT_PT:
       return (variant.pt.size == 0);
+
+    case BTRACE_FORMAT_ETM:
+      return (variant.etm.size == 0);
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
@@ -191,6 +200,36 @@ btrace_data_append (struct btrace_data *dst,
 	  }
 	}
       return 0;
+
+    case BTRACE_FORMAT_ETM:
+      switch (dst->format)
+	{
+	default:
+	  return -1;
+
+	case BTRACE_FORMAT_NONE:
+	  dst->format = BTRACE_FORMAT_ETM;
+	  dst->variant.etm.data = nullptr;
+	  dst->variant.etm.size = 0;
+
+	/* fall-through.  */
+	case BTRACE_FORMAT_ETM:
+	  {
+	    size_t size = src->variant.etm.size + dst->variant.etm.size;
+	    gdb_byte *data = (gdb_byte *) xmalloc (size);
+
+	    if (dst->variant.etm.size > 0)
+	      memcpy (data, dst->variant.etm.data, dst->variant.etm.size);
+	    memcpy (data + dst->variant.etm.size, src->variant.etm.data,
+		    src->variant.etm.size);
+
+	    xfree (dst->variant.etm.data);
+
+	    dst->variant.etm.data = data;
+	    dst->variant.etm.size = size;
+	  }
+	}
+      return 0;
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
diff --git a/gdbsupport/btrace-common.h b/gdbsupport/btrace-common.h
index 153b977723a..ee05ecb8b10 100644
--- a/gdbsupport/btrace-common.h
+++ b/gdbsupport/btrace-common.h
@@ -187,6 +187,109 @@ struct btrace_data_pt
   size_t size;
 };
 
+/* Parameters needed for decoding ETMv3 traces.
+   See open source coresight trace decoder library (opencsd)
+   for further details.  */
+struct cs_etmv3_trace_params
+{
+  /* ETMv3 Main Control Register.  */
+  uint32_t reg_ctrl;
+
+  /* ETMv3 Trace ID Register.  */
+  uint32_t reg_trc_id;
+
+  /* ETMv3 Configuration Code Extension Register.  */
+  uint32_t reg_ccer;
+
+  /* ETMv3 ID Register.  */
+  uint32_t reg_idr;
+};
+
+/* Parameters needed for decoding ETMv4 traces.
+   See open source coresight trace decoder library (opencsd)
+   for further details.  */
+struct cs_etmv4_trace_params
+{
+  /* ETMv4 ID Register 0.  */
+  uint32_t reg_idr0;
+
+  /* ETMv4 ID Register 1.  */
+  uint32_t reg_idr1;
+
+  /* ETMv4 ID Register 2.  */
+  uint32_t reg_idr2;
+
+  /* ETMv4 ID Register 8.  */
+  uint32_t reg_idr8;
+
+  /* ETMv4 Config Register.  */
+  uint32_t reg_configr;
+
+  /* ETMv4 Trace ID Register.  */
+  uint32_t reg_traceidr;
+};
+
+/* Parameters of trace source.  */
+struct cs_etm_trace_params
+{
+  /* Architecture version of trace source.  */
+  int arch_ver;
+  /* Core profile of the trace source.  */
+  int core_profile;
+  /* Traces protocol.  */
+  int protocol;
+  union {
+    struct cs_etmv3_trace_params etmv3;
+    struct cs_etmv4_trace_params etmv4;
+  };
+};
+
+/* Parameters of trace sink/decoder.  */
+struct cs_etm_decoder_params
+{
+  uint8_t formatted:1,     /* Formatted input, or single source input.  */
+  fsyncs	   :1,     /* Formatted data has fsyncs.  */
+  hsyncs	   :1,     /* Formatted data has hsyncs.  */
+  frame_aligned    :1,     /* Formatted frames are memory aligned.  */
+  reset_on_4x_sync :1,     /* Reset decoders at 4x consecutive fsyncs.  */
+  __res	    :3;     /* Reserved, not used.  */
+};
+
+/* Configuration information to go with the etm trace data.  */
+struct btrace_data_etm_config
+{
+  /* Count of the CPUs (trace sources).  */
+  int    cpu_count;
+  /* List of traces sources parameters.  */
+  std::vector<struct cs_etm_trace_params> *etm_trace_params;
+  /* Trace sink parameters.  */
+  struct cs_etm_decoder_params etm_decoder_params;
+};
+
+/* Branch trace in ARM Processor Trace format.  */
+struct btrace_data_etm
+{
+  /* Some configuration information to go with the data.  */
+  struct btrace_data_etm_config config;
+
+  /* The trace data.  */
+  gdb_byte *data;
+
+  /* The size of DATA in bytes.  */
+  size_t size;
+
+  /* Trace id for this thread.
+     On a linux system, trace_id is assigned per cpu. The kernel copies 
+     the traces of each thread in a dedicated ring buffer. By this,
+     traces belonging to different threads are de-multiplexed.
+     On an RTOS system, especially when routing the traces outside of the SoC,
+     the OS has no other mean for de-multiplexing the traces than
+     the trace_id. The hardware (ETM IP) reserves 7 bits for the trace_id.
+     On linux system trace id is not needed, set it to 0xFF to ignore it
+     during parsing.  */
+  uint8_t trace_id;
+};
+
 /* The branch trace data.  */
 struct btrace_data
 {
@@ -224,6 +327,9 @@ struct btrace_data
 
     /* Format == BTRACE_FORMAT_PT.  */
     struct btrace_data_pt pt;
+
+    /* Format == BTRACE_FORMAT_ETM.  */
+    struct btrace_data_etm etm;
   } variant;
 
 private:
-- 
2.25.1


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os
  2021-05-31 21:33 [PATCH v6 0/7] extend branch tracing to use ARM CoreSight traces Zied Guermazi
                   ` (2 preceding siblings ...)
  2021-05-31 21:33 ` [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant Zied Guermazi
@ 2021-05-31 21:33 ` Zied Guermazi
  2021-06-23  8:00   ` Metzger, Markus T
  2021-06-30 13:24   ` Luis Machado
  2021-05-31 21:33 ` [PATCH v6 5/7] fix issue: gdb hangs in the command following a commad returning with TARGET_WAITKIND_NO_HISTORY Zied Guermazi
                   ` (2 subsequent siblings)
  6 siblings, 2 replies; 35+ messages in thread
From: Zied Guermazi @ 2021-05-31 21:33 UTC (permalink / raw)
  To: gdb-patches, markus.t.metzger; +Cc: Zied Guermazi

This patch implement the lower layer for starting ad stopping
ARM CoreSight tracing on linux targets for arm and aarch64

gdb/ChangeLog

	* nat/linux-btrace.h (btrace_tinfo_etm): New.
	(btrace_target_info): add etm.
	* nat/linux-btrace.c (linux_enable_bts): get page size from sysconf.
	(linux_enable_pt): get page size from sysconf.
	(perf_event_etm_event_type): New.
	(perf_event_etm_event_sink): New.
	(linux_enable_etm): New.
	(linux_enable_btrace): add enabling etm traces.
	(linux_disable_bts): get page size from sysconf.
	(linux_disable_pt): get page size from sysconf.
	(linux_disable_etm): New.
	(linux_disable_btrace): add disabling etm traces.
	(get_cpu_count): New.
	(cs_etm_is_etmv4): New.
	(cs_etm_get_register): New.
	(coresight_get_trace_id): New.
	(fill_etm_trace_params): New.
	(linux_fill_btrace_etm_config): New.
	(linux_read_etm): New.
	(linux_read_btrace): add reading etm traces.
	* arm-linux-nat.c (arm_linux_nat_target::enable_btrace): New.
	(arm_linux_nat_target::disable_btrace): New.
	(arm_linux_nat_target::teardown_btrace): New.
	(arm_linux_nat_target::read_btrace): New.
	(arm_linux_nat_target::btrace_conf): New.
	* aarch64-linux-nat.c (aarch64_linux_nat_target::enable_btrace): New.
	(aarch64_linux_nat_target::disable_btrace): New.
	(aarch64_linux_nat_target::teardown_btrace): New.
	(aarch64_linux_nat_target::read_btrace): New.
	(aarch64_linux_nat_target::btrace_conf): New.
---
 gdb/aarch64-linux-nat.c |  68 +++++++
 gdb/arm-linux-nat.c     |  68 +++++++
 gdb/nat/linux-btrace.c  | 395 ++++++++++++++++++++++++++++++++++++++--
 gdb/nat/linux-btrace.h  |  19 ++
 4 files changed, 537 insertions(+), 13 deletions(-)

diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
index 61224022f6a..4b629e1b3d2 100644
--- a/gdb/aarch64-linux-nat.c
+++ b/gdb/aarch64-linux-nat.c
@@ -35,6 +35,7 @@
 #include "nat/aarch64-linux.h"
 #include "nat/aarch64-linux-hw-point.h"
 #include "nat/aarch64-sve-linux-ptrace.h"
+#include "nat/linux-btrace.h"
 
 #include "elf/external.h"
 #include "elf/common.h"
@@ -88,6 +89,17 @@ class aarch64_linux_nat_target final : public linux_nat_target
   /* Override the GNU/Linux post attach hook.  */
   void post_attach (int pid) override;
 
+  /* Override branch tracing.  */
+  struct btrace_target_info *enable_btrace (ptid_t ptid,
+				const struct btrace_config *conf) override;
+  void disable_btrace (struct btrace_target_info *tinfo) override;
+  void teardown_btrace (struct btrace_target_info *tinfo) override;
+  enum btrace_error read_btrace (struct btrace_data *data,
+				  struct btrace_target_info *btinfo,
+				  enum btrace_read_type type) override;
+  const struct btrace_config *btrace_conf (const struct btrace_target_info *)
+				override;
+
   /* These three defer to common nat/ code.  */
   void low_new_thread (struct lwp_info *lp) override
   { aarch64_linux_new_thread (lp); }
@@ -714,6 +726,62 @@ aarch64_linux_nat_target::post_attach (int pid)
   linux_nat_target::post_attach (pid);
 }
 
+/* Enable branch tracing.  */
+
+struct btrace_target_info *
+aarch64_linux_nat_target::enable_btrace (ptid_t ptid,
+					  const struct btrace_config *conf)
+{
+  struct btrace_target_info *tinfo = nullptr;
+  try
+    {
+      tinfo = linux_enable_btrace (ptid, conf);
+    }
+  catch (const gdb_exception_error &exception)
+    {
+      error (_("Could not enable branch tracing for %s: %s"),
+	     target_pid_to_str (ptid).c_str (), exception.what ());
+    }
+
+  return tinfo;
+}
+
+/* Disable branch tracing.  */
+
+void
+aarch64_linux_nat_target::disable_btrace (struct btrace_target_info *tinfo)
+{
+  enum btrace_error errcode = linux_disable_btrace (tinfo);
+
+  if (errcode != BTRACE_ERR_NONE)
+    error (_("Could not disable branch tracing."));
+}
+
+/* Teardown branch tracing.  */
+
+void
+aarch64_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)
+{
+  /* Ignore errors.  */
+  linux_disable_btrace (tinfo);
+}
+
+enum btrace_error
+aarch64_linux_nat_target::read_btrace (struct btrace_data *data,
+				       struct btrace_target_info *btinfo,
+				       enum btrace_read_type type)
+{
+  return linux_read_btrace (data, btinfo, type);
+}
+
+/* See to_btrace_conf in target.h.  */
+
+const struct btrace_config *
+aarch64_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)
+{
+  return linux_btrace_conf (btinfo);
+}
+
 /* Implement the "read_description" target_ops method.  */
 
 const struct target_desc *
diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c
index 880ac0da044..32631b33328 100644
--- a/gdb/arm-linux-nat.c
+++ b/gdb/arm-linux-nat.c
@@ -39,6 +39,7 @@
 #include <sys/procfs.h>
 
 #include "nat/linux-ptrace.h"
+#include "nat/linux-btrace.h"
 #include "linux-tdep.h"
 
 /* Prototypes for supply_gregset etc.  */
@@ -95,6 +96,17 @@ class arm_linux_nat_target final : public linux_nat_target
 
   const struct target_desc *read_description () override;
 
+  /* Override branch tracing.  */
+  struct btrace_target_info *enable_btrace (ptid_t ptid,
+				const struct btrace_config *conf) override;
+  void disable_btrace (struct btrace_target_info *tinfo) override;
+  void teardown_btrace (struct btrace_target_info *tinfo) override;
+  enum btrace_error read_btrace (struct btrace_data *data,
+				  struct btrace_target_info *btinfo,
+				  enum btrace_read_type type) override;
+  const struct btrace_config *btrace_conf (const struct btrace_target_info *)
+				override;
+
   /* Override linux_nat_target low methods.  */
 
   /* Handle thread creation and exit.  */
@@ -1190,6 +1202,62 @@ arm_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr,
   return start <= addr && start + length - 1 >= addr;
 }
 
+/* Enable branch tracing.  */
+
+struct btrace_target_info *
+arm_linux_nat_target::enable_btrace (ptid_t ptid,
+				     const struct btrace_config *conf)
+{
+  struct btrace_target_info *tinfo = nullptr;
+  try
+    {
+      tinfo = linux_enable_btrace (ptid, conf);
+    }
+  catch (const gdb_exception_error &exception)
+    {
+      error (_("Could not enable branch tracing for %s: %s"),
+	     target_pid_to_str (ptid).c_str (), exception.what ());
+    }
+
+  return tinfo;
+}
+
+/* Disable branch tracing.  */
+
+void
+arm_linux_nat_target::disable_btrace (struct btrace_target_info *tinfo)
+{
+  enum btrace_error errcode = linux_disable_btrace (tinfo);
+
+  if (errcode != BTRACE_ERR_NONE)
+    error (_("Could not disable branch tracing."));
+}
+
+/* Teardown branch tracing.  */
+
+void
+arm_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)
+{
+  /* Ignore errors.  */
+  linux_disable_btrace (tinfo);
+}
+
+enum btrace_error
+arm_linux_nat_target::read_btrace (struct btrace_data *data,
+				   struct btrace_target_info *btinfo,
+				   enum btrace_read_type type)
+{
+  return linux_read_btrace (data, btinfo, type);
+}
+
+/* See to_btrace_conf in target.h.  */
+
+const struct btrace_config *
+arm_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)
+{
+  return linux_btrace_conf (btinfo);
+}
+
 /* Handle thread creation.  We need to copy the breakpoints and watchpoints
    in the parent thread to the child thread.  */
 void
diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c
index 324f7ef0407..9ef182d774a 100644
--- a/gdb/nat/linux-btrace.c
+++ b/gdb/nat/linux-btrace.c
@@ -32,6 +32,10 @@
 
 #include <sys/syscall.h>
 
+#if defined (HAVE_LIBOPENCSD_C_API)
+#  include <opencsd/ocsd_if_types.h>
+#endif
+
 #if HAVE_LINUX_PERF_EVENT_H && defined(SYS_perf_event_open)
 #include <unistd.h>
 #include <sys/mman.h>
@@ -483,10 +487,11 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf)
   scoped_fd fd (syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0));
   if (fd.get () < 0)
     diagnose_perf_event_open_fail ();
+  long page_size = sysconf (_SC_PAGESIZE);
 
   /* Convert the requested size in bytes to pages (rounding up).  */
-  pages = ((size_t) conf->size / PAGE_SIZE
-	   + ((conf->size % PAGE_SIZE) == 0 ? 0 : 1));
+  pages = ((size_t) conf->size / page_size
+	   + ((conf->size % page_size) == 0 ? 0 : 1));
   /* We need at least one page.  */
   if (pages == 0)
     pages = 1;
@@ -505,17 +510,17 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf)
       size_t length;
       __u64 data_size;
 
-      data_size = (__u64) pages * PAGE_SIZE;
+      data_size = (__u64) pages * page_size;
 
       /* Don't ask for more than we can represent in the configuration.  */
       if ((__u64) UINT_MAX < data_size)
 	continue;
 
       size = (size_t) data_size;
-      length = size + PAGE_SIZE;
+      length = size + page_size;
 
       /* Check for overflows.  */
-      if ((__u64) length != data_size + PAGE_SIZE)
+      if ((__u64) length != data_size + page_size)
 	continue;
 
       errno = 0;
@@ -530,7 +535,7 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf)
 
   struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)
     data.get ();
-  data_offset = PAGE_SIZE;
+  data_offset = page_size;
 
 #if defined (PERF_ATTR_SIZE_VER5)
   if (offsetof (struct perf_event_mmap_page, data_size) <= header->size)
@@ -613,7 +618,8 @@ linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
     diagnose_perf_event_open_fail ();
 
   /* Allocate the configuration page. */
-  scoped_mmap data (nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
+  long page_size = sysconf (_SC_PAGESIZE);
+  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
 		    fd.get (), 0);
   if (data.get () == MAP_FAILED)
     error (_("Failed to map trace user page: %s."), safe_strerror (errno));
@@ -624,8 +630,8 @@ linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
   header->aux_offset = header->data_offset + header->data_size;
 
   /* Convert the requested size in bytes to pages (rounding up).  */
-  pages = ((size_t) conf->size / PAGE_SIZE
-	   + ((conf->size % PAGE_SIZE) == 0 ? 0 : 1));
+  pages = ((size_t) conf->size / page_size
+	   + ((conf->size % page_size) == 0 ? 0 : 1));
   /* We need at least one page.  */
   if (pages == 0)
     pages = 1;
@@ -644,7 +650,7 @@ linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
       size_t length;
       __u64 data_size;
 
-      data_size = (__u64) pages * PAGE_SIZE;
+      data_size = (__u64) pages * page_size;
 
       /* Don't ask for more than we can represent in the configuration.  */
       if ((__u64) UINT_MAX < data_size)
@@ -689,6 +695,154 @@ linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
 
 #endif /* !defined (PERF_ATTR_SIZE_VER5) */
 
+/* Determine the etm event type.  */
+
+static int
+perf_event_etm_event_type ()
+{
+  static const char filename[] = "/sys/bus/event_source/devices/cs_etm/type";
+
+  errno = 0;
+  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
+  if (file.get () == nullptr)
+    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
+
+  int type, found = fscanf (file.get (), "%d", &type);
+  if (found != 1)
+    error (_("Failed to read the ETM event type from %s."), filename);
+
+  return type;
+}
+
+/* Get the sink hash to use in the event attributes.  */
+
+static unsigned int
+perf_event_etm_event_sink (const struct btrace_config_etm *conf)
+{
+  char filename[PATH_MAX];
+
+  snprintf (filename, PATH_MAX,
+	    "/sys/bus/event_source/devices/cs_etm/sinks/%s", conf->sink);
+  errno = 0;
+  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
+  if (file.get () == nullptr)
+    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
+
+  unsigned int sink;
+  int  found = fscanf (file.get (), "0x%x", &sink);
+  if (found != 1)
+    error (_("Failed to read the ETM sink from %s."), filename);
+
+  return sink;
+}
+
+/* Enable ARM CoreSight ETM tracing.  */
+
+static struct btrace_target_info *
+linux_enable_etm (ptid_t ptid, const struct btrace_config_etm *conf)
+{
+  struct btrace_tinfo_etm *etm;
+  size_t pages;
+  int pid, pg;
+
+  pid = ptid.lwp ();
+  if (pid == 0)
+    pid = ptid.pid ();
+
+  gdb::unique_xmalloc_ptr<btrace_target_info> tinfo
+    (XCNEW (btrace_target_info));
+  tinfo->ptid = ptid;
+
+  tinfo->conf.format = BTRACE_FORMAT_ETM;
+  etm = &tinfo->variant.etm;
+
+  etm->attr.type = perf_event_etm_event_type ();
+  etm->attr.size = sizeof (etm->attr);
+
+  etm->attr.sample_type = PERF_SAMPLE_CPU;
+  etm->attr.read_format = PERF_FORMAT_ID;
+  etm->attr.sample_id_all = 1;
+  etm->attr.enable_on_exec = 1;
+  etm->attr.exclude_kernel = 1;
+  etm->attr.exclude_hv = 1;
+  etm->attr.exclude_idle = 1;
+  if (conf->sink != nullptr)
+    {
+      if (strcmp (conf->sink, "default") != 0)
+	etm->attr.config2 = perf_event_etm_event_sink (conf);
+    }
+
+  errno = 0;
+  scoped_fd fd (syscall (SYS_perf_event_open, &etm->attr, pid, -1, -1, 0));
+  if (fd.get () < 0)
+    diagnose_perf_event_open_fail ();
+
+  /* Allocate the configuration page.  */
+  long page_size = sysconf (_SC_PAGESIZE);
+  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
+		    fd.get (), 0);
+  if (data.get () == MAP_FAILED)
+    error (_("Failed to map trace user page: %s."), safe_strerror (errno));
+
+  struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)
+    data.get ();
+
+  header->aux_offset = header->data_offset + header->data_size;
+  /* Convert the requested size in bytes to pages (rounding up).  */
+  pages = ((size_t) conf->size / page_size
+	   + ((conf->size % page_size) == 0 ? 0 : 1));
+  /* We need at least one page.  */
+  if (pages == 0)
+    pages = 1;
+
+  /* The buffer size can be requested in powers of two pages.  Adjust PAGES
+     to the next power of two.  */
+  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
+    if ((pages & ((size_t) 1 << pg)) != 0)
+      pages += ((size_t) 1 << pg);
+
+  /* We try to allocate the requested size.
+     If that fails, try to get as much as we can.  */
+  scoped_mmap aux;
+  for (; pages > 0; pages >>= 1)
+    {
+      size_t length;
+      __u64 data_size;
+      data_size = (__u64) pages * page_size;
+
+      /* Don't ask for more than we can represent in the configuration.  */
+      if ((__u64) UINT_MAX < data_size)
+	continue;
+
+      length = (size_t) data_size;
+
+      /* Check for overflows.  */
+      if ((__u64) length != data_size)
+	continue;
+
+      header->aux_size = data_size;
+
+      errno = 0;
+      aux.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (),
+		 header->aux_offset);
+      if (aux.get () != MAP_FAILED)
+	break;
+    }
+  if (pages == 0)
+    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));
+
+  etm->etm.size = aux.size ();
+  etm->etm.mem = (const uint8_t *) aux.release ();
+  etm->etm.data_head = &header->aux_head;
+  etm->etm.last_head = header->aux_tail;
+  etm->header = (struct perf_event_mmap_page *) data.release ();
+  gdb_assert (etm->header == header);
+  etm->file = fd.release ();
+
+  tinfo->conf.etm.size = (unsigned int) etm->etm.size;
+  return tinfo.release ();
+}
+
 /* See linux-btrace.h.  */
 
 struct btrace_target_info *
@@ -707,6 +861,10 @@ linux_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
 
     case BTRACE_FORMAT_PT:
       return linux_enable_pt (ptid, &conf->pt);
+
+    case BTRACE_FORMAT_ETM:
+      return linux_enable_etm (ptid, &conf->etm);
+
     }
 }
 
@@ -715,7 +873,8 @@ linux_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
 static enum btrace_error
 linux_disable_bts (struct btrace_tinfo_bts *tinfo)
 {
-  munmap((void *) tinfo->header, tinfo->bts.size + PAGE_SIZE);
+  long page_size = sysconf (_SC_PAGESIZE);
+  munmap ((void *) tinfo->header, tinfo->bts.size + page_size);
   close (tinfo->file);
 
   return BTRACE_ERR_NONE;
@@ -726,8 +885,22 @@ linux_disable_bts (struct btrace_tinfo_bts *tinfo)
 static enum btrace_error
 linux_disable_pt (struct btrace_tinfo_pt *tinfo)
 {
-  munmap((void *) tinfo->pt.mem, tinfo->pt.size);
-  munmap((void *) tinfo->header, PAGE_SIZE);
+  long page_size = sysconf (_SC_PAGESIZE);
+  munmap ((void *) tinfo->pt.mem, tinfo->pt.size);
+  munmap ((void *) tinfo->header, page_size);
+  close (tinfo->file);
+
+  return BTRACE_ERR_NONE;
+}
+
+/* Disable ARM CoreSight ETM tracing.  */
+
+static enum btrace_error
+linux_disable_etm (struct btrace_tinfo_etm *tinfo)
+{
+  long page_size = sysconf (_SC_PAGESIZE);
+  munmap ((void *) tinfo->etm.mem, tinfo->etm.size);
+  munmap ((void *) tinfo->header, page_size);
   close (tinfo->file);
 
   return BTRACE_ERR_NONE;
@@ -753,6 +926,10 @@ linux_disable_btrace (struct btrace_target_info *tinfo)
     case BTRACE_FORMAT_PT:
       errcode = linux_disable_pt (&tinfo->variant.pt);
       break;
+
+    case BTRACE_FORMAT_ETM:
+      errcode = linux_disable_etm (&tinfo->variant.etm);
+      break;
     }
 
   if (errcode == BTRACE_ERR_NONE)
@@ -898,6 +1075,190 @@ linux_read_pt (struct btrace_data_pt *btrace,
   internal_error (__FILE__, __LINE__, _("Unknown btrace read type."));
 }
 
+/* Return the number of CPUs that are present.  */
+
+static int
+get_cpu_count (void)
+{
+  static const char filename[] = "/sys/devices/system/cpu/present";
+
+  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
+  if (file.get () == nullptr)
+    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
+
+  fseek (file.get (), 0, SEEK_END);
+  int length = ftell (file.get ());
+  fseek (file.get (), 0, SEEK_SET);
+
+  char *buffer = (char *) xmalloc (length+1);
+
+  length = fread (buffer, 1, length, file.get ());
+  buffer[length]='\0';
+  while ((--length) != 0)
+    {
+      if ((buffer[length] == ',') || (buffer[length] == '-'))
+	{
+	  length++;
+	  break;
+	}
+    }
+
+  int cpu_count;
+  int found = sscanf (&buffer[length], "%d", &cpu_count);
+  if (found < 1)
+    error (_("Failed to get cpu count in %s: %s."),
+	     buffer, safe_strerror (errno));
+
+  cpu_count ++;
+  return (cpu_count);
+}
+
+/* Check if the ETM is an etmv4.  */
+
+static bool
+cs_etm_is_etmv4 (int cpu)
+{
+  char filename[PATH_MAX];
+  snprintf (filename, PATH_MAX,
+	    "/sys/bus/event_source/devices/cs_etm/cpu%d/trcidr/trcidr0", cpu);
+  errno = 0;
+  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
+  if (file.get () == nullptr)
+    return false;
+
+  return true;
+}
+
+/* Get etm configuration register from sys filesystem.  */
+
+static uint32_t
+cs_etm_get_register (int cpu, const char *path)
+{
+  char filename[PATH_MAX];
+
+  /* Get coresight register from sysfs.  */
+  snprintf (filename, PATH_MAX,
+	    "/sys/bus/event_source/devices/cs_etm/cpu%d/%s", cpu, path);
+  errno = 0;
+  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
+  if (file.get () == nullptr)
+    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
+
+  uint32_t val = 0;
+
+  int  found = fscanf (file.get (), "0x%x", &val);
+  if (found != 1)
+    error (_("Failed to read coresight register from %s."), filename);
+  return val;
+}
+
+#define CORESIGHT_ETM_PMU_SEED  0x10
+
+/* Calculate trace_id for this cpu
+   to be kept aligned with coresight-pmu.h.  */
+
+static inline int
+coresight_get_trace_id (int cpu)
+{
+  return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));
+}
+
+/* PTMs ETMIDR[11:8] set to b0011.  */
+#define ETMIDR_PTM_VERSION 0x00000300
+
+/* Collect and fill etm trace parameter.  */
+
+static void
+fill_etm_trace_params (struct cs_etm_trace_params *etm_trace_params, int cpu)
+{
+  if (cs_etm_is_etmv4 (cpu) == true)
+    {
+      etm_trace_params->arch_ver = ARCH_V8;
+      etm_trace_params->core_profile = profile_CortexA;
+      etm_trace_params->protocol = OCSD_PROTOCOL_ETMV4I;
+      /* This is the parameter passed in etm->attr.config in the call to
+	 perf_event_open remapped according to linux/coresight-pmu.h.  */
+      etm_trace_params->etmv4.reg_configr = 0;
+      etm_trace_params->etmv4.reg_idr0
+	 = cs_etm_get_register (cpu, "trcidr/trcidr0");
+      etm_trace_params->etmv4.reg_idr1
+	 = cs_etm_get_register (cpu, "trcidr/trcidr1");
+      etm_trace_params->etmv4.reg_idr2
+	 = cs_etm_get_register (cpu, "trcidr/trcidr2");
+      etm_trace_params->etmv4.reg_idr8
+	 = cs_etm_get_register (cpu, "trcidr/trcidr8");
+      etm_trace_params->etmv4.reg_traceidr = coresight_get_trace_id (cpu);
+    }
+  else
+    {
+      etm_trace_params->arch_ver = ARCH_V7;
+      etm_trace_params->core_profile = profile_CortexA;
+      etm_trace_params->protocol
+	= (etm_trace_params->etmv3.reg_idr & ETMIDR_PTM_VERSION)
+	  == ETMIDR_PTM_VERSION ? OCSD_PROTOCOL_PTM : OCSD_PROTOCOL_ETMV3;
+      etm_trace_params->etmv3.reg_ccer
+	 = cs_etm_get_register (cpu, "mgmt/etmccer");
+      etm_trace_params->etmv3.reg_ctrl
+	 = cs_etm_get_register (cpu, "mgmt/etmcr");
+      etm_trace_params->etmv3.reg_idr
+	 = cs_etm_get_register (cpu, "mgmt/etmidr");
+      etm_trace_params->etmv3.reg_trc_id
+	 = cs_etm_get_register (cpu, "traceid");
+    }
+}
+
+static void
+linux_fill_btrace_etm_config (struct btrace_target_info *tinfo,
+			       struct btrace_data_etm_config *conf)
+{
+
+  cs_etm_trace_params etm_trace_params;
+  conf->cpu_count = get_cpu_count ();
+  conf->etm_trace_params = new std::vector<cs_etm_trace_params>;
+  for (int i = 0; i < conf->cpu_count; i++)
+    {
+      fill_etm_trace_params (&etm_trace_params,i);
+      conf->etm_trace_params->push_back (etm_trace_params);
+    }
+
+  conf->etm_decoder_params.formatted = 1;
+  conf->etm_decoder_params.fsyncs = 0;
+  conf->etm_decoder_params.hsyncs = 0;
+  conf->etm_decoder_params.frame_aligned = 0;
+  conf->etm_decoder_params.reset_on_4x_sync = 1;
+}
+
+static enum btrace_error
+linux_read_etm (struct btrace_data_etm *btrace,
+		struct btrace_target_info *tinfo,
+		enum btrace_read_type type)
+{
+  struct perf_event_buffer *etm;
+  etm = &tinfo->variant.etm.etm;
+
+  linux_fill_btrace_etm_config (tinfo, &btrace->config);
+
+  switch (type)
+    {
+    case BTRACE_READ_DELTA:
+      /* We don't support delta reads.  The data head (i.e. aux_head) wraps
+	 around to stay inside the aux buffer.  */
+      return BTRACE_ERR_NOT_SUPPORTED;
+
+    case BTRACE_READ_NEW:
+      if (!perf_event_new_data (etm))
+	return BTRACE_ERR_NONE;
+
+      /* Fall through.  */
+    case BTRACE_READ_ALL:
+      perf_event_read_all (etm, &(btrace->data),&(btrace->size));
+      return BTRACE_ERR_NONE;
+    }
+
+  internal_error (__FILE__, __LINE__, _("Unkown btrace read type."));
+}
+
+
 /* See linux-btrace.h.  */
 
 enum btrace_error
@@ -924,6 +1285,14 @@ linux_read_btrace (struct btrace_data *btrace,
       btrace->variant.pt.size = 0;
 
       return linux_read_pt (&btrace->variant.pt, tinfo, type);
+
+    case BTRACE_FORMAT_ETM:
+      /* We read btrace in ARM CoreSight ETM Trace format.  */
+      btrace->format = BTRACE_FORMAT_ETM;
+      btrace->variant.etm.data = NULL;
+      btrace->variant.etm.size = 0;
+
+      return linux_read_etm (&btrace->variant.etm, tinfo, type);
     }
 
   internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
diff --git a/gdb/nat/linux-btrace.h b/gdb/nat/linux-btrace.h
index 607182da144..3038d2f45a0 100644
--- a/gdb/nat/linux-btrace.h
+++ b/gdb/nat/linux-btrace.h
@@ -78,6 +78,22 @@ struct btrace_tinfo_pt
   /* The trace perf event buffer.  */
   struct perf_event_buffer pt;
 };
+
+/* Branch trace target information for ARM CoreSight ETM Trace.  */
+struct btrace_tinfo_etm
+{
+  /* The Linux perf_event configuration for collecting the branch trace.  */
+  struct perf_event_attr attr;
+
+  /* The perf event file.  */
+  int file;
+
+  /* The perf event configuration page.  */
+  volatile struct perf_event_mmap_page *header;
+
+  /* The trace perf event buffer.  */
+  struct perf_event_buffer etm;
+};
 #endif /* HAVE_LINUX_PERF_EVENT_H */
 
 /* Branch trace target information per thread.  */
@@ -98,6 +114,9 @@ struct btrace_target_info
 
     /* CONF.FORMAT == BTRACE_FORMAT_PT.  */
     struct btrace_tinfo_pt pt;
+
+    /* CONF.FORMAT == BTRACE_FORMAT_ETM.  */
+    struct btrace_tinfo_etm etm;
   } variant;
 #endif /* HAVE_LINUX_PERF_EVENT_H */
 };
-- 
2.25.1


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v6 5/7] fix issue: gdb hangs in the command following a commad returning with TARGET_WAITKIND_NO_HISTORY
  2021-05-31 21:33 [PATCH v6 0/7] extend branch tracing to use ARM CoreSight traces Zied Guermazi
                   ` (3 preceding siblings ...)
  2021-05-31 21:33 ` [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os Zied Guermazi
@ 2021-05-31 21:33 ` Zied Guermazi
  2021-06-23  8:08   ` Metzger, Markus T
  2021-05-31 21:33 ` [PATCH v6 6/7] add support for coresight btrace via remote protocol Zied Guermazi
  2021-05-31 21:33 ` [PATCH v6 7/7] adapt btrace testcases for arm target Zied Guermazi
  6 siblings, 1 reply; 35+ messages in thread
From: Zied Guermazi @ 2021-05-31 21:33 UTC (permalink / raw)
  To: gdb-patches, markus.t.metzger; +Cc: Zied Guermazi

This patch fixes an issue observed with btrace when replaying the execution.
The issue was observed on ARMv7 processors.
To reproduce the issue, the user needs to replay in forward direction
until he reaches the end of history, then replay backwards
(e.g a reverse-next) and then replay forwards. GDB hangs and the user
can not issue new commands.
This fix keeps the same behaviour of gdb as when TARGET_WAITKIND_NO_HISTORY
is hit on other architectures.

gdb/ChangeLog

	* infrun.c (set_step_over_info): add debug print.
	(handle_inferior_event): clear step over info
	in case TARGET_WAITKIND_NO_HISTORY.
---
 gdb/infrun.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/gdb/infrun.c b/gdb/infrun.c
index e9624d2a9b6..c52c8505983 100644
--- a/gdb/infrun.c
+++ b/gdb/infrun.c
@@ -1311,6 +1311,7 @@ set_step_over_info (const address_space *aspace, CORE_ADDR address,
 		    int nonsteppable_watchpoint_p,
 		    int thread)
 {
+  infrun_debug_printf ("setting step over info");
   step_over_info.aspace = aspace;
   step_over_info.address = address;
   step_over_info.nonsteppable_watchpoint_p = nonsteppable_watchpoint_p;
@@ -5739,7 +5740,7 @@ handle_inferior_event (struct execution_control_state *ecs)
       delete_just_stopped_threads_single_step_breakpoints ();
       ecs->event_thread->suspend.stop_pc
 	= regcache_read_pc (get_thread_regcache (inferior_thread ()));
-
+      clear_step_over_info ();
       if (handle_stop_requested (ecs))
 	return;
 
-- 
2.25.1


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v6 6/7] add support for coresight btrace via remote protocol
  2021-05-31 21:33 [PATCH v6 0/7] extend branch tracing to use ARM CoreSight traces Zied Guermazi
                   ` (4 preceding siblings ...)
  2021-05-31 21:33 ` [PATCH v6 5/7] fix issue: gdb hangs in the command following a commad returning with TARGET_WAITKIND_NO_HISTORY Zied Guermazi
@ 2021-05-31 21:33 ` Zied Guermazi
  2021-06-01 12:08   ` Eli Zaretskii
  2021-06-23 10:59   ` Metzger, Markus T
  2021-05-31 21:33 ` [PATCH v6 7/7] adapt btrace testcases for arm target Zied Guermazi
  6 siblings, 2 replies; 35+ messages in thread
From: Zied Guermazi @ 2021-05-31 21:33 UTC (permalink / raw)
  To: gdb-patches, markus.t.metzger; +Cc: Zied Guermazi

This patch extends the remote protocol to use ARM CoreSight traces for btrace.
This patch adds the capabilities enumeration as well as the required protocol
extentions for configuring traces collection, starting and stopping tracing,
and transferring the traces as well as the parameters needed for decoding them.

gdb/ChangeLog

	* NEWS: list new remote packets for extending btrace
	to support using ARM CoreSight Traces.
	* btrace.c (check_xml_btrace_version): add version 1.1
	(parse_xml_btrace_etm_config_source_config_cpu_etmv4_config): New.
	(parse_xml_btrace_etm_config_source_config_cpu_etmv3_config): New.
	(parse_xml_btrace_etm_config_source_config_cpu_etm_config): New.
	(parse_xml_btrace_etm_config_source_config): New.
	(parse_xml_btrace_etm_config_source_config_end): New
	(parse_xml_btrace_etm_config_sink_config): New.
	(parse_xml_btrace_etm_raw): New.
	(parse_xml_btrace_etm): New.
	(parse_xml_btrace_conf_etm): New.
	* btrace.dtd: add etm data and decoding parameters.
	* btrace-conf.dtd: add etm configuration.
	* remote.c (remote_target::btrace_sync_conf): add etm configuration.
	(remote_target::remote_btrace_maybe_reopen): warn if ETM was disabled.
	(remote_target::enable_btrace): add coresight etm.
	(_initialize_remote): add etm related packets.

gdb/doc/ChangeLog

	* gdb.texinfo (General Query Packets) Document extending GDB remote
	protocol packets to support using ARM CoreSight traces for btrace.

gdbserver/ChangeLog

	* configure.srv: add btrace for aarch64*-*-linux* and arm*-*-linux*.
	* linux-low.c (linux_low_encode_etm_config): New.
	(linux_process_target::read_btrace): encode CoreSight traces
	and related decoding parameters.
	(linux_process_target::read_btrace_conf): encode CoreSight
	configuration.
	* server.cc (handle_btrace_enable_etm): New.
	(handle_btrace_general_set): add etm handling.
	(handle_btrace_conf_general_set): add etm handling.
	(supported_btrace_packets): add etm related packets.
---
 gdb/NEWS                     |  11 ++
 gdb/btrace.c                 | 368 ++++++++++++++++++++++++++++++++++-
 gdb/doc/gdb.texinfo          |  63 +++++-
 gdb/features/btrace-conf.dtd |  10 +-
 gdb/features/btrace.dtd      |  38 +++-
 gdb/remote.c                 |  66 ++++++-
 gdbserver/configure.srv      |   4 +
 gdbserver/linux-low.cc       | 100 +++++++++-
 gdbserver/server.cc          |  34 +++-
 9 files changed, 685 insertions(+), 9 deletions(-)

diff --git a/gdb/NEWS b/gdb/NEWS
index 02035901f6e..ad93def71af 100644
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -209,6 +209,17 @@ ptype[/FLAGS] TYPE | EXPRESSION
 ARM Symbian			arm*-*-symbianelf*
 
 * New remote packets
+Qbtrace:etm
+  Enable ARM CoreSight ETM Trace-based branch tracing for the current
+  process.  The remote stub reports support for this packet to GDB's
+  qSupported query.
+
+Qbtrace-conf:etm:size
+  Set the requested ring buffer size for branch tracing in
+  ARM CoreSight ETM Trace format.
+
+Qbtrace-conf:etm:sink
+  Set the trace sink for branch tracing in ARM CoreSight ETM Trace format.
 
 qMemTags
   Request the remote to send allocation tags for a particular memory range.
diff --git a/gdb/btrace.c b/gdb/btrace.c
index 2676389b63e..c10da71b7cd 100644
--- a/gdb/btrace.c
+++ b/gdb/btrace.c
@@ -2631,7 +2631,7 @@ check_xml_btrace_version (struct gdb_xml_parser *parser,
   const char *version
     = (const char *) xml_find_attribute (attributes, "version")->value.get ();
 
-  if (strcmp (version, "1.0") != 0)
+  if ((strcmp (version, "1.0") != 0) && (strcmp (version, "1.1") != 0))
     gdb_xml_error (parser, _("Unsupported btrace version: \"%s\""), version);
 }
 
@@ -2766,6 +2766,253 @@ parse_xml_btrace_pt (struct gdb_xml_parser *parser,
   btrace->variant.pt.size = 0;
 }
 
+/* Parse a btrace etm "cpu-etm-config-etmv4_config" xml record.  */
+
+static void
+parse_xml_btrace_etm_config_source_config_cpu_etmv4_config (
+				struct gdb_xml_parser *parser,
+				const struct gdb_xml_element *element,
+				void *user_data,
+				std::vector<gdb_xml_value> &attributes)
+{
+  struct btrace_data *btrace;
+  cs_etm_trace_params *etm_trace_params;
+
+  DEBUG ("parse_xml_btrace_etm_config_source_config_cpu_etmv4_config");
+
+  btrace = (struct btrace_data *) user_data;
+  etm_trace_params = & (btrace->variant.etm.config.etm_trace_params->back ());
+
+  struct gdb_xml_value *reg_idr0;
+  reg_idr0
+    = xml_find_attribute (attributes, "reg_idr0");
+
+  struct gdb_xml_value *reg_idr1;
+  reg_idr1
+    = xml_find_attribute (attributes, "reg_idr1");
+
+  struct gdb_xml_value *reg_idr2;
+  reg_idr2
+    = xml_find_attribute (attributes, "reg_idr2");
+
+  struct gdb_xml_value *reg_idr8;
+  reg_idr8
+    = xml_find_attribute (attributes, "reg_idr8");
+
+  struct gdb_xml_value *reg_configr;
+  reg_configr
+    = xml_find_attribute (attributes, "reg_configr");
+
+  struct gdb_xml_value *reg_traceidr;
+  reg_traceidr
+    = xml_find_attribute (attributes, "reg_traceidr");
+
+  etm_trace_params->etmv4.reg_idr0
+    = (unsigned int) *(ULONGEST *) reg_idr0->value.get ();
+  etm_trace_params->etmv4.reg_idr1
+    = (unsigned int) *(ULONGEST *) reg_idr1->value.get ();
+  etm_trace_params->etmv4.reg_idr2
+    = (unsigned int) *(ULONGEST *) reg_idr2->value.get ();
+  etm_trace_params->etmv4.reg_idr8
+    = (unsigned int) *(ULONGEST *) reg_idr8->value.get ();
+  etm_trace_params->etmv4.reg_configr
+    = (unsigned int) *(ULONGEST *) reg_configr->value.get ();
+  etm_trace_params->etmv4.reg_traceidr
+    = (unsigned int) *(ULONGEST *) reg_traceidr->value.get ();
+
+}
+
+/* Parse a btrace etm "cpu-etm-config-etmv3_config" xml record.  */
+
+static void
+parse_xml_btrace_etm_config_source_config_cpu_etmv3_config (
+				struct gdb_xml_parser *parser,
+				const struct gdb_xml_element *element,
+				void *user_data,
+				std::vector<gdb_xml_value> &attributes)
+{
+  struct btrace_data *btrace;
+  cs_etm_trace_params *etm_trace_params;
+
+  DEBUG ("parse_xml_btrace_etm_config_source_config_cpu_etmv3_config");
+
+  btrace = (struct btrace_data *) user_data;
+  etm_trace_params = & (btrace->variant.etm.config.etm_trace_params->back ());
+
+  struct gdb_xml_value *reg_ctrl;
+  reg_ctrl
+    = xml_find_attribute (attributes, "reg_ctrl");
+
+  struct gdb_xml_value *reg_trc_id;
+  reg_trc_id
+    = xml_find_attribute (attributes, "reg_trc_id");
+
+  struct gdb_xml_value *reg_ccer;
+  reg_ccer
+    = xml_find_attribute (attributes, "reg_ccer");
+
+  struct gdb_xml_value *reg_idr;
+  reg_idr
+    = xml_find_attribute (attributes, "reg_idr");
+
+  etm_trace_params->etmv3.reg_ctrl
+    = (unsigned int) *(ULONGEST *) reg_ctrl->value.get ();
+  etm_trace_params->etmv3.reg_trc_id
+    = (unsigned int) *(ULONGEST *) reg_trc_id->value.get ();
+  etm_trace_params->etmv3.reg_ccer
+    = (unsigned int) *(ULONGEST *) reg_ccer->value.get ();
+  etm_trace_params->etmv3.reg_idr
+    = (unsigned int) *(ULONGEST *) reg_idr->value.get ();
+}
+
+
+/* Parse a btrace etm "cpu-etm-config" xml record.  */
+
+static void
+parse_xml_btrace_etm_config_source_config_cpu_etm_config (
+				struct gdb_xml_parser *parser,
+				const struct gdb_xml_element *element,
+				void *user_data,
+				std::vector<gdb_xml_value> &attributes)
+{
+  struct btrace_data *btrace;
+  cs_etm_trace_params etm_trace_params;
+
+  DEBUG ("parse_xml_btrace_etm_config_source_config_cpu_etm_config");
+
+  btrace = (struct btrace_data *) user_data;
+
+  struct gdb_xml_value *arch_ver;
+  arch_ver
+    = xml_find_attribute (attributes, "arch_ver");
+
+  struct gdb_xml_value *core_prof;
+  core_prof
+    = xml_find_attribute (attributes, "core_prof");
+
+  struct gdb_xml_value *protocol;
+  protocol
+    = xml_find_attribute (attributes, "protocol");
+
+  etm_trace_params.arch_ver = (int) *(ULONGEST *) arch_ver->value.get ();
+  etm_trace_params.core_profile = (int) *(ULONGEST *) core_prof->value.get ();
+  etm_trace_params.protocol = (int) *(ULONGEST *) protocol->value.get ();
+
+  btrace->variant.etm.config.etm_trace_params->push_back (etm_trace_params);
+}
+
+/* Parse a btrace etm "source-config" xml record.  */
+
+static void
+parse_xml_btrace_etm_config_source_config (struct gdb_xml_parser *parser,
+				const struct gdb_xml_element *element,
+				void *user_data,
+				std::vector<gdb_xml_value> &attributes)
+{
+  struct btrace_data *btrace;
+  struct gdb_xml_value *trace_id;
+
+  DEBUG ("parse_xml_btrace_etm_config_source_config");
+  btrace = (struct btrace_data *) user_data;
+
+  trace_id = xml_find_attribute (attributes, "trace_id");
+  if (trace_id != NULL)
+    btrace->variant.etm.trace_id
+      = (uint8_t) *(ULONGEST *) trace_id->value.get ();
+  else
+    btrace->variant.etm.trace_id = 0xFF;
+  btrace->variant.etm.config.etm_trace_params
+    = new std::vector<cs_etm_trace_params>;
+}
+
+/* Get the number of cpus.  */
+
+static void
+parse_xml_btrace_etm_config_source_config_end (struct gdb_xml_parser *parser,
+				const struct gdb_xml_element *element,
+				void *user_data, const char *body_text)
+{
+  struct btrace_data *btrace;
+
+  DEBUG ("parse_xml_btrace_etm_config_source_config_end");
+  btrace = (struct btrace_data *) user_data;
+
+  btrace->variant.etm.config.cpu_count
+    = btrace->variant.etm.config.etm_trace_params->size ();
+}
+
+/* Parse a btrace etm "sink-config" xml record.  */
+
+static void
+parse_xml_btrace_etm_config_sink_config (struct gdb_xml_parser *parser,
+				const struct gdb_xml_element *element,
+				void *user_data,
+				std::vector<gdb_xml_value> &attributes)
+{
+  struct btrace_data *btrace;
+
+  DEBUG ("parse_xml_btrace_etm_config_sink_config");
+
+  btrace = (struct btrace_data *) user_data;
+
+  struct gdb_xml_value *formatted;
+  formatted = xml_find_attribute (attributes, "formatted");
+
+  struct gdb_xml_value *fsyncs;
+  fsyncs = xml_find_attribute (attributes, "fsyncs");
+
+  struct gdb_xml_value *hsyncs;
+  hsyncs = xml_find_attribute (attributes, "hsyncs");
+
+  struct gdb_xml_value *frame_aligned;
+  frame_aligned = xml_find_attribute (attributes, "frame_aligned");
+
+  struct gdb_xml_value *reset_on_4x_sync;
+  reset_on_4x_sync = xml_find_attribute (attributes, "reset_on_4x_sync");
+
+  btrace->variant.etm.config.etm_decoder_params.formatted
+    = (uint8_t) *(ULONGEST *) formatted->value.get ();
+  btrace->variant.etm.config.etm_decoder_params.fsyncs
+    = (uint8_t) *(ULONGEST *) fsyncs->value.get ();
+  btrace->variant.etm.config.etm_decoder_params.hsyncs
+    = (uint8_t) *(ULONGEST *) hsyncs->value.get ();
+  btrace->variant.etm.config.etm_decoder_params.frame_aligned
+    = (uint8_t) *(ULONGEST *) frame_aligned->value.get ();
+  btrace->variant.etm.config.etm_decoder_params.reset_on_4x_sync
+    = (uint8_t) *(ULONGEST *) reset_on_4x_sync->value.get ();
+}
+
+
+/* Parse a btrace etm "raw" xml record.  */
+
+static void
+parse_xml_btrace_etm_raw (struct gdb_xml_parser *parser,
+			  const struct gdb_xml_element *element,
+			  void *user_data, const char *body_text)
+{
+  struct btrace_data *btrace;
+  DEBUG ("parse_xml_btrace_etm_raw");
+  btrace = (struct btrace_data *) user_data;
+  parse_xml_raw (parser, body_text, &btrace->variant.etm.data,
+		 &btrace->variant.etm.size);
+}
+
+/* Parse a btrace "etm" xml record.  */
+
+static void
+parse_xml_btrace_etm (struct gdb_xml_parser *parser,
+		      const struct gdb_xml_element *element,
+		      void *user_data,
+		      std::vector<gdb_xml_value> &attributes)
+{
+  struct btrace_data *btrace;
+  DEBUG ("parse_xml_btrace_etm");
+  btrace = (struct btrace_data *) user_data;
+  btrace->format = BTRACE_FORMAT_ETM;
+  btrace->variant.etm.data = NULL;
+  btrace->variant.etm.size = 0;
+}
+
 static const struct gdb_xml_attribute block_attributes[] = {
   { "begin", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
   { "end", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
@@ -2793,6 +3040,91 @@ static const struct gdb_xml_element btrace_pt_children[] = {
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
+static const struct gdb_xml_attribute
+btrace_etm_config_source_config_cpu_config_etmv3_config_attributes[] = {
+  { "reg_ctrl", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "reg_trc_id", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "reg_ccer", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "reg_idr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute
+btrace_etm_config_source_config_cpu_config_etmv4_config_attributes[] = {
+  { "reg_idr0", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "reg_idr1", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "reg_idr2", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "reg_idr8", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "reg_configr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "reg_traceidr", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element
+btrace_etm_config_source_config_cpu_etm_config_children[] = {
+  { "etmv3-config",
+      btrace_etm_config_source_config_cpu_config_etmv3_config_attributes,
+      NULL, GDB_XML_EF_OPTIONAL,
+      parse_xml_btrace_etm_config_source_config_cpu_etmv3_config, NULL },
+  { "etmv4-config",
+      btrace_etm_config_source_config_cpu_config_etmv4_config_attributes,
+      NULL, GDB_XML_EF_OPTIONAL,
+      parse_xml_btrace_etm_config_source_config_cpu_etmv4_config, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute
+btrace_etm_config_source_config_etm_config_attributes[] = {
+  { "cpu_id", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "arch_ver", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "core_prof", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "protocol", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element
+btrace_etm_config_source_config_children[] = {
+  { "cpu-etm-config", btrace_etm_config_source_config_etm_config_attributes,
+      btrace_etm_config_source_config_cpu_etm_config_children,
+      GDB_XML_EF_REPEATABLE,
+      parse_xml_btrace_etm_config_source_config_cpu_etm_config, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute
+btrace_etm_config_sink_config_attributes[] = {
+  { "formatted", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "fsyncs", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "hsyncs", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "frame_aligned", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { "reset_on_4x_sync", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_attribute
+btrace_etm_config_source_config_attributes[] = {
+  { "trace_id", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element btrace_etm_config_children[] = {
+  { "source-config", btrace_etm_config_source_config_attributes,
+      btrace_etm_config_source_config_children, GDB_XML_EF_NONE,
+      parse_xml_btrace_etm_config_source_config,
+      parse_xml_btrace_etm_config_source_config_end },
+  { "sink-config", btrace_etm_config_sink_config_attributes, NULL,
+      GDB_XML_EF_NONE, parse_xml_btrace_etm_config_sink_config, NULL },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
+static const struct gdb_xml_element btrace_etm_children[] = {
+  { "etm-config", NULL, btrace_etm_config_children, GDB_XML_EF_NONE, NULL,
+      NULL },
+  { "raw", NULL, NULL, GDB_XML_EF_OPTIONAL, NULL,
+      parse_xml_btrace_etm_raw },
+  { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
+};
+
 static const struct gdb_xml_attribute btrace_attributes[] = {
   { "version", GDB_XML_AF_NONE, NULL, NULL },
   { NULL, GDB_XML_AF_NONE, NULL, NULL }
@@ -2803,6 +3135,8 @@ static const struct gdb_xml_element btrace_children[] = {
     GDB_XML_EF_REPEATABLE | GDB_XML_EF_OPTIONAL, parse_xml_btrace_block, NULL },
   { "pt", NULL, btrace_pt_children, GDB_XML_EF_OPTIONAL, parse_xml_btrace_pt,
     NULL },
+  { "etm", NULL, btrace_etm_children, GDB_XML_EF_OPTIONAL,
+    parse_xml_btrace_etm, NULL },
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
@@ -2883,6 +3217,30 @@ parse_xml_btrace_conf_pt (struct gdb_xml_parser *parser,
     conf->pt.size = (unsigned int) *(ULONGEST *) size->value.get ();
 }
 
+/* Parse a btrace-conf "etm" xml record.  */
+
+static void
+parse_xml_btrace_conf_etm (struct gdb_xml_parser *parser,
+			   const struct gdb_xml_element *element,
+			   void *user_data,
+			   std::vector<gdb_xml_value> &attributes)
+{
+  DEBUG ("parse_xml_btrace_conf_etm");
+
+  struct btrace_config *conf = (struct btrace_config *) user_data;
+  conf->format = BTRACE_FORMAT_ETM;
+  conf->etm.size = 0;
+
+  struct gdb_xml_value *size = xml_find_attribute (attributes, "size");
+  if (size != nullptr)
+    conf->etm.size = (unsigned int) *(ULONGEST *) size->value.get ();
+
+  struct gdb_xml_value *sink;
+  sink = xml_find_attribute (attributes, "sink");
+  if (sink != nullptr)
+    conf->etm.sink = (char*) sink->value.get ();
+}
+
 static const struct gdb_xml_attribute btrace_conf_pt_attributes[] = {
   { "size", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
   { NULL, GDB_XML_AF_NONE, NULL, NULL }
@@ -2893,11 +3251,19 @@ static const struct gdb_xml_attribute btrace_conf_bts_attributes[] = {
   { NULL, GDB_XML_AF_NONE, NULL, NULL }
 };
 
+static const struct gdb_xml_attribute btrace_conf_etm_attributes[] = {
+  { "size", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
+  { "sink", GDB_XML_AF_OPTIONAL, NULL, NULL },
+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
+};
+
 static const struct gdb_xml_element btrace_conf_children[] = {
   { "bts", btrace_conf_bts_attributes, NULL, GDB_XML_EF_OPTIONAL,
     parse_xml_btrace_conf_bts, NULL },
   { "pt", btrace_conf_pt_attributes, NULL, GDB_XML_EF_OPTIONAL,
     parse_xml_btrace_conf_pt, NULL },
+  { "etm", btrace_conf_etm_attributes, NULL, GDB_XML_EF_OPTIONAL,
+    parse_xml_btrace_conf_etm, NULL },
   { NULL, NULL, NULL, GDB_XML_EF_NONE, NULL, NULL }
 };
 
diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index b9b09ff97f2..83ddbc28aa5 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -42072,6 +42072,20 @@ These are the currently defined stub features and their properties:
 @tab @samp{-}
 @tab No
 
+@item @samp{Qbtrace:etm}
+@tab Yes
+@tab @samp{-}
+@tab Yes
+
+@item @samp{Qbtrace-conf:etm:size}
+@tab Yes
+@tab @samp{-}
+@tab Yes
+
+@item @samp{Qbtrace-conf:etm:sink}
+@tab Yes
+@tab @samp{-}
+@tab Yes
 @end multitable
 
 These are the currently defined stub features, in more detail:
@@ -42248,9 +42262,11 @@ The remote stub understands the @samp{Qbtrace:off} packet.
 
 @item Qbtrace:bts
 The remote stub understands the @samp{Qbtrace:bts} packet.
+(@pxref{bts}).
 
 @item Qbtrace:pt
 The remote stub understands the @samp{Qbtrace:pt} packet.
+(@pxref{pt}).
 
 @item Qbtrace-conf:bts:size
 The remote stub understands the @samp{Qbtrace-conf:bts:size} packet.
@@ -42286,7 +42302,6 @@ The remote stub understands the @samp{QThreadEvents} packet.
 @item no-resumed
 The remote stub reports the @samp{N} stop reply.
 
-
 @item memory-tagging
 The remote stub supports and implements the required memory tagging
 functionality and understands the @samp{qMemTags} (@pxref{qMemTags}) and
@@ -42296,6 +42311,15 @@ For AArch64 GNU/Linux systems, this feature also requires access to the
 @file{/proc/@var{pid}/smaps} file so memory mapping page flags can be inspected.
 This is done via the @samp{vFile} requests.
 
+@item Qbtrace:etm
+The remote stub understands the @samp{Qbtrace:etm} packet.
+(@pxref{etm}).
+
+@item Qbtrace-conf:etm:size
+The remote stub understands the @samp{Qbtrace-conf:etm:size} packet.
+
+@item Qbtrace-conf:etm:sink
+The remote stub understands the @samp{Qbtrace-conf:etm:sink} packet.
 @end table
 
 @item qSymbol::
@@ -42702,6 +42726,7 @@ A badly formed request or an error was encountered.
 @end table
 
 @item Qbtrace:bts
+@anchor{bts}
 Enable branch tracing for the current thread using Branch Trace Store.
 
 Reply:
@@ -42713,6 +42738,7 @@ A badly formed request or an error was encountered.
 @end table
 
 @item Qbtrace:pt
+@anchor{pt}
 Enable branch tracing for the current thread using Intel Processor Trace.
 
 Reply:
@@ -42758,6 +42784,41 @@ The ring buffer size has been set.
 A badly formed request or an error was encountered.
 @end table
 
+@item Qbtrace:etm
+@anchor{etm}
+Enable branch tracing for the current thread using ARM CoreSight ETM Trace.
+
+Reply:
+@table @samp
+@item OK
+Branch tracing has been enabled.
+@item E.errtext
+A badly formed request or an error was encountered.
+@end table
+
+@item Qbtrace-conf:etm:size=@var{value}
+Set the requested ring buffer size for new threads that use the
+btrace recording method in etm format.
+
+Reply:
+@table @samp
+@item OK
+The ring buffer size has been set.
+@item E.errtext
+A badly formed request or an error was encountered.
+@end table
+
+@item Qbtrace-conf:etm:sink=@var{value}
+Set the requested trace sink for new threads that use the
+btrace recording method in etm format.
+
+Reply:
+@table @samp
+@item OK
+The ring buffer size has been set.
+@item E.errtext
+A badly formed request or an error was encountered.
+@end table
 @end table
 
 @node Architecture-Specific Protocol Details
diff --git a/gdb/features/btrace-conf.dtd b/gdb/features/btrace-conf.dtd
index 4b060bb408c..7334d035f34 100644
--- a/gdb/features/btrace-conf.dtd
+++ b/gdb/features/btrace-conf.dtd
@@ -4,11 +4,17 @@
      are permitted in any medium without royalty provided the copyright
      notice and this notice are preserved.  -->
 
-<!ELEMENT btrace-conf	(bts?, pt?)>
-<!ATTLIST btrace-conf	version	CDATA	#FIXED "1.0">
+<!ELEMENT btrace-conf	(bts?, pt?, etm?)>
+<!ATTLIST btrace-conf	version	(1.0|1.1) #REQUIRED>
 
 <!ELEMENT bts	EMPTY>
 <!ATTLIST bts	size	CDATA	#IMPLIED>
 
 <!ELEMENT pt	EMPTY>
 <!ATTLIST pt	size	CDATA	#IMPLIED>
+
+<!ELEMENT etm	EMPTY>
+<!ATTLIST etm   size    CDATA   #IMPLIED
+                sink	CDATA   #IMPLIED
+                filter  CDATA   #IMPLIED
+                config  CDATA   #IMPLIED>
diff --git a/gdb/features/btrace.dtd b/gdb/features/btrace.dtd
index 9bb930d7d3b..001b77f35d0 100644
--- a/gdb/features/btrace.dtd
+++ b/gdb/features/btrace.dtd
@@ -4,8 +4,8 @@
      are permitted in any medium without royalty provided the copyright
      notice and this notice are preserved.  -->
 
-<!ELEMENT btrace  (block* | pt)>
-<!ATTLIST btrace  version CDATA   #FIXED "1.0">
+<!ELEMENT btrace  (block* | pt | etm)>
+<!ATTLIST btrace  version (1.0|1.1) #REQUIRED>
 
 <!ELEMENT block        EMPTY>
 <!ATTLIST block        begin  CDATA   #REQUIRED
@@ -21,4 +21,38 @@
               model    CDATA #REQUIRED
               stepping CDATA #REQUIRED>
 
+<!ELEMENT etm (etm-config?, raw?)>
+
+<!ELEMENT etm-config (source-config, sink-config)>
+
+<!ELEMENT source-config   (cpu-etm-config+)>
+<!ATTLIST source-config    trace_id         CDATA #IMPLIED>
+
+<!ELEMENT cpu-etm-config  (etmv3-config|etmv4-config)>
+<!ATTLIST cpu-etm-config   cpu_id           CDATA #REQUIRED
+                           arch_ver         CDATA #REQUIRED
+                           core_prof        CDATA #REQUIRED
+                           protocol         CDATA #REQUIRED>
+
+<!ELEMENT etmv3-config     EMPTY>
+<!ATTLIST etmv3-config     reg_ctrl         CDATA #REQUIRED
+                           reg_trc_id       CDATA #REQUIRED
+                           reg_ccer         CDATA #REQUIRED
+                           reg_idr          CDATA #REQUIRED>
+
+<!ELEMENT etmv4-config     EMPTY>
+<!ATTLIST etmv4-config     reg_idr0         CDATA #REQUIRED
+                           reg_idr1         CDATA #REQUIRED
+                           reg_idr2         CDATA #REQUIRED
+                           reg_idr8         CDATA #REQUIRED
+                           reg_configr      CDATA #REQUIRED
+                           reg_traceidr     CDATA #REQUIRED>
+
+<!ELEMENT sink-config      EMPTY>
+<!ATTLIST sink-config      formatted         CDATA #REQUIRED
+                           fsyncs            CDATA #REQUIRED
+                           hsyncs            CDATA #REQUIRED
+                           frame_aligned     CDATA #REQUIRED
+                           reset_on_4x_sync  CDATA #REQUIRED>
+
 <!ELEMENT raw (#PCDATA)>
diff --git a/gdb/remote.c b/gdb/remote.c
index 9b465d77343..2defaa4503e 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -2184,6 +2184,15 @@ enum {
      packets and the tag violation stop replies.  */
   PACKET_memory_tagging_feature,
 
+  /* Support for the Qbtrace-etm packet.  */
+  PACKET_Qbtrace_etm,
+
+  /* Support for the Qbtrace-conf:etm:size packet.  */
+  PACKET_Qbtrace_conf_etm_size,
+
+  /* Support for the Qbtrace-conf:etm:sink packet.  */
+  PACKET_Qbtrace_conf_etm_sink,
+
   PACKET_MAX
 };
 
@@ -5333,6 +5342,12 @@ static const struct protocol_feature remote_protocol_features[] = {
   { "no-resumed", PACKET_DISABLE, remote_supported_packet, PACKET_no_resumed },
   { "memory-tagging", PACKET_DISABLE, remote_supported_packet,
     PACKET_memory_tagging_feature },
+  { "Qbtrace:etm", PACKET_DISABLE, remote_supported_packet,
+    PACKET_Qbtrace_etm },
+  { "Qbtrace-conf:etm:size", PACKET_DISABLE, remote_supported_packet,
+    PACKET_Qbtrace_conf_etm_size },
+  { "Qbtrace-conf:etm:sink", PACKET_DISABLE, remote_supported_packet,
+    PACKET_Qbtrace_conf_etm_sink },
 };
 
 static char *remote_support_xml;
@@ -14020,6 +14035,28 @@ remote_target::btrace_sync_conf (const btrace_config *conf)
 
       rs->btrace_config.pt.size = conf->pt.size;
     }
+
+  packet = &remote_protocol_packets[PACKET_Qbtrace_conf_etm_size];
+  if (packet_config_support (packet) == PACKET_ENABLE
+      && conf->etm.size != rs->btrace_config.etm.size)
+    {
+      pos = buf;
+      pos += xsnprintf (pos, endbuf - pos, "%s=0x%x", packet->name,
+                        conf->etm.size);
+
+      putpkt (buf);
+      getpkt (&rs->buf, 0);
+
+      if (packet_ok (buf, packet) == PACKET_ERROR)
+	{
+	  if (buf[0] == 'E' && buf[1] == '.')
+	    error (_("Failed to configure the trace buffer size: %s"), buf + 2);
+	  else
+	    error (_("Failed to configure the trace buffer size."));
+	}
+
+      rs->btrace_config.etm.size = conf->etm.size;
+    }
 }
 
 /* Read the current thread's btrace configuration from the target and
@@ -14042,7 +14079,7 @@ remote_target::remote_btrace_maybe_reopen ()
 {
   struct remote_state *rs = get_remote_state ();
   int btrace_target_pushed = 0;
-#if !defined (HAVE_LIBIPT)
+#if !defined (HAVE_LIBIPT) || !defined (HAVE_LIBOPENCSD_C_API)
   int warned = 0;
 #endif
 
@@ -14077,6 +14114,20 @@ remote_target::remote_btrace_maybe_reopen ()
 	}
 #endif /* !defined (HAVE_LIBIPT) */
 
+#if !defined (HAVE_LIBOPENCSD_C_API)
+      if (rs->btrace_config.format == BTRACE_FORMAT_ETM)
+	{
+	  if (!warned)
+	    {
+	      warned = 1;
+	      warning (_("Target is recording using ARM CoreSight Processor "
+			 "Trace but support was disabled at compile time."));
+	    }
+
+	  continue;
+	}
+#endif /* !defined (HAVE_LIBOPENCSD_C_API) */
+
       /* Push target, once, but before anything else happens.  This way our
 	 changes to the threads will be cleaned up by unpushing the target
 	 in case btrace_read_config () throws.  */
@@ -14114,6 +14165,10 @@ remote_target::enable_btrace (ptid_t ptid, const struct btrace_config *conf)
       case BTRACE_FORMAT_PT:
 	packet = &remote_protocol_packets[PACKET_Qbtrace_pt];
 	break;
+
+      case BTRACE_FORMAT_ETM:
+	packet = &remote_protocol_packets[PACKET_Qbtrace_etm];
+	break;
     }
 
   if (packet == NULL || packet_config_support (packet) != PACKET_ENABLE)
@@ -15222,6 +15277,15 @@ Show the maximum size of the address (in bits) in a memory packet."), NULL,
   add_packet_config_cmd (&remote_protocol_packets[PACKET_memory_tagging_feature],
 			 "memory-tagging-feature", "memory-tagging-feature", 0);
 
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_etm],
+			 "Qbtrace:etm", "enable-btrace-etm", 0);
+
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_conf_etm_size],
+			 "Qbtrace-conf:etm:size", "btrace-conf-etm-size", 0);
+
+  add_packet_config_cmd (&remote_protocol_packets[PACKET_Qbtrace_conf_etm_sink],
+			 "Qbtrace-conf:etm:sink", "btrace-conf-etm-sink", 0);
+
   /* Assert that we've registered "set remote foo-packet" commands
      for all packet configs.  */
   {
diff --git a/gdbserver/configure.srv b/gdbserver/configure.srv
index 971f537bffa..0c3a33a6975 100644
--- a/gdbserver/configure.srv
+++ b/gdbserver/configure.srv
@@ -51,12 +51,14 @@ case "${gdbserver_host}" in
 			srv_tgtobj="$srv_tgtobj linux-aarch64-tdesc.o"
 			srv_tgtobj="$srv_tgtobj nat/aarch64-mte-linux-ptrace.o"
 			srv_tgtobj="$srv_tgtobj nat/aarch64-sve-linux-ptrace.o"
+			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} $srv_linux_obj"
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
 			ipa_obj="linux-aarch64-ipa.o"
 			ipa_obj="${ipa_obj} linux-aarch64-tdesc-ipa.o"
 			ipa_obj="${ipa_obj} arch/aarch64-ipa.o"
+			srv_linux_btrace=yes
 			;;
   aarch64*-*-netbsd*)	srv_regobj=""
 			srv_tgtobj="netbsd-low.o netbsd-aarch64-low.o fork-child.o"
@@ -79,6 +81,7 @@ case "${gdbserver_host}" in
 			srv_tgtobj="$srv_tgtobj linux-arm-tdesc.o"
 			srv_tgtobj="$srv_tgtobj linux-aarch32-low.o"
 			srv_tgtobj="$srv_tgtobj linux-aarch32-tdesc.o"
+			srv_tgtobj="${srv_tgtobj} nat/linux-btrace.o"
 			srv_tgtobj="${srv_tgtobj} arch/aarch32.o"
 			srv_tgtobj="${srv_tgtobj} arch/arm.o"
 			srv_tgtobj="${srv_tgtobj} arch/arm-linux.o"
@@ -86,6 +89,7 @@ case "${gdbserver_host}" in
 			srv_linux_usrregs=yes
 			srv_linux_regsets=yes
 			srv_linux_thread_db=yes
+			srv_linux_btrace=yes
 			;;
   i[34567]86-*-cygwin*)	srv_regobj=""
 			srv_tgtobj="x86-low.o nat/x86-dregs.o win32-low.o"
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 5c6191d941c..6c4989bdb2c 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -51,6 +51,9 @@
 #include "gdbsupport/environ.h"
 #include "gdbsupport/gdb-sigmask.h"
 #include "gdbsupport/scoped_restore.h"
+#if defined (HAVE_LIBOPENCSD_C_API)
+#  include <opencsd/ocsd_if_types.h>
+#endif
 #ifndef ELFMAG0
 /* Don't include <linux/elf.h> here.  If it got included by gdb_proc_service.h
    then ELFMAG0 will have been defined.  If it didn't get included by
@@ -6983,6 +6986,73 @@ linux_low_encode_pt_config (struct buffer *buffer,
   buffer_grow_str (buffer, "</pt-config>\n");
 }
 
+/* Encode ARM CoreSight Processor Trace configuration.  */
+
+static void
+linux_low_encode_etm_config (struct buffer *buffer,
+			    const struct btrace_data_etm_config *config)
+{
+  int architecture;
+  buffer_grow_str (buffer, "<etm-config>\n");
+  buffer_grow_str (buffer, "<source-config>\n");
+  for (int i=0; i< config->cpu_count;i++)
+  {
+    if ((config->etm_trace_params->at (i).protocol == OCSD_PROTOCOL_ETMV3)
+	||(config->etm_trace_params->at (i).protocol == OCSD_PROTOCOL_PTM))
+      {
+	architecture = ARCH_V7;
+      }
+    else if (config->etm_trace_params->at (i).protocol == OCSD_PROTOCOL_ETMV4I)
+      {
+	architecture = ARCH_V8;
+      }
+    else
+      {
+	architecture = ARCH_UNKNOWN;
+      }
+
+    buffer_xml_printf (buffer,"<cpu-etm-config arch_ver=\"0x%x\" "
+	"core_prof=\"0x%x\" cpu_id=\"0x%x\" protocol=\"0x%x\">\n",
+			  architecture, profile_CortexA,
+			  i, config->etm_trace_params->at (i).protocol);
+    if (architecture == ARCH_V7)
+    {
+      buffer_xml_printf (buffer,
+	"<etmv3-config reg_idr=\"0x%x\" reg_ctrl=\"0x%x\" "
+	"reg_ccer=\"0x%x\" reg_trc_id=\"0x%x\"/>\n",
+			  config->etm_trace_params->at (i).etmv3.reg_idr,
+			  config->etm_trace_params->at (i).etmv3.reg_ctrl,
+			  config->etm_trace_params->at (i).etmv3.reg_ccer,
+			  config->etm_trace_params->at (i).etmv3.reg_trc_id);
+    }
+    if (architecture == ARCH_V8)
+    {
+      buffer_xml_printf (buffer,
+	"<etmv4-config reg_idr0=\"0x%x\" reg_idr1=\"0x%x\" "
+	"reg_idr2=\"0x%x\" reg_idr8=\"0x%x\" "
+			  "reg_configr=\"0x%x\" reg_traceidr=\"0x%x\"/>\n",
+			  config->etm_trace_params->at (i).etmv4.reg_idr0,
+			  config->etm_trace_params->at (i).etmv4.reg_idr1,
+			  config->etm_trace_params->at (i).etmv4.reg_idr2,
+			  config->etm_trace_params->at (i).etmv4.reg_idr8,
+			  config->etm_trace_params->at (i).etmv4.reg_configr,
+			  config->etm_trace_params->at (i).etmv4.reg_traceidr);
+    }
+    buffer_xml_printf (buffer,"</cpu-etm-config>\n");
+  }
+  buffer_grow_str (buffer,"</source-config>\n");
+  buffer_xml_printf (buffer,
+    "<sink-config  formatted=\"0x%x\" "
+		"fsyncs=\"0x%x\" hsyncs=\"0x%x\" "
+		"frame_aligned=\"0x%x\" reset_on_4x_sync=\"0x%x\"/>\n",
+		config->etm_decoder_params.formatted,
+		config->etm_decoder_params.fsyncs,
+		config->etm_decoder_params.hsyncs,
+		config->etm_decoder_params.frame_aligned,
+		config->etm_decoder_params.reset_on_4x_sync);
+  buffer_grow_str (buffer,"</etm-config>\n");
+}
+
 /* Encode a raw buffer.  */
 
 static void
@@ -7060,6 +7130,20 @@ linux_process_target::read_btrace (btrace_target_info *tinfo,
       buffer_grow_str0 (buffer, "</btrace>\n");
       break;
 
+    case BTRACE_FORMAT_ETM:
+      buffer_grow_str (buffer, "<!DOCTYPE btrace SYSTEM \"btrace.dtd\">\n");
+      buffer_grow_str (buffer, "<btrace version=\"1.1\">\n");
+      buffer_grow_str (buffer, "<etm>\n");
+
+      linux_low_encode_etm_config (buffer, &btrace.variant.etm.config);
+
+      linux_low_encode_raw (buffer, btrace.variant.etm.data,
+			    btrace.variant.etm.size);
+
+      buffer_grow_str (buffer, "</etm>\n");
+      buffer_grow_str0 (buffer, "</btrace>\n");
+      break;
+
     default:
       buffer_grow_str0 (buffer, "E.Unsupported Trace Format.");
       return -1;
@@ -7077,7 +7161,7 @@ linux_process_target::read_btrace_conf (const btrace_target_info *tinfo,
   const struct btrace_config *conf;
 
   buffer_grow_str (buffer, "<!DOCTYPE btrace-conf SYSTEM \"btrace-conf.dtd\">\n");
-  buffer_grow_str (buffer, "<btrace-conf version=\"1.0\">\n");
+  buffer_grow_str (buffer, "<btrace-conf version=\"1.1\">\n");
 
   conf = linux_btrace_conf (tinfo);
   if (conf != NULL)
@@ -7098,6 +7182,20 @@ linux_process_target::read_btrace_conf (const btrace_target_info *tinfo,
 	  buffer_xml_printf (buffer, " size=\"0x%x\"", conf->pt.size);
 	  buffer_xml_printf (buffer, "/>\n");
 	  break;
+
+	case BTRACE_FORMAT_ETM:
+	  buffer_xml_printf (buffer, "<etm");
+	  buffer_xml_printf (buffer, " size=\"0x%x\"", conf->etm.size);
+	  if (conf->etm.sink !=NULL)
+	    {
+	      buffer_xml_printf (buffer, " sink=\"%s\"", conf->etm.sink);
+	    }
+	  else
+	    {
+	      buffer_xml_printf (buffer, " sink=\"default\"");
+	    }
+	  buffer_xml_printf (buffer, "/>\n");
+	  break;
 	}
     }
 
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 32dcc05924e..278978421bc 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -424,6 +424,18 @@ handle_btrace_enable_pt (struct thread_info *thread)
   thread->btrace = target_enable_btrace (thread->id, &current_btrace_conf);
 }
 
+/* Handle btrace enabling in ARM CoreSight Trace format.  */
+
+static void
+handle_btrace_enable_etm (struct thread_info *thread)
+{
+  if (thread->btrace != NULL)
+    error (_("Btrace already enabled."));
+
+  current_btrace_conf.format = BTRACE_FORMAT_ETM;
+  thread->btrace = target_enable_btrace (thread->id, &current_btrace_conf);
+}
+
 /* Handle btrace disabling.  */
 
 static void
@@ -473,10 +485,12 @@ handle_btrace_general_set (char *own_buf)
 	handle_btrace_enable_bts (thread);
       else if (strcmp (op, "pt") == 0)
 	handle_btrace_enable_pt (thread);
+      else if (strcmp (op, "etm") == 0)
+	handle_btrace_enable_etm (thread);
       else if (strcmp (op, "off") == 0)
 	handle_btrace_disable (thread);
       else
-	error (_("Bad Qbtrace operation.  Use bts, pt, or off."));
+	error (_("Bad Qbtrace operation.  Use bts, pt, etm, or off."));
 
       write_ok (own_buf);
     }
@@ -546,6 +560,21 @@ handle_btrace_conf_general_set (char *own_buf)
 
       current_btrace_conf.pt.size = (unsigned int) size;
     }
+  else if (strncmp (op, "etm:size=", strlen ("etm:size=")) == 0)
+    {
+      unsigned long size;
+      char *endp = NULL;
+
+      errno = 0;
+      size = strtoul (op + strlen ("etm:size="), &endp, 16);
+      if (endp == NULL || *endp != 0 || errno != 0 || size > UINT_MAX)
+	{
+	  strcpy (own_buf, "E.Bad size value.");
+	  return -1;
+	}
+
+      current_btrace_conf.etm.size = (unsigned int) size;
+    }
   else
     {
       strcpy (own_buf, "E.Bad Qbtrace configuration option.");
@@ -2189,6 +2218,9 @@ supported_btrace_packets (char *buf)
   strcat (buf, ";Qbtrace-conf:bts:size+");
   strcat (buf, ";Qbtrace:pt+");
   strcat (buf, ";Qbtrace-conf:pt:size+");
+  strcat (buf, ";Qbtrace:etm+");
+  strcat (buf, ";Qbtrace-conf:etm:size+");
+  strcat (buf, ";Qbtrace-conf:etm:sink+");
   strcat (buf, ";Qbtrace:off+");
   strcat (buf, ";qXfer:btrace:read+");
   strcat (buf, ";qXfer:btrace-conf:read+");
-- 
2.25.1


^ permalink raw reply	[flat|nested] 35+ messages in thread

* [PATCH v6 7/7] adapt btrace testcases for arm target
  2021-05-31 21:33 [PATCH v6 0/7] extend branch tracing to use ARM CoreSight traces Zied Guermazi
                   ` (5 preceding siblings ...)
  2021-05-31 21:33 ` [PATCH v6 6/7] add support for coresight btrace via remote protocol Zied Guermazi
@ 2021-05-31 21:33 ` Zied Guermazi
  2021-06-22 21:28   ` Lancelot SIX
                     ` (2 more replies)
  6 siblings, 3 replies; 35+ messages in thread
From: Zied Guermazi @ 2021-05-31 21:33 UTC (permalink / raw)
  To: gdb-patches, markus.t.metzger; +Cc: Zied Guermazi

This patch extends the test suite for btrace to cover using
ARM CoreSight traces.

gdb/ChangeLog
	* testsuite/lib/gdb.exp (skip_btrace_tests): enable btrace tests
	for arm.
	* testsuite/gdb.btrace/buffer-size.exp: enable btrace tests
	and adapt test for arm.
	* testsuite/gdb.btrace/delta.exp (check_trace): enable btrace tests
	and adapt test for arm.
	* testsuite/gdb.btrace/instruction_history.exp: enable btrace tests
	and adapt tests for arm.
	* testsuite/gdb.btrace/instruction_history.S: renamed to
	x86-instruction_history.S.
	* testsuite/gdb.btrace/aarch64-instruction_history.S: New.
	* testsuite/gdb.btrace/arm-instruction_history.S: New.
	* testsuite/gdb.btrace/non-stop.exp: enable btrace tests
	and adapt tests for arm.
	* testsuite/gdb.btrace/reconnect.exp: enable btrace tests
	and adapt tests for arm.
	* testsuite/gdb.btrace/record_goto.exp: enable btrace tests
	and adapt tests for arm.
	* testsuite/gdb.btrace/aarch64-record_goto.S: New.
	* testsuite/gdb.btrace/arm-record_goto.S: New.
	* testsuite/gdb.btrace/stepi.exp: enable btrace tests
	and adapt tests for arm.
	* testsuite/gdb.btrace/tailcall.exp: enable btrace tests
	and adapt tests for arm.
	* testsuite/gdb.btrace/aarch64-tailcall.S: New.
	* testsuite/gdb.btrace/arm-tailcall.S: New.
	* testsuite/gdb.btrace/tailcall-only.exp: enable btrace tests
	and adapt tests for arm.
	* testsuite/gdb.btrace/aarch64-tailcall-only.S: New.
	* testsuite/gdb.btrace/arm-tailcall-only.S: New.
---
 .../gdb.btrace/aarch64-instruction_history.S  |  31 ++
 .../gdb.btrace/aarch64-record_goto.S          | 399 ++++++++++++++
 .../gdb.btrace/aarch64-tailcall-only.S        | 516 ++++++++++++++++++
 gdb/testsuite/gdb.btrace/aarch64-tailcall.S   | 408 ++++++++++++++
 .../gdb.btrace/arm-instruction_history.S      |  31 ++
 gdb/testsuite/gdb.btrace/arm-record_goto.S    | 432 +++++++++++++++
 gdb/testsuite/gdb.btrace/arm-tailcall-only.S  | 503 +++++++++++++++++
 gdb/testsuite/gdb.btrace/arm-tailcall.S       | 390 +++++++++++++
 gdb/testsuite/gdb.btrace/buffer-size.exp      |  15 +-
 .../gdb.btrace/instruction_history.exp        | 106 +++-
 gdb/testsuite/gdb.btrace/non-stop.exp         |  60 +-
 gdb/testsuite/gdb.btrace/record_goto.exp      | 253 ++++++---
 gdb/testsuite/gdb.btrace/stepi.exp            | 107 +++-
 gdb/testsuite/gdb.btrace/tailcall-only.exp    |  20 +-
 gdb/testsuite/gdb.btrace/tailcall.exp         |  58 +-
 ...on_history.S => x86-instruction_history.S} |   0
 gdb/testsuite/lib/gdb.exp                     |   2 +-
 17 files changed, 3189 insertions(+), 142 deletions(-)
 create mode 100644 gdb/testsuite/gdb.btrace/aarch64-instruction_history.S
 create mode 100644 gdb/testsuite/gdb.btrace/aarch64-record_goto.S
 create mode 100644 gdb/testsuite/gdb.btrace/aarch64-tailcall-only.S
 create mode 100644 gdb/testsuite/gdb.btrace/aarch64-tailcall.S
 create mode 100644 gdb/testsuite/gdb.btrace/arm-instruction_history.S
 create mode 100644 gdb/testsuite/gdb.btrace/arm-record_goto.S
 create mode 100644 gdb/testsuite/gdb.btrace/arm-tailcall-only.S
 create mode 100644 gdb/testsuite/gdb.btrace/arm-tailcall.S
 rename gdb/testsuite/gdb.btrace/{instruction_history.S => x86-instruction_history.S} (100%)

diff --git a/gdb/testsuite/gdb.btrace/aarch64-instruction_history.S b/gdb/testsuite/gdb.btrace/aarch64-instruction_history.S
new file mode 100644
index 00000000000..0ccf43a1cf4
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/aarch64-instruction_history.S
@@ -0,0 +1,31 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2021 Free Software Foundation, Inc.
+
+   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 3 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/>.  */
+.arch armv8-a
+.text
+.globl loop
+.type  loop, %function
+loop:
+	mov x0, #2 /* bp.1 */
+L1:
+	cmp x0, #0
+	beq L2
+	subs x0, x0, #1
+	b L1
+L2:
+	ret /* bp.2 */
+
+
diff --git a/gdb/testsuite/gdb.btrace/aarch64-record_goto.S b/gdb/testsuite/gdb.btrace/aarch64-record_goto.S
new file mode 100644
index 00000000000..282f8d41a3c
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/aarch64-record_goto.S
@@ -0,0 +1,399 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2021 Free Software Foundation, Inc.
+
+   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 3 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/>.
+
+   This file has been generated on an armv8 machine using:
+   gcc -S -dA -g record_goto.c -o aarch64-record_goto.S  */
+
+	.arch armv8-a
+	.file	"record_goto.c"
+	.text
+.Ltext0:
+	.align	2
+	.global	fun1
+	//.tune generic
+	.type	fun1, %function
+fun1:
+.LFB0:
+	.file 1 "record_goto.c"
+	// record_goto.c:22:1
+	.loc 1 22 1
+	.cfi_startproc
+// BLOCK 2 seq:0
+// PRED: ENTRY (FALLTHRU)
+	// record_goto.c:23:1
+	.loc 1 23 1
+	nop
+// SUCC: EXIT [always] 
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	fun1, .-fun1
+	.align	2
+	.global	fun2
+	.type	fun2, %function
+fun2:
+.LFB1:
+	// record_goto.c:27:1
+	.loc 1 27 1
+	.cfi_startproc
+// BLOCK 2 seq:0
+// PRED: ENTRY (FALLTHRU)
+	stp	x29, x30, [sp, -16]!
+	.cfi_def_cfa_offset 16
+	.cfi_offset 29, -16
+	.cfi_offset 30, -8
+	mov	x29, sp
+	// record_goto.c:28:3
+	.loc 1 28 3
+	bl	fun1
+	// record_goto.c:29:1
+	.loc 1 29 1
+	nop
+	ldp	x29, x30, [sp], 16
+	.cfi_restore 30
+	.cfi_restore 29
+	.cfi_def_cfa_offset 0
+// SUCC: EXIT [always] 
+	ret
+	.cfi_endproc
+.LFE1:
+	.size	fun2, .-fun2
+	.align	2
+	.global	fun3
+	.type	fun3, %function
+fun3:
+.LFB2:
+	// record_goto.c:33:1
+	.loc 1 33 1
+	.cfi_startproc
+// BLOCK 2 seq:0
+// PRED: ENTRY (FALLTHRU)
+	stp	x29, x30, [sp, -16]!
+	.cfi_def_cfa_offset 16
+	.cfi_offset 29, -16
+	.cfi_offset 30, -8
+	mov	x29, sp
+	// record_goto.c:34:3
+	.loc 1 34 3
+	bl	fun1
+	// record_goto.c:35:3
+	.loc 1 35 3
+	bl	fun2
+	// record_goto.c:36:1
+	.loc 1 36 1
+	nop
+	ldp	x29, x30, [sp], 16
+	.cfi_restore 30
+	.cfi_restore 29
+	.cfi_def_cfa_offset 0
+// SUCC: EXIT [always] 
+	ret
+	.cfi_endproc
+.LFE2:
+	.size	fun3, .-fun3
+	.align	2
+	.global	fun4
+	.type	fun4, %function
+fun4:
+.LFB3:
+	// record_goto.c:40:1
+	.loc 1 40 1
+	.cfi_startproc
+// BLOCK 2 seq:0
+// PRED: ENTRY (FALLTHRU)
+	stp	x29, x30, [sp, -16]!
+	.cfi_def_cfa_offset 16
+	.cfi_offset 29, -16
+	.cfi_offset 30, -8
+	mov	x29, sp
+	// record_goto.c:41:3
+	.loc 1 41 3
+	bl	fun1
+	// record_goto.c:42:3
+	.loc 1 42 3
+	bl	fun2
+	// record_goto.c:43:3
+	.loc 1 43 3
+	bl	fun3
+	// record_goto.c:44:1
+	.loc 1 44 1
+	nop
+	ldp	x29, x30, [sp], 16
+	.cfi_restore 30
+	.cfi_restore 29
+	.cfi_def_cfa_offset 0
+// SUCC: EXIT [always] 
+	ret
+	.cfi_endproc
+.LFE3:
+	.size	fun4, .-fun4
+	.align	2
+	.global	main
+	.type	main, %function
+main:
+.LFB4:
+	// record_goto.c:48:1
+	.loc 1 48 1
+	.cfi_startproc
+// BLOCK 2 seq:0
+// PRED: ENTRY (FALLTHRU)
+	stp	x29, x30, [sp, -16]!
+	.cfi_def_cfa_offset 16
+	.cfi_offset 29, -16
+	.cfi_offset 30, -8
+	mov	x29, sp
+	// record_goto.c:49:3
+	.loc 1 49 3
+	bl	fun4
+	// record_goto.c:50:10
+	.loc 1 50 10
+	mov	w0, 0
+	// record_goto.c:51:1
+	.loc 1 51 1
+	ldp	x29, x30, [sp], 16
+	.cfi_restore 30
+	.cfi_restore 29
+	.cfi_def_cfa_offset 0
+// SUCC: EXIT [always] 
+	ret
+	.cfi_endproc
+.LFE4:
+	.size	main, .-main
+.Letext0:
+	.section	.debug_info,"",@progbits
+.Ldebug_info0:
+	.4byte	0xb7	// Length of Compilation Unit Info
+	.2byte	0x4	// DWARF version number
+	.4byte	.Ldebug_abbrev0	// Offset Into Abbrev. Section
+	.byte	0x8	// Pointer Size (in bytes)
+	.uleb128 0x1	// (DIE (0xb) DW_TAG_compile_unit)
+	.4byte	.LASF4	// DW_AT_producer: "GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -fasynchronous-unwind-tables"
+	.byte	0xc	// DW_AT_language
+	.4byte	.LASF5	// DW_AT_name: "record_goto.c"
+	.4byte	.LASF6	// DW_AT_comp_dir: "/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
+	.8byte	.Ltext0	// DW_AT_low_pc
+	.8byte	.Letext0-.Ltext0	// DW_AT_high_pc
+	.4byte	.Ldebug_line0	// DW_AT_stmt_list
+	.uleb128 0x2	// (DIE (0x2d) DW_TAG_subprogram)
+			// DW_AT_external
+	.4byte	.LASF7	// DW_AT_name: "main"
+	.byte	0x1	// DW_AT_decl_file (record_goto.c)
+	.byte	0x2f	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.4byte	0x4b	// DW_AT_type
+	.8byte	.LFB4	// DW_AT_low_pc
+	.8byte	.LFE4-.LFB4	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_tail_call_sites
+	.uleb128 0x3	// (DIE (0x4b) DW_TAG_base_type)
+	.byte	0x4	// DW_AT_byte_size
+	.byte	0x5	// DW_AT_encoding
+	.ascii "int\0"	// DW_AT_name
+	.uleb128 0x4	// (DIE (0x52) DW_TAG_subprogram)
+			// DW_AT_external
+	.4byte	.LASF0	// DW_AT_name: "fun4"
+	.byte	0x1	// DW_AT_decl_file (record_goto.c)
+	.byte	0x27	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.8byte	.LFB3	// DW_AT_low_pc
+	.8byte	.LFE3-.LFB3	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_tail_call_sites
+	.uleb128 0x4	// (DIE (0x6c) DW_TAG_subprogram)
+			// DW_AT_external
+	.4byte	.LASF1	// DW_AT_name: "fun3"
+	.byte	0x1	// DW_AT_decl_file (record_goto.c)
+	.byte	0x20	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.8byte	.LFB2	// DW_AT_low_pc
+	.8byte	.LFE2-.LFB2	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_tail_call_sites
+	.uleb128 0x4	// (DIE (0x86) DW_TAG_subprogram)
+			// DW_AT_external
+	.4byte	.LASF2	// DW_AT_name: "fun2"
+	.byte	0x1	// DW_AT_decl_file (record_goto.c)
+	.byte	0x1a	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.8byte	.LFB1	// DW_AT_low_pc
+	.8byte	.LFE1-.LFB1	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_tail_call_sites
+	.uleb128 0x5	// (DIE (0xa0) DW_TAG_subprogram)
+			// DW_AT_external
+	.4byte	.LASF3	// DW_AT_name: "fun1"
+	.byte	0x1	// DW_AT_decl_file (record_goto.c)
+	.byte	0x15	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.8byte	.LFB0	// DW_AT_low_pc
+	.8byte	.LFE0-.LFB0	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_call_sites
+	.byte	0	// end of children of DIE 0xb
+	.section	.debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+	.uleb128 0x1	// (abbrev code)
+	.uleb128 0x11	// (TAG: DW_TAG_compile_unit)
+	.byte	0x1	// DW_children_yes
+	.uleb128 0x25	// (DW_AT_producer)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x13	// (DW_AT_language)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x1b	// (DW_AT_comp_dir)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x12	// (DW_AT_high_pc)
+	.uleb128 0x7	// (DW_FORM_data8)
+	.uleb128 0x10	// (DW_AT_stmt_list)
+	.uleb128 0x17	// (DW_FORM_sec_offset)
+	.byte	0
+	.byte	0
+	.uleb128 0x2	// (abbrev code)
+	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
+	.byte	0	// DW_children_no
+	.uleb128 0x3f	// (DW_AT_external)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x27	// (DW_AT_prototyped)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x49	// (DW_AT_type)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x12	// (DW_AT_high_pc)
+	.uleb128 0x7	// (DW_FORM_data8)
+	.uleb128 0x40	// (DW_AT_frame_base)
+	.uleb128 0x18	// (DW_FORM_exprloc)
+	.uleb128 0x2116	// (DW_AT_GNU_all_tail_call_sites)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.byte	0
+	.byte	0
+	.uleb128 0x3	// (abbrev code)
+	.uleb128 0x24	// (TAG: DW_TAG_base_type)
+	.byte	0	// DW_children_no
+	.uleb128 0xb	// (DW_AT_byte_size)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3e	// (DW_AT_encoding)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0x8	// (DW_FORM_string)
+	.byte	0
+	.byte	0
+	.uleb128 0x4	// (abbrev code)
+	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
+	.byte	0	// DW_children_no
+	.uleb128 0x3f	// (DW_AT_external)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x27	// (DW_AT_prototyped)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x12	// (DW_AT_high_pc)
+	.uleb128 0x7	// (DW_FORM_data8)
+	.uleb128 0x40	// (DW_AT_frame_base)
+	.uleb128 0x18	// (DW_FORM_exprloc)
+	.uleb128 0x2116	// (DW_AT_GNU_all_tail_call_sites)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.byte	0
+	.byte	0
+	.uleb128 0x5	// (abbrev code)
+	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
+	.byte	0	// DW_children_no
+	.uleb128 0x3f	// (DW_AT_external)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x27	// (DW_AT_prototyped)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x12	// (DW_AT_high_pc)
+	.uleb128 0x7	// (DW_FORM_data8)
+	.uleb128 0x40	// (DW_AT_frame_base)
+	.uleb128 0x18	// (DW_FORM_exprloc)
+	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.byte	0
+	.byte	0
+	.byte	0
+	.section	.debug_aranges,"",@progbits
+	.4byte	0x2c	// Length of Address Ranges Info
+	.2byte	0x2	// DWARF aranges version
+	.4byte	.Ldebug_info0	// Offset of Compilation Unit Info
+	.byte	0x8	// Size of Address
+	.byte	0	// Size of Segment Descriptor
+	.2byte	0	// Pad to 16 byte boundary
+	.2byte	0
+	.8byte	.Ltext0	// Address
+	.8byte	.Letext0-.Ltext0	// Length
+	.8byte	0
+	.8byte	0
+	.section	.debug_line,"",@progbits
+.Ldebug_line0:
+	.section	.debug_str,"MS",@progbits,1
+.LASF5:
+	.string	"record_goto.c"
+.LASF4:
+	.string	"GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -fasynchronous-unwind-tables"
+.LASF3:
+	.string	"fun1"
+.LASF2:
+	.string	"fun2"
+.LASF0:
+	.string	"fun4"
+.LASF6:
+	.string	"/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
+.LASF7:
+	.string	"main"
+.LASF1:
+	.string	"fun3"
+	.ident	"GCC: (Debian 10.2.1-3) 10.2.1 20201224"
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gdb/testsuite/gdb.btrace/aarch64-tailcall-only.S b/gdb/testsuite/gdb.btrace/aarch64-tailcall-only.S
new file mode 100644
index 00000000000..f3e12ef2bc8
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/aarch64-tailcall-only.S
@@ -0,0 +1,516 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2021 Free Software Foundation, Inc.
+
+   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 3 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/>.
+
+   This file has been generated on an armv8 machine using:
+   gcc -m32 -S -O2 -dA -g tailcall-only.c -o aarch64-tailcall-only.S
+ */
+
+	.arch armv8-a
+	.file	"tailcall-only.c"
+	.text
+.Ltext0:
+	.align	2
+	.p2align 4,,11
+	//.tune generic
+	.type	bar_1, %function
+bar_1:
+.LFB0:
+	.file 1 "tailcall-only.c"
+	// tailcall-only.c:22:1
+	.loc 1 22 1 view -0
+	.cfi_startproc
+// BLOCK 2, count:1073741824 (estimated locally) seq:0
+// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
+	// tailcall-only.c:23:3
+	.loc 1 23 3 view .LVU1
+	// tailcall-only.c:24:1
+	.loc 1 24 1 is_stmt 0 view .LVU2
+	mov	w0, 42
+// SUCC: EXIT [always]  count:1073741824 (estimated locally)
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	bar_1, .-bar_1
+	.align	2
+	.p2align 4,,11
+	.type	bar, %function
+bar:
+.LFB1:
+	// tailcall-only.c:28:1
+	.loc 1 28 1 is_stmt 1 view -0
+	.cfi_startproc
+// BLOCK 2, count:1073741824 (estimated locally) seq:0
+// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
+	// tailcall-only.c:29:3
+	.loc 1 29 3 view .LVU4
+// SUCC: EXIT [always]  count:1073741824 (estimated locally) (ABNORMAL,SIBCALL)
+	// tailcall-only.c:29:10
+	.loc 1 29 10 is_stmt 0 view .LVU5
+	b	bar_1
+.LVL0:
+	.cfi_endproc
+.LFE1:
+	.size	bar, .-bar
+	.align	2
+	.p2align 4,,11
+	.type	foo_1, %function
+foo_1:
+.LFB2:
+	// tailcall-only.c:34:1
+	.loc 1 34 1 is_stmt 1 view -0
+	.cfi_startproc
+// BLOCK 2, count:1073741824 (estimated locally) seq:0
+// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
+	// tailcall-only.c:35:3
+	.loc 1 35 3 view .LVU7
+// SUCC: EXIT [always]  count:1073741824 (estimated locally) (ABNORMAL,SIBCALL)
+	// tailcall-only.c:35:10
+	.loc 1 35 10 is_stmt 0 view .LVU8
+	b	bar
+.LVL1:
+	.cfi_endproc
+.LFE2:
+	.size	foo_1, .-foo_1
+	.align	2
+	.p2align 4,,11
+	.type	foo, %function
+foo:
+.LFB3:
+	// tailcall-only.c:40:1
+	.loc 1 40 1 is_stmt 1 view -0
+	.cfi_startproc
+// BLOCK 2, count:1073741824 (estimated locally) seq:0
+// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
+	// tailcall-only.c:41:3
+	.loc 1 41 3 view .LVU10
+// SUCC: EXIT [always]  count:1073741824 (estimated locally) (ABNORMAL,SIBCALL)
+	// tailcall-only.c:41:10
+	.loc 1 41 10 is_stmt 0 view .LVU11
+	b	foo_1
+.LVL2:
+	.cfi_endproc
+.LFE3:
+	.size	foo, .-foo
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.p2align 4,,11
+	.global	main
+	.type	main, %function
+main:
+.LFB4:
+	// tailcall-only.c:46:1
+	.loc 1 46 1 is_stmt 1 view -0
+	.cfi_startproc
+// BLOCK 2, count:1073741824 (estimated locally) seq:0
+// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
+	// tailcall-only.c:47:3
+	.loc 1 47 3 view .LVU13
+	// tailcall-only.c:49:3
+	.loc 1 49 3 view .LVU14
+	// tailcall-only.c:46:1
+	.loc 1 46 1 is_stmt 0 view .LVU15
+	stp	x29, x30, [sp, -16]!
+	.cfi_def_cfa_offset 16
+	.cfi_offset 29, -16
+	.cfi_offset 30, -8
+	mov	x29, sp
+	// tailcall-only.c:49:12
+	.loc 1 49 12 view .LVU16
+	bl	foo
+.LVL3:
+	// DEBUG answer => x0
+	// tailcall-only.c:50:3
+	.loc 1 50 3 is_stmt 1 view .LVU17
+	// DEBUG answer => x0+0x1
+	// tailcall-only.c:52:3
+	.loc 1 52 3 view .LVU18
+	// tailcall-only.c:53:1
+	.loc 1 53 1 is_stmt 0 view .LVU19
+	add	w0, w0, 1
+.LVL4:
+	// DEBUG answer => x0
+	// tailcall-only.c:53:1
+	.loc 1 53 1 view .LVU20
+	ldp	x29, x30, [sp], 16
+	.cfi_restore 30
+	.cfi_restore 29
+	.cfi_def_cfa_offset 0
+// SUCC: EXIT [always]  count:1073741824 (estimated locally)
+	ret
+	.cfi_endproc
+.LFE4:
+	.size	main, .-main
+	.text
+.Letext0:
+	.section	.debug_info,"",@progbits
+.Ldebug_info0:
+	.4byte	0x11f	// Length of Compilation Unit Info
+	.2byte	0x4	// DWARF version number
+	.4byte	.Ldebug_abbrev0	// Offset Into Abbrev. Section
+	.byte	0x8	// Pointer Size (in bytes)
+	.uleb128 0x1	// (DIE (0xb) DW_TAG_compile_unit)
+	.4byte	.LASF1	// DW_AT_producer: "GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -O2 -fasynchronous-unwind-tables"
+	.byte	0xc	// DW_AT_language
+	.4byte	.LASF2	// DW_AT_name: "tailcall-only.c"
+	.4byte	.LASF3	// DW_AT_comp_dir: "/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
+	.4byte	.Ldebug_ranges0+0	// DW_AT_ranges
+	.8byte	0	// DW_AT_low_pc
+	.4byte	.Ldebug_line0	// DW_AT_stmt_list
+	.uleb128 0x2	// (DIE (0x29) DW_TAG_subprogram)
+			// DW_AT_external
+	.4byte	.LASF4	// DW_AT_name: "main"
+	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
+	.byte	0x2d	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.4byte	0x6d	// DW_AT_type
+	.8byte	.LFB4	// DW_AT_low_pc
+	.8byte	.LFE4-.LFB4	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_call_sites
+	.4byte	0x6d	// DW_AT_sibling
+	.uleb128 0x3	// (DIE (0x4b) DW_TAG_variable)
+	.4byte	.LASF5	// DW_AT_name: "answer"
+	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
+	.byte	0x2f	// DW_AT_decl_line
+	.byte	0x7	// DW_AT_decl_column
+	.4byte	0x6d	// DW_AT_type
+	.4byte	.LLST0	// DW_AT_location
+	.4byte	.LVUS0	// DW_AT_GNU_locviews
+	.uleb128 0x4	// (DIE (0x5f) DW_TAG_GNU_call_site)
+	.8byte	.LVL3	// DW_AT_low_pc
+	.4byte	0x74	// DW_AT_abstract_origin
+	.byte	0	// end of children of DIE 0x29
+	.uleb128 0x5	// (DIE (0x6d) DW_TAG_base_type)
+	.byte	0x4	// DW_AT_byte_size
+	.byte	0x5	// DW_AT_encoding
+	.ascii "int\0"	// DW_AT_name
+	.uleb128 0x6	// (DIE (0x74) DW_TAG_subprogram)
+	.ascii "foo\0"	// DW_AT_name
+	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
+	.byte	0x27	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.4byte	0x6d	// DW_AT_type
+	.8byte	.LFB3	// DW_AT_low_pc
+	.8byte	.LFE3-.LFB3	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_call_sites
+	.4byte	0xa4	// DW_AT_sibling
+	.uleb128 0x7	// (DIE (0x96) DW_TAG_GNU_call_site)
+	.8byte	.LVL2	// DW_AT_low_pc
+			// DW_AT_GNU_tail_call
+	.4byte	0xa4	// DW_AT_abstract_origin
+	.byte	0	// end of children of DIE 0x74
+	.uleb128 0x8	// (DIE (0xa4) DW_TAG_subprogram)
+	.4byte	.LASF0	// DW_AT_name: "foo_1"
+	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
+	.byte	0x21	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.4byte	0x6d	// DW_AT_type
+	.8byte	.LFB2	// DW_AT_low_pc
+	.8byte	.LFE2-.LFB2	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_call_sites
+	.4byte	0xd4	// DW_AT_sibling
+	.uleb128 0x7	// (DIE (0xc6) DW_TAG_GNU_call_site)
+	.8byte	.LVL1	// DW_AT_low_pc
+			// DW_AT_GNU_tail_call
+	.4byte	0xd4	// DW_AT_abstract_origin
+	.byte	0	// end of children of DIE 0xa4
+	.uleb128 0x6	// (DIE (0xd4) DW_TAG_subprogram)
+	.ascii "bar\0"	// DW_AT_name
+	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
+	.byte	0x1b	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.4byte	0x6d	// DW_AT_type
+	.8byte	.LFB1	// DW_AT_low_pc
+	.8byte	.LFE1-.LFB1	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_call_sites
+	.4byte	0x104	// DW_AT_sibling
+	.uleb128 0x7	// (DIE (0xf6) DW_TAG_GNU_call_site)
+	.8byte	.LVL0	// DW_AT_low_pc
+			// DW_AT_GNU_tail_call
+	.4byte	0x104	// DW_AT_abstract_origin
+	.byte	0	// end of children of DIE 0xd4
+	.uleb128 0x9	// (DIE (0x104) DW_TAG_subprogram)
+	.4byte	.LASF6	// DW_AT_name: "bar_1"
+	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
+	.byte	0x15	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.4byte	0x6d	// DW_AT_type
+	.8byte	.LFB0	// DW_AT_low_pc
+	.8byte	.LFE0-.LFB0	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_call_sites
+	.byte	0	// end of children of DIE 0xb
+	.section	.debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+	.uleb128 0x1	// (abbrev code)
+	.uleb128 0x11	// (TAG: DW_TAG_compile_unit)
+	.byte	0x1	// DW_children_yes
+	.uleb128 0x25	// (DW_AT_producer)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x13	// (DW_AT_language)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x1b	// (DW_AT_comp_dir)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x55	// (DW_AT_ranges)
+	.uleb128 0x17	// (DW_FORM_sec_offset)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x10	// (DW_AT_stmt_list)
+	.uleb128 0x17	// (DW_FORM_sec_offset)
+	.byte	0
+	.byte	0
+	.uleb128 0x2	// (abbrev code)
+	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
+	.byte	0x1	// DW_children_yes
+	.uleb128 0x3f	// (DW_AT_external)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x27	// (DW_AT_prototyped)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x49	// (DW_AT_type)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x12	// (DW_AT_high_pc)
+	.uleb128 0x7	// (DW_FORM_data8)
+	.uleb128 0x40	// (DW_AT_frame_base)
+	.uleb128 0x18	// (DW_FORM_exprloc)
+	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x1	// (DW_AT_sibling)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x3	// (abbrev code)
+	.uleb128 0x34	// (TAG: DW_TAG_variable)
+	.byte	0	// DW_children_no
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x49	// (DW_AT_type)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.uleb128 0x2	// (DW_AT_location)
+	.uleb128 0x17	// (DW_FORM_sec_offset)
+	.uleb128 0x2137	// (DW_AT_GNU_locviews)
+	.uleb128 0x17	// (DW_FORM_sec_offset)
+	.byte	0
+	.byte	0
+	.uleb128 0x4	// (abbrev code)
+	.uleb128 0x4109	// (TAG: DW_TAG_GNU_call_site)
+	.byte	0	// DW_children_no
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x31	// (DW_AT_abstract_origin)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x5	// (abbrev code)
+	.uleb128 0x24	// (TAG: DW_TAG_base_type)
+	.byte	0	// DW_children_no
+	.uleb128 0xb	// (DW_AT_byte_size)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3e	// (DW_AT_encoding)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0x8	// (DW_FORM_string)
+	.byte	0
+	.byte	0
+	.uleb128 0x6	// (abbrev code)
+	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
+	.byte	0x1	// DW_children_yes
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0x8	// (DW_FORM_string)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x27	// (DW_AT_prototyped)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x49	// (DW_AT_type)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x12	// (DW_AT_high_pc)
+	.uleb128 0x7	// (DW_FORM_data8)
+	.uleb128 0x40	// (DW_AT_frame_base)
+	.uleb128 0x18	// (DW_FORM_exprloc)
+	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x1	// (DW_AT_sibling)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x7	// (abbrev code)
+	.uleb128 0x4109	// (TAG: DW_TAG_GNU_call_site)
+	.byte	0	// DW_children_no
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x2115	// (DW_AT_GNU_tail_call)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x31	// (DW_AT_abstract_origin)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x8	// (abbrev code)
+	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
+	.byte	0x1	// DW_children_yes
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x27	// (DW_AT_prototyped)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x49	// (DW_AT_type)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x12	// (DW_AT_high_pc)
+	.uleb128 0x7	// (DW_FORM_data8)
+	.uleb128 0x40	// (DW_AT_frame_base)
+	.uleb128 0x18	// (DW_FORM_exprloc)
+	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x1	// (DW_AT_sibling)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x9	// (abbrev code)
+	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
+	.byte	0	// DW_children_no
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x27	// (DW_AT_prototyped)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x49	// (DW_AT_type)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x12	// (DW_AT_high_pc)
+	.uleb128 0x7	// (DW_FORM_data8)
+	.uleb128 0x40	// (DW_AT_frame_base)
+	.uleb128 0x18	// (DW_FORM_exprloc)
+	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.byte	0
+	.byte	0
+	.byte	0
+	.section	.debug_loc,"",@progbits
+.Ldebug_loc0:
+.LVUS0:
+	.uleb128 .LVU17	// View list begin (*.LVUS0)
+	.uleb128 .LVU18	// View list end (*.LVUS0)
+	.uleb128 .LVU18	// View list begin (*.LVUS0)
+	.uleb128 .LVU20	// View list end (*.LVUS0)
+	.uleb128 .LVU20	// View list begin (*.LVUS0)
+	.uleb128 0	// View list end (*.LVUS0)
+.LLST0:
+	.8byte	.LVL3	// Location list begin address (*.LLST0)
+	.8byte	.LVL3	// Location list end address (*.LLST0)
+	.2byte	0x1	// Location expression size
+	.byte	0x50	// DW_OP_reg0
+	.8byte	.LVL3	// Location list begin address (*.LLST0)
+	.8byte	.LVL4	// Location list end address (*.LLST0)
+	.2byte	0x3	// Location expression size
+	.byte	0x70	// DW_OP_breg0
+	.sleb128 1
+	.byte	0x9f	// DW_OP_stack_value
+	.8byte	.LVL4	// Location list begin address (*.LLST0)
+	.8byte	.LFE4	// Location list end address (*.LLST0)
+	.2byte	0x1	// Location expression size
+	.byte	0x50	// DW_OP_reg0
+	.8byte	0	// Location list terminator begin (*.LLST0)
+	.8byte	0	// Location list terminator end (*.LLST0)
+	.section	.debug_aranges,"",@progbits
+	.4byte	0x3c	// Length of Address Ranges Info
+	.2byte	0x2	// DWARF aranges version
+	.4byte	.Ldebug_info0	// Offset of Compilation Unit Info
+	.byte	0x8	// Size of Address
+	.byte	0	// Size of Segment Descriptor
+	.2byte	0	// Pad to 16 byte boundary
+	.2byte	0
+	.8byte	.Ltext0	// Address
+	.8byte	.Letext0-.Ltext0	// Length
+	.8byte	.LFB4	// Address
+	.8byte	.LFE4-.LFB4	// Length
+	.8byte	0
+	.8byte	0
+	.section	.debug_ranges,"",@progbits
+.Ldebug_ranges0:
+	.8byte	.Ltext0	// Offset 0
+	.8byte	.Letext0
+	.8byte	.LFB4	// Offset 0x10
+	.8byte	.LFE4
+	.8byte	0
+	.8byte	0
+	.section	.debug_line,"",@progbits
+.Ldebug_line0:
+	.section	.debug_str,"MS",@progbits,1
+.LASF6:
+	.string	"bar_1"
+.LASF2:
+	.string	"tailcall-only.c"
+.LASF3:
+	.string	"/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
+.LASF5:
+	.string	"answer"
+.LASF4:
+	.string	"main"
+.LASF1:
+	.string	"GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -O2 -fasynchronous-unwind-tables"
+.LASF0:
+	.string	"foo_1"
+	.ident	"GCC: (Debian 10.2.1-3) 10.2.1 20201224"
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gdb/testsuite/gdb.btrace/aarch64-tailcall.S b/gdb/testsuite/gdb.btrace/aarch64-tailcall.S
new file mode 100644
index 00000000000..2023b878a1a
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/aarch64-tailcall.S
@@ -0,0 +1,408 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2021 Free Software Foundation, Inc.
+
+   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 3 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/>.
+
+   This file has been generated on an armv8 machine using:
+   gcc -S -O2 -dA -g tailcall.c -o aarch64-tailcall.S  */
+
+	.arch armv8-a
+	.file	"tailcall.c"
+	.text
+.Ltext0:
+	.align	2
+	.p2align 4,,11
+	//.tune generic
+	.type	bar, %function
+bar:
+.LFB0:
+	.file 1 "tailcall.c"
+	// tailcall.c:22:1
+	.loc 1 22 1 view -0
+	.cfi_startproc
+// BLOCK 2, count:1073741824 (estimated locally) seq:0
+// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
+	// tailcall.c:23:3
+	.loc 1 23 3 view .LVU1
+	// tailcall.c:24:1
+	.loc 1 24 1 is_stmt 0 view .LVU2
+	mov	w0, 42
+// SUCC: EXIT [always]  count:1073741824 (estimated locally)
+	ret
+	.cfi_endproc
+.LFE0:
+	.size	bar, .-bar
+	.align	2
+	.p2align 4,,11
+	.type	foo, %function
+foo:
+.LFB1:
+	// tailcall.c:28:1
+	.loc 1 28 1 is_stmt 1 view -0
+	.cfi_startproc
+// BLOCK 2, count:1073741824 (estimated locally) seq:0
+// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
+	// tailcall.c:29:3
+	.loc 1 29 3 view .LVU4
+// SUCC: EXIT [always]  count:1073741824 (estimated locally) (ABNORMAL,SIBCALL)
+	// tailcall.c:29:10
+	.loc 1 29 10 is_stmt 0 view .LVU5
+	b	bar
+.LVL0:
+	.cfi_endproc
+.LFE1:
+	.size	foo, .-foo
+	.section	.text.startup,"ax",@progbits
+	.align	2
+	.p2align 4,,11
+	.global	main
+	.type	main, %function
+main:
+.LFB2:
+	// tailcall.c:34:1
+	.loc 1 34 1 is_stmt 1 view -0
+	.cfi_startproc
+// BLOCK 2, count:1073741824 (estimated locally) seq:0
+// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
+	// tailcall.c:35:3
+	.loc 1 35 3 view .LVU7
+	// tailcall.c:37:3
+	.loc 1 37 3 view .LVU8
+	// tailcall.c:34:1
+	.loc 1 34 1 is_stmt 0 view .LVU9
+	stp	x29, x30, [sp, -16]!
+	.cfi_def_cfa_offset 16
+	.cfi_offset 29, -16
+	.cfi_offset 30, -8
+	mov	x29, sp
+	// tailcall.c:37:12
+	.loc 1 37 12 view .LVU10
+	bl	foo
+.LVL1:
+	// DEBUG answer => x0
+	// tailcall.c:38:3
+	.loc 1 38 3 is_stmt 1 view .LVU11
+	// DEBUG answer => x0+0x1
+	// tailcall.c:40:3
+	.loc 1 40 3 view .LVU12
+	// tailcall.c:41:1
+	.loc 1 41 1 is_stmt 0 view .LVU13
+	add	w0, w0, 1
+.LVL2:
+	// DEBUG answer => x0
+	// tailcall.c:41:1
+	.loc 1 41 1 view .LVU14
+	ldp	x29, x30, [sp], 16
+	.cfi_restore 30
+	.cfi_restore 29
+	.cfi_def_cfa_offset 0
+// SUCC: EXIT [always]  count:1073741824 (estimated locally)
+	ret
+	.cfi_endproc
+.LFE2:
+	.size	main, .-main
+	.text
+.Letext0:
+	.section	.debug_info,"",@progbits
+.Ldebug_info0:
+	.4byte	0xbf	// Length of Compilation Unit Info
+	.2byte	0x4	// DWARF version number
+	.4byte	.Ldebug_abbrev0	// Offset Into Abbrev. Section
+	.byte	0x8	// Pointer Size (in bytes)
+	.uleb128 0x1	// (DIE (0xb) DW_TAG_compile_unit)
+	.4byte	.LASF0	// DW_AT_producer: "GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -O2 -fasynchronous-unwind-tables"
+	.byte	0xc	// DW_AT_language
+	.4byte	.LASF1	// DW_AT_name: "tailcall.c"
+	.4byte	.LASF2	// DW_AT_comp_dir: "/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
+	.4byte	.Ldebug_ranges0+0	// DW_AT_ranges
+	.8byte	0	// DW_AT_low_pc
+	.4byte	.Ldebug_line0	// DW_AT_stmt_list
+	.uleb128 0x2	// (DIE (0x29) DW_TAG_subprogram)
+			// DW_AT_external
+	.4byte	.LASF3	// DW_AT_name: "main"
+	.byte	0x1	// DW_AT_decl_file (tailcall.c)
+	.byte	0x21	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.4byte	0x6d	// DW_AT_type
+	.8byte	.LFB2	// DW_AT_low_pc
+	.8byte	.LFE2-.LFB2	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_call_sites
+	.4byte	0x6d	// DW_AT_sibling
+	.uleb128 0x3	// (DIE (0x4b) DW_TAG_variable)
+	.4byte	.LASF4	// DW_AT_name: "answer"
+	.byte	0x1	// DW_AT_decl_file (tailcall.c)
+	.byte	0x23	// DW_AT_decl_line
+	.byte	0x7	// DW_AT_decl_column
+	.4byte	0x6d	// DW_AT_type
+	.4byte	.LLST0	// DW_AT_location
+	.4byte	.LVUS0	// DW_AT_GNU_locviews
+	.uleb128 0x4	// (DIE (0x5f) DW_TAG_GNU_call_site)
+	.8byte	.LVL1	// DW_AT_low_pc
+	.4byte	0x74	// DW_AT_abstract_origin
+	.byte	0	// end of children of DIE 0x29
+	.uleb128 0x5	// (DIE (0x6d) DW_TAG_base_type)
+	.byte	0x4	// DW_AT_byte_size
+	.byte	0x5	// DW_AT_encoding
+	.ascii "int\0"	// DW_AT_name
+	.uleb128 0x6	// (DIE (0x74) DW_TAG_subprogram)
+	.ascii "foo\0"	// DW_AT_name
+	.byte	0x1	// DW_AT_decl_file (tailcall.c)
+	.byte	0x1b	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.4byte	0x6d	// DW_AT_type
+	.8byte	.LFB1	// DW_AT_low_pc
+	.8byte	.LFE1-.LFB1	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_call_sites
+	.4byte	0xa4	// DW_AT_sibling
+	.uleb128 0x7	// (DIE (0x96) DW_TAG_GNU_call_site)
+	.8byte	.LVL0	// DW_AT_low_pc
+			// DW_AT_GNU_tail_call
+	.4byte	0xa4	// DW_AT_abstract_origin
+	.byte	0	// end of children of DIE 0x74
+	.uleb128 0x8	// (DIE (0xa4) DW_TAG_subprogram)
+	.ascii "bar\0"	// DW_AT_name
+	.byte	0x1	// DW_AT_decl_file (tailcall.c)
+	.byte	0x15	// DW_AT_decl_line
+	.byte	0x1	// DW_AT_decl_column
+			// DW_AT_prototyped
+	.4byte	0x6d	// DW_AT_type
+	.8byte	.LFB0	// DW_AT_low_pc
+	.8byte	.LFE0-.LFB0	// DW_AT_high_pc
+	.uleb128 0x1	// DW_AT_frame_base
+	.byte	0x9c	// DW_OP_call_frame_cfa
+			// DW_AT_GNU_all_call_sites
+	.byte	0	// end of children of DIE 0xb
+	.section	.debug_abbrev,"",@progbits
+.Ldebug_abbrev0:
+	.uleb128 0x1	// (abbrev code)
+	.uleb128 0x11	// (TAG: DW_TAG_compile_unit)
+	.byte	0x1	// DW_children_yes
+	.uleb128 0x25	// (DW_AT_producer)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x13	// (DW_AT_language)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x1b	// (DW_AT_comp_dir)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x55	// (DW_AT_ranges)
+	.uleb128 0x17	// (DW_FORM_sec_offset)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x10	// (DW_AT_stmt_list)
+	.uleb128 0x17	// (DW_FORM_sec_offset)
+	.byte	0
+	.byte	0
+	.uleb128 0x2	// (abbrev code)
+	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
+	.byte	0x1	// DW_children_yes
+	.uleb128 0x3f	// (DW_AT_external)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x27	// (DW_AT_prototyped)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x49	// (DW_AT_type)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x12	// (DW_AT_high_pc)
+	.uleb128 0x7	// (DW_FORM_data8)
+	.uleb128 0x40	// (DW_AT_frame_base)
+	.uleb128 0x18	// (DW_FORM_exprloc)
+	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x1	// (DW_AT_sibling)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x3	// (abbrev code)
+	.uleb128 0x34	// (TAG: DW_TAG_variable)
+	.byte	0	// DW_children_no
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0xe	// (DW_FORM_strp)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x49	// (DW_AT_type)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.uleb128 0x2	// (DW_AT_location)
+	.uleb128 0x17	// (DW_FORM_sec_offset)
+	.uleb128 0x2137	// (DW_AT_GNU_locviews)
+	.uleb128 0x17	// (DW_FORM_sec_offset)
+	.byte	0
+	.byte	0
+	.uleb128 0x4	// (abbrev code)
+	.uleb128 0x4109	// (TAG: DW_TAG_GNU_call_site)
+	.byte	0	// DW_children_no
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x31	// (DW_AT_abstract_origin)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x5	// (abbrev code)
+	.uleb128 0x24	// (TAG: DW_TAG_base_type)
+	.byte	0	// DW_children_no
+	.uleb128 0xb	// (DW_AT_byte_size)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3e	// (DW_AT_encoding)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0x8	// (DW_FORM_string)
+	.byte	0
+	.byte	0
+	.uleb128 0x6	// (abbrev code)
+	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
+	.byte	0x1	// DW_children_yes
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0x8	// (DW_FORM_string)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x27	// (DW_AT_prototyped)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x49	// (DW_AT_type)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x12	// (DW_AT_high_pc)
+	.uleb128 0x7	// (DW_FORM_data8)
+	.uleb128 0x40	// (DW_AT_frame_base)
+	.uleb128 0x18	// (DW_FORM_exprloc)
+	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x1	// (DW_AT_sibling)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x7	// (abbrev code)
+	.uleb128 0x4109	// (TAG: DW_TAG_GNU_call_site)
+	.byte	0	// DW_children_no
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x2115	// (DW_AT_GNU_tail_call)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x31	// (DW_AT_abstract_origin)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x8	// (abbrev code)
+	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
+	.byte	0	// DW_children_no
+	.uleb128 0x3	// (DW_AT_name)
+	.uleb128 0x8	// (DW_FORM_string)
+	.uleb128 0x3a	// (DW_AT_decl_file)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x3b	// (DW_AT_decl_line)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x39	// (DW_AT_decl_column)
+	.uleb128 0xb	// (DW_FORM_data1)
+	.uleb128 0x27	// (DW_AT_prototyped)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.uleb128 0x49	// (DW_AT_type)
+	.uleb128 0x13	// (DW_FORM_ref4)
+	.uleb128 0x11	// (DW_AT_low_pc)
+	.uleb128 0x1	// (DW_FORM_addr)
+	.uleb128 0x12	// (DW_AT_high_pc)
+	.uleb128 0x7	// (DW_FORM_data8)
+	.uleb128 0x40	// (DW_AT_frame_base)
+	.uleb128 0x18	// (DW_FORM_exprloc)
+	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	// (DW_FORM_flag_present)
+	.byte	0
+	.byte	0
+	.byte	0
+	.section	.debug_loc,"",@progbits
+.Ldebug_loc0:
+.LVUS0:
+	.uleb128 .LVU11	// View list begin (*.LVUS0)
+	.uleb128 .LVU12	// View list end (*.LVUS0)
+	.uleb128 .LVU12	// View list begin (*.LVUS0)
+	.uleb128 .LVU14	// View list end (*.LVUS0)
+	.uleb128 .LVU14	// View list begin (*.LVUS0)
+	.uleb128 0	// View list end (*.LVUS0)
+.LLST0:
+	.8byte	.LVL1	// Location list begin address (*.LLST0)
+	.8byte	.LVL1	// Location list end address (*.LLST0)
+	.2byte	0x1	// Location expression size
+	.byte	0x50	// DW_OP_reg0
+	.8byte	.LVL1	// Location list begin address (*.LLST0)
+	.8byte	.LVL2	// Location list end address (*.LLST0)
+	.2byte	0x3	// Location expression size
+	.byte	0x70	// DW_OP_breg0
+	.sleb128 1
+	.byte	0x9f	// DW_OP_stack_value
+	.8byte	.LVL2	// Location list begin address (*.LLST0)
+	.8byte	.LFE2	// Location list end address (*.LLST0)
+	.2byte	0x1	// Location expression size
+	.byte	0x50	// DW_OP_reg0
+	.8byte	0	// Location list terminator begin (*.LLST0)
+	.8byte	0	// Location list terminator end (*.LLST0)
+	.section	.debug_aranges,"",@progbits
+	.4byte	0x3c	// Length of Address Ranges Info
+	.2byte	0x2	// DWARF aranges version
+	.4byte	.Ldebug_info0	// Offset of Compilation Unit Info
+	.byte	0x8	// Size of Address
+	.byte	0	// Size of Segment Descriptor
+	.2byte	0	// Pad to 16 byte boundary
+	.2byte	0
+	.8byte	.Ltext0	// Address
+	.8byte	.Letext0-.Ltext0	// Length
+	.8byte	.LFB2	// Address
+	.8byte	.LFE2-.LFB2	// Length
+	.8byte	0
+	.8byte	0
+	.section	.debug_ranges,"",@progbits
+.Ldebug_ranges0:
+	.8byte	.Ltext0	// Offset 0
+	.8byte	.Letext0
+	.8byte	.LFB2	// Offset 0x10
+	.8byte	.LFE2
+	.8byte	0
+	.8byte	0
+	.section	.debug_line,"",@progbits
+.Ldebug_line0:
+	.section	.debug_str,"MS",@progbits,1
+.LASF0:
+	.string	"GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -O2 -fasynchronous-unwind-tables"
+.LASF2:
+	.string	"/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
+.LASF4:
+	.string	"answer"
+.LASF1:
+	.string	"tailcall.c"
+.LASF3:
+	.string	"main"
+	.ident	"GCC: (Debian 10.2.1-3) 10.2.1 20201224"
+	.section	.note.GNU-stack,"",@progbits
diff --git a/gdb/testsuite/gdb.btrace/arm-instruction_history.S b/gdb/testsuite/gdb.btrace/arm-instruction_history.S
new file mode 100644
index 00000000000..16ef0356993
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/arm-instruction_history.S
@@ -0,0 +1,31 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2021 Free Software Foundation, Inc.
+
+   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 3 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/>.  */
+
+.arm
+.text
+.globl loop
+.type  loop, %function
+loop:
+	movs r0, #2 /* bp.1 */
+L1:
+	cmp r0, #0
+	beq L2
+	subs r0, r0, #1
+	b L1
+L2:
+	bx lr /* bp.2 */
+
diff --git a/gdb/testsuite/gdb.btrace/arm-record_goto.S b/gdb/testsuite/gdb.btrace/arm-record_goto.S
new file mode 100644
index 00000000000..d68f1187a74
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/arm-record_goto.S
@@ -0,0 +1,432 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2021 Free Software Foundation, Inc.
+
+   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 3 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/>.
+
+   This file has been generated on an armv7 machine using:
+   gcc -S -dA -g record_goto.c -o arm-record_goto.S  */
+
+	.arch armv7-a
+	.eabi_attribute 28, 1	@ Tag_ABI_VFP_args
+	.eabi_attribute 20, 1	@ Tag_ABI_FP_denormal
+	.eabi_attribute 21, 1	@ Tag_ABI_FP_exceptions
+	.eabi_attribute 23, 3	@ Tag_ABI_FP_number_model
+	.eabi_attribute 24, 1	@ Tag_ABI_align8_needed
+	.eabi_attribute 25, 1	@ Tag_ABI_align8_preserved
+	.eabi_attribute 26, 2	@ Tag_ABI_enum_size
+	.eabi_attribute 30, 6	@ Tag_ABI_optimization_goals
+	.eabi_attribute 34, 1	@ Tag_CPU_unaligned_access
+	.eabi_attribute 18, 4	@ Tag_ABI_PCS_wchar_t
+	.file	"record_goto.c"
+	.text
+.Ltext0:
+	.cfi_sections	.debug_frame
+	.align	1
+	.global	fun1
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	fun1, %function
+fun1:
+.LFB0:
+	.file 1 "record_goto.c"
+	@ record_goto.c:22
+	.loc 1 22 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 1, uses_anonymous_args = 0
+	@ link register save eliminated.
+@ BLOCK 2 seq:0
+@ PRED: ENTRY (FALLTHRU)
+	push	{r7}
+	.cfi_def_cfa_offset 4
+	.cfi_offset 7, -4
+	add	r7, sp, #0
+	.cfi_def_cfa_register 7
+	@ record_goto.c:23
+	.loc 1 23 0
+	nop
+	mov	sp, r7
+	.cfi_def_cfa_register 13
+	@ sp needed
+	ldr	r7, [sp], #4
+	.cfi_restore 7
+	.cfi_def_cfa_offset 0
+@ SUCC: EXIT [100.0%] 
+	bx	lr
+	.cfi_endproc
+.LFE0:
+	.size	fun1, .-fun1
+	.align	1
+	.global	fun2
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	fun2, %function
+fun2:
+.LFB1:
+	@ record_goto.c:27
+	.loc 1 27 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 1, uses_anonymous_args = 0
+@ BLOCK 2 seq:0
+@ PRED: ENTRY (FALLTHRU)
+	push	{r7, lr}
+	.cfi_def_cfa_offset 8
+	.cfi_offset 7, -8
+	.cfi_offset 14, -4
+	add	r7, sp, #0
+	.cfi_def_cfa_register 7
+	@ record_goto.c:28
+	.loc 1 28 0
+	bl	fun1(PLT)
+	@ record_goto.c:29
+	.loc 1 29 0
+	nop
+@ SUCC: EXIT [100.0%] 
+	pop	{r7, pc}
+	.cfi_endproc
+.LFE1:
+	.size	fun2, .-fun2
+	.align	1
+	.global	fun3
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	fun3, %function
+fun3:
+.LFB2:
+	@ record_goto.c:33
+	.loc 1 33 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 1, uses_anonymous_args = 0
+@ BLOCK 2 seq:0
+@ PRED: ENTRY (FALLTHRU)
+	push	{r7, lr}
+	.cfi_def_cfa_offset 8
+	.cfi_offset 7, -8
+	.cfi_offset 14, -4
+	add	r7, sp, #0
+	.cfi_def_cfa_register 7
+	@ record_goto.c:34
+	.loc 1 34 0
+	bl	fun1(PLT)
+	@ record_goto.c:35
+	.loc 1 35 0
+	bl	fun2(PLT)
+	@ record_goto.c:36
+	.loc 1 36 0
+	nop
+@ SUCC: EXIT [100.0%] 
+	pop	{r7, pc}
+	.cfi_endproc
+.LFE2:
+	.size	fun3, .-fun3
+	.align	1
+	.global	fun4
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	fun4, %function
+fun4:
+.LFB3:
+	@ record_goto.c:40
+	.loc 1 40 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 1, uses_anonymous_args = 0
+@ BLOCK 2 seq:0
+@ PRED: ENTRY (FALLTHRU)
+	push	{r7, lr}
+	.cfi_def_cfa_offset 8
+	.cfi_offset 7, -8
+	.cfi_offset 14, -4
+	add	r7, sp, #0
+	.cfi_def_cfa_register 7
+	@ record_goto.c:41
+	.loc 1 41 0
+	bl	fun1(PLT)
+	@ record_goto.c:42
+	.loc 1 42 0
+	bl	fun2(PLT)
+	@ record_goto.c:43
+	.loc 1 43 0
+	bl	fun3(PLT)
+	@ record_goto.c:44
+	.loc 1 44 0
+	nop
+@ SUCC: EXIT [100.0%] 
+	pop	{r7, pc}
+	.cfi_endproc
+.LFE3:
+	.size	fun4, .-fun4
+	.align	1
+	.global	main
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	main, %function
+main:
+.LFB4:
+	@ record_goto.c:48
+	.loc 1 48 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 1, uses_anonymous_args = 0
+@ BLOCK 2 seq:0
+@ PRED: ENTRY (FALLTHRU)
+	push	{r7, lr}
+	.cfi_def_cfa_offset 8
+	.cfi_offset 7, -8
+	.cfi_offset 14, -4
+	add	r7, sp, #0
+	.cfi_def_cfa_register 7
+	@ record_goto.c:49
+	.loc 1 49 0
+	bl	fun4(PLT)
+	@ record_goto.c:50
+	.loc 1 50 0
+	movs	r3, #0
+	@ record_goto.c:51
+	.loc 1 51 0
+	mov	r0, r3
+@ SUCC: EXIT [100.0%] 
+	pop	{r7, pc}
+	.cfi_endproc
+.LFE4:
+	.size	main, .-main
+.Letext0:
+	.section	.debug_info,"",%progbits
+.Ldebug_info0:
+	.4byte	0x82	@ Length of Compilation Unit Info
+	.2byte	0x4	@ DWARF version number
+	.4byte	.Ldebug_abbrev0	@ Offset Into Abbrev. Section
+	.byte	0x4	@ Pointer Size (in bytes)
+	.uleb128 0x1	@ (DIE (0xb) DW_TAG_compile_unit)
+	.4byte	.LASF4	@ DW_AT_producer: "GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -fstack-protector-strong"
+	.byte	0xc	@ DW_AT_language
+	.4byte	.LASF5	@ DW_AT_name: "record_goto.c"
+	.4byte	.LASF6	@ DW_AT_comp_dir: "/home/ubuntu/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
+	.4byte	.Ltext0	@ DW_AT_low_pc
+	.4byte	.Letext0-.Ltext0	@ DW_AT_high_pc
+	.4byte	.Ldebug_line0	@ DW_AT_stmt_list
+	.uleb128 0x2	@ (DIE (0x25) DW_TAG_subprogram)
+			@ DW_AT_external
+	.4byte	.LASF7	@ DW_AT_name: "main"
+	.byte	0x1	@ DW_AT_decl_file (record_goto.c)
+	.byte	0x2f	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	0x3a	@ DW_AT_type
+	.4byte	.LFB4	@ DW_AT_low_pc
+	.4byte	.LFE4-.LFB4	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_tail_call_sites
+	.uleb128 0x3	@ (DIE (0x3a) DW_TAG_base_type)
+	.byte	0x4	@ DW_AT_byte_size
+	.byte	0x5	@ DW_AT_encoding
+	.ascii "int\0"	@ DW_AT_name
+	.uleb128 0x4	@ (DIE (0x41) DW_TAG_subprogram)
+			@ DW_AT_external
+	.4byte	.LASF0	@ DW_AT_name: "fun4"
+	.byte	0x1	@ DW_AT_decl_file (record_goto.c)
+	.byte	0x27	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	.LFB3	@ DW_AT_low_pc
+	.4byte	.LFE3-.LFB3	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_tail_call_sites
+	.uleb128 0x4	@ (DIE (0x52) DW_TAG_subprogram)
+			@ DW_AT_external
+	.4byte	.LASF1	@ DW_AT_name: "fun3"
+	.byte	0x1	@ DW_AT_decl_file (record_goto.c)
+	.byte	0x20	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	.LFB2	@ DW_AT_low_pc
+	.4byte	.LFE2-.LFB2	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_tail_call_sites
+	.uleb128 0x4	@ (DIE (0x63) DW_TAG_subprogram)
+			@ DW_AT_external
+	.4byte	.LASF2	@ DW_AT_name: "fun2"
+	.byte	0x1	@ DW_AT_decl_file (record_goto.c)
+	.byte	0x1a	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	.LFB1	@ DW_AT_low_pc
+	.4byte	.LFE1-.LFB1	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_tail_call_sites
+	.uleb128 0x5	@ (DIE (0x74) DW_TAG_subprogram)
+			@ DW_AT_external
+	.4byte	.LASF3	@ DW_AT_name: "fun1"
+	.byte	0x1	@ DW_AT_decl_file (record_goto.c)
+	.byte	0x15	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	.LFB0	@ DW_AT_low_pc
+	.4byte	.LFE0-.LFB0	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_call_sites
+	.byte	0	@ end of children of DIE 0xb
+	.section	.debug_abbrev,"",%progbits
+.Ldebug_abbrev0:
+	.uleb128 0x1	@ (abbrev code)
+	.uleb128 0x11	@ (TAG: DW_TAG_compile_unit)
+	.byte	0x1	@ DW_children_yes
+	.uleb128 0x25	@ (DW_AT_producer)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x13	@ (DW_AT_language)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x1b	@ (DW_AT_comp_dir)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x12	@ (DW_AT_high_pc)
+	.uleb128 0x6	@ (DW_FORM_data4)
+	.uleb128 0x10	@ (DW_AT_stmt_list)
+	.uleb128 0x17	@ (DW_FORM_sec_offset)
+	.byte	0
+	.byte	0
+	.uleb128 0x2	@ (abbrev code)
+	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
+	.byte	0	@ DW_children_no
+	.uleb128 0x3f	@ (DW_AT_external)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x27	@ (DW_AT_prototyped)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x49	@ (DW_AT_type)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x12	@ (DW_AT_high_pc)
+	.uleb128 0x6	@ (DW_FORM_data4)
+	.uleb128 0x40	@ (DW_AT_frame_base)
+	.uleb128 0x18	@ (DW_FORM_exprloc)
+	.uleb128 0x2116	@ (DW_AT_GNU_all_tail_call_sites)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.byte	0
+	.byte	0
+	.uleb128 0x3	@ (abbrev code)
+	.uleb128 0x24	@ (TAG: DW_TAG_base_type)
+	.byte	0	@ DW_children_no
+	.uleb128 0xb	@ (DW_AT_byte_size)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3e	@ (DW_AT_encoding)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0x8	@ (DW_FORM_string)
+	.byte	0
+	.byte	0
+	.uleb128 0x4	@ (abbrev code)
+	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
+	.byte	0	@ DW_children_no
+	.uleb128 0x3f	@ (DW_AT_external)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x27	@ (DW_AT_prototyped)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x12	@ (DW_AT_high_pc)
+	.uleb128 0x6	@ (DW_FORM_data4)
+	.uleb128 0x40	@ (DW_AT_frame_base)
+	.uleb128 0x18	@ (DW_FORM_exprloc)
+	.uleb128 0x2116	@ (DW_AT_GNU_all_tail_call_sites)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.byte	0
+	.byte	0
+	.uleb128 0x5	@ (abbrev code)
+	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
+	.byte	0	@ DW_children_no
+	.uleb128 0x3f	@ (DW_AT_external)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x27	@ (DW_AT_prototyped)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x12	@ (DW_AT_high_pc)
+	.uleb128 0x6	@ (DW_FORM_data4)
+	.uleb128 0x40	@ (DW_AT_frame_base)
+	.uleb128 0x18	@ (DW_FORM_exprloc)
+	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.byte	0
+	.byte	0
+	.byte	0
+	.section	.debug_aranges,"",%progbits
+	.4byte	0x1c	@ Length of Address Ranges Info
+	.2byte	0x2	@ DWARF Version
+	.4byte	.Ldebug_info0	@ Offset of Compilation Unit Info
+	.byte	0x4	@ Size of Address
+	.byte	0	@ Size of Segment Descriptor
+	.2byte	0	@ Pad to 8 byte boundary
+	.2byte	0
+	.4byte	.Ltext0	@ Address
+	.4byte	.Letext0-.Ltext0	@ Length
+	.4byte	0
+	.4byte	0
+	.section	.debug_line,"",%progbits
+.Ldebug_line0:
+	.section	.debug_str,"MS",%progbits,1
+.LASF5:
+	.ascii	"record_goto.c\000"
+.LASF2:
+	.ascii	"fun2\000"
+.LASF6:
+	.ascii	"/home/ubuntu/development/gdb/binutils-gdb/gdb/tests"
+	.ascii	"uite/gdb.btrace\000"
+.LASF4:
+	.ascii	"GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu"
+	.ascii	"=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -fstack-pro"
+	.ascii	"tector-strong\000"
+.LASF0:
+	.ascii	"fun4\000"
+.LASF3:
+	.ascii	"fun1\000"
+.LASF7:
+	.ascii	"main\000"
+.LASF1:
+	.ascii	"fun3\000"
+	.ident	"GCC: (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1) 7.4.0"
+	.section	.note.GNU-stack,"",%progbits
diff --git a/gdb/testsuite/gdb.btrace/arm-tailcall-only.S b/gdb/testsuite/gdb.btrace/arm-tailcall-only.S
new file mode 100644
index 00000000000..73a89c6e533
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/arm-tailcall-only.S
@@ -0,0 +1,503 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2016-2020 Free Software Foundation, Inc.
+
+   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 3 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/>.
+
+   This file has been generated on an armv7 machine using:
+   gcc -S -O2 -dA -g tailcall-only.c -o arm-tailcall-only.S  */
+
+	.eabi_attribute 28, 1	@ Tag_ABI_VFP_args
+	.eabi_attribute 20, 1	@ Tag_ABI_FP_denormal
+	.eabi_attribute 21, 1	@ Tag_ABI_FP_exceptions
+	.eabi_attribute 23, 3	@ Tag_ABI_FP_number_model
+	.eabi_attribute 24, 1	@ Tag_ABI_align8_needed
+	.eabi_attribute 25, 1	@ Tag_ABI_align8_preserved
+	.eabi_attribute 26, 2	@ Tag_ABI_enum_size
+	.eabi_attribute 30, 2	@ Tag_ABI_optimization_goals
+	.eabi_attribute 34, 1	@ Tag_CPU_unaligned_access
+	.eabi_attribute 18, 4	@ Tag_ABI_PCS_wchar_t
+	.file	"tailcall-only.c"
+	.text
+.Ltext0:
+	.cfi_sections	.debug_frame
+	.align	1
+	.p2align 2,,3
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	bar_1, %function
+bar_1:
+.LFB0:
+	.file 1 "tailcall-only.c"
+	@ tailcall-only.c:22
+	.loc 1 22 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+	@ link register save eliminated.
+@ BLOCK 2 freq:10000 seq:0
+@ PRED: ENTRY [100.0%]  (FALLTHRU)
+	@ tailcall-only.c:24
+	.loc 1 24 0
+	movs	r0, #42
+@ SUCC: EXIT [100.0%] 
+	bx	lr
+	.cfi_endproc
+.LFE0:
+	.size	bar_1, .-bar_1
+	.align	1
+	.p2align 2,,3
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	bar, %function
+bar:
+.LFB1:
+	@ tailcall-only.c:28
+	.loc 1 28 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+	@ link register save eliminated.
+@ BLOCK 2 freq:10000 seq:0
+@ PRED: ENTRY [100.0%]  (FALLTHRU)
+@ SUCC: EXIT [100.0%]  (ABNORMAL,SIBCALL)
+	@ tailcall-only.c:29
+	.loc 1 29 0
+	b	bar_1(PLT)
+.LVL0:
+	.cfi_endproc
+.LFE1:
+	.size	bar, .-bar
+	.align	1
+	.p2align 2,,3
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	foo_1, %function
+foo_1:
+.LFB2:
+	@ tailcall-only.c:34
+	.loc 1 34 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+	@ link register save eliminated.
+@ BLOCK 2 freq:10000 seq:0
+@ PRED: ENTRY [100.0%]  (FALLTHRU)
+@ SUCC: EXIT [100.0%]  (ABNORMAL,SIBCALL)
+	@ tailcall-only.c:35
+	.loc 1 35 0
+	b	bar(PLT)
+.LVL1:
+	.cfi_endproc
+.LFE2:
+	.size	foo_1, .-foo_1
+	.align	1
+	.p2align 2,,3
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	foo, %function
+foo:
+.LFB3:
+	@ tailcall-only.c:40
+	.loc 1 40 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+	@ link register save eliminated.
+@ BLOCK 2 freq:10000 seq:0
+@ PRED: ENTRY [100.0%]  (FALLTHRU)
+@ SUCC: EXIT [100.0%]  (ABNORMAL,SIBCALL)
+	@ tailcall-only.c:41
+	.loc 1 41 0
+	b	foo_1(PLT)
+.LVL2:
+	.cfi_endproc
+.LFE3:
+	.size	foo, .-foo
+	.section	.text.startup,"ax",%progbits
+	.align	1
+	.p2align 2,,3
+	.global	main
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	main, %function
+main:
+.LFB4:
+	@ tailcall-only.c:46
+	.loc 1 46 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+@ BLOCK 2 freq:10000 seq:0
+@ PRED: ENTRY [100.0%]  (FALLTHRU)
+	push	{r3, lr}
+	.cfi_def_cfa_offset 8
+	.cfi_offset 3, -8
+	.cfi_offset 14, -4
+	@ tailcall-only.c:49
+	.loc 1 49 0
+	bl	foo(PLT)
+.LVL3:
+	@ tailcall-only.c:53
+	.loc 1 53 0
+	adds	r0, r0, #1
+.LVL4:
+@ SUCC: EXIT [100.0%] 
+	pop	{r3, pc}
+	.cfi_endproc
+.LFE4:
+	.size	main, .-main
+	.text
+.Letext0:
+	.section	.debug_info,"",%progbits
+.Ldebug_info0:
+	.4byte	0xd9	@ Length of Compilation Unit Info
+	.2byte	0x4	@ DWARF version number
+	.4byte	.Ldebug_abbrev0	@ Offset Into Abbrev. Section
+	.byte	0x4	@ Pointer Size (in bytes)
+	.uleb128 0x1	@ (DIE (0xb) DW_TAG_compile_unit)
+	.4byte	.LASF1	@ DW_AT_producer: "GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -O2 -fstack-protector-strong"
+	.byte	0xc	@ DW_AT_language
+	.4byte	.LASF2	@ DW_AT_name: "tailcall-only.c"
+	.4byte	.LASF3	@ DW_AT_comp_dir: "/home/ubuntu/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
+	.4byte	.Ldebug_ranges0+0	@ DW_AT_ranges
+	.4byte	0	@ DW_AT_low_pc
+	.4byte	.Ldebug_line0	@ DW_AT_stmt_list
+	.uleb128 0x2	@ (DIE (0x25) DW_TAG_subprogram)
+			@ DW_AT_external
+	.4byte	.LASF4	@ DW_AT_name: "main"
+	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
+	.byte	0x2d	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	0x57	@ DW_AT_type
+	.4byte	.LFB4	@ DW_AT_low_pc
+	.4byte	.LFE4-.LFB4	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_call_sites
+	.4byte	0x57	@ DW_AT_sibling
+	.uleb128 0x3	@ (DIE (0x3e) DW_TAG_variable)
+	.4byte	.LASF5	@ DW_AT_name: "answer"
+	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
+	.byte	0x2f	@ DW_AT_decl_line
+	.4byte	0x57	@ DW_AT_type
+	.4byte	.LLST0	@ DW_AT_location
+	.uleb128 0x4	@ (DIE (0x4d) DW_TAG_GNU_call_site)
+	.4byte	.LVL3	@ DW_AT_low_pc
+	.4byte	0x5e	@ DW_AT_abstract_origin
+	.byte	0	@ end of children of DIE 0x25
+	.uleb128 0x5	@ (DIE (0x57) DW_TAG_base_type)
+	.byte	0x4	@ DW_AT_byte_size
+	.byte	0x5	@ DW_AT_encoding
+	.ascii "int\0"	@ DW_AT_name
+	.uleb128 0x6	@ (DIE (0x5e) DW_TAG_subprogram)
+	.ascii "foo\0"	@ DW_AT_name
+	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
+	.byte	0x27	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	0x57	@ DW_AT_type
+	.4byte	.LFB3	@ DW_AT_low_pc
+	.4byte	.LFE3-.LFB3	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_call_sites
+	.4byte	0x81	@ DW_AT_sibling
+	.uleb128 0x7	@ (DIE (0x77) DW_TAG_GNU_call_site)
+	.4byte	.LVL2	@ DW_AT_low_pc
+			@ DW_AT_GNU_tail_call
+	.4byte	0x81	@ DW_AT_abstract_origin
+	.byte	0	@ end of children of DIE 0x5e
+	.uleb128 0x8	@ (DIE (0x81) DW_TAG_subprogram)
+	.4byte	.LASF0	@ DW_AT_name: "foo_1"
+	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
+	.byte	0x21	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	0x57	@ DW_AT_type
+	.4byte	.LFB2	@ DW_AT_low_pc
+	.4byte	.LFE2-.LFB2	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_call_sites
+	.4byte	0xa4	@ DW_AT_sibling
+	.uleb128 0x7	@ (DIE (0x9a) DW_TAG_GNU_call_site)
+	.4byte	.LVL1	@ DW_AT_low_pc
+			@ DW_AT_GNU_tail_call
+	.4byte	0xa4	@ DW_AT_abstract_origin
+	.byte	0	@ end of children of DIE 0x81
+	.uleb128 0x6	@ (DIE (0xa4) DW_TAG_subprogram)
+	.ascii "bar\0"	@ DW_AT_name
+	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
+	.byte	0x1b	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	0x57	@ DW_AT_type
+	.4byte	.LFB1	@ DW_AT_low_pc
+	.4byte	.LFE1-.LFB1	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_call_sites
+	.4byte	0xc7	@ DW_AT_sibling
+	.uleb128 0x7	@ (DIE (0xbd) DW_TAG_GNU_call_site)
+	.4byte	.LVL0	@ DW_AT_low_pc
+			@ DW_AT_GNU_tail_call
+	.4byte	0xc7	@ DW_AT_abstract_origin
+	.byte	0	@ end of children of DIE 0xa4
+	.uleb128 0x9	@ (DIE (0xc7) DW_TAG_subprogram)
+	.4byte	.LASF6	@ DW_AT_name: "bar_1"
+	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
+	.byte	0x15	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	0x57	@ DW_AT_type
+	.4byte	.LFB0	@ DW_AT_low_pc
+	.4byte	.LFE0-.LFB0	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_call_sites
+	.byte	0	@ end of children of DIE 0xb
+	.section	.debug_abbrev,"",%progbits
+.Ldebug_abbrev0:
+	.uleb128 0x1	@ (abbrev code)
+	.uleb128 0x11	@ (TAG: DW_TAG_compile_unit)
+	.byte	0x1	@ DW_children_yes
+	.uleb128 0x25	@ (DW_AT_producer)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x13	@ (DW_AT_language)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x1b	@ (DW_AT_comp_dir)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x55	@ (DW_AT_ranges)
+	.uleb128 0x17	@ (DW_FORM_sec_offset)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x10	@ (DW_AT_stmt_list)
+	.uleb128 0x17	@ (DW_FORM_sec_offset)
+	.byte	0
+	.byte	0
+	.uleb128 0x2	@ (abbrev code)
+	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
+	.byte	0x1	@ DW_children_yes
+	.uleb128 0x3f	@ (DW_AT_external)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x27	@ (DW_AT_prototyped)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x49	@ (DW_AT_type)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x12	@ (DW_AT_high_pc)
+	.uleb128 0x6	@ (DW_FORM_data4)
+	.uleb128 0x40	@ (DW_AT_frame_base)
+	.uleb128 0x18	@ (DW_FORM_exprloc)
+	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x1	@ (DW_AT_sibling)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x3	@ (abbrev code)
+	.uleb128 0x34	@ (TAG: DW_TAG_variable)
+	.byte	0	@ DW_children_no
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x49	@ (DW_AT_type)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.uleb128 0x2	@ (DW_AT_location)
+	.uleb128 0x17	@ (DW_FORM_sec_offset)
+	.byte	0
+	.byte	0
+	.uleb128 0x4	@ (abbrev code)
+	.uleb128 0x4109	@ (TAG: DW_TAG_GNU_call_site)
+	.byte	0	@ DW_children_no
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x31	@ (DW_AT_abstract_origin)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x5	@ (abbrev code)
+	.uleb128 0x24	@ (TAG: DW_TAG_base_type)
+	.byte	0	@ DW_children_no
+	.uleb128 0xb	@ (DW_AT_byte_size)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3e	@ (DW_AT_encoding)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0x8	@ (DW_FORM_string)
+	.byte	0
+	.byte	0
+	.uleb128 0x6	@ (abbrev code)
+	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
+	.byte	0x1	@ DW_children_yes
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0x8	@ (DW_FORM_string)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x27	@ (DW_AT_prototyped)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x49	@ (DW_AT_type)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x12	@ (DW_AT_high_pc)
+	.uleb128 0x6	@ (DW_FORM_data4)
+	.uleb128 0x40	@ (DW_AT_frame_base)
+	.uleb128 0x18	@ (DW_FORM_exprloc)
+	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x1	@ (DW_AT_sibling)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x7	@ (abbrev code)
+	.uleb128 0x4109	@ (TAG: DW_TAG_GNU_call_site)
+	.byte	0	@ DW_children_no
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x2115	@ (DW_AT_GNU_tail_call)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x31	@ (DW_AT_abstract_origin)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x8	@ (abbrev code)
+	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
+	.byte	0x1	@ DW_children_yes
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x27	@ (DW_AT_prototyped)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x49	@ (DW_AT_type)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x12	@ (DW_AT_high_pc)
+	.uleb128 0x6	@ (DW_FORM_data4)
+	.uleb128 0x40	@ (DW_AT_frame_base)
+	.uleb128 0x18	@ (DW_FORM_exprloc)
+	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x1	@ (DW_AT_sibling)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x9	@ (abbrev code)
+	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
+	.byte	0	@ DW_children_no
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x27	@ (DW_AT_prototyped)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x49	@ (DW_AT_type)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x12	@ (DW_AT_high_pc)
+	.uleb128 0x6	@ (DW_FORM_data4)
+	.uleb128 0x40	@ (DW_AT_frame_base)
+	.uleb128 0x18	@ (DW_FORM_exprloc)
+	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.byte	0
+	.byte	0
+	.byte	0
+	.section	.debug_loc,"",%progbits
+.Ldebug_loc0:
+.LLST0:
+	.4byte	.LVL3	@ Location list begin address (*.LLST0)
+	.4byte	.LVL4	@ Location list end address (*.LLST0)
+	.2byte	0x3	@ Location expression size
+	.byte	0x70	@ DW_OP_breg0
+	.sleb128 1
+	.byte	0x9f	@ DW_OP_stack_value
+	.4byte	.LVL4	@ Location list begin address (*.LLST0)
+	.4byte	.LFE4	@ Location list end address (*.LLST0)
+	.2byte	0x1	@ Location expression size
+	.byte	0x50	@ DW_OP_reg0
+	.4byte	0	@ Location list terminator begin (*.LLST0)
+	.4byte	0	@ Location list terminator end (*.LLST0)
+	.section	.debug_aranges,"",%progbits
+	.4byte	0x24	@ Length of Address Ranges Info
+	.2byte	0x2	@ DWARF Version
+	.4byte	.Ldebug_info0	@ Offset of Compilation Unit Info
+	.byte	0x4	@ Size of Address
+	.byte	0	@ Size of Segment Descriptor
+	.2byte	0	@ Pad to 8 byte boundary
+	.2byte	0
+	.4byte	.Ltext0	@ Address
+	.4byte	.Letext0-.Ltext0	@ Length
+	.4byte	.LFB4	@ Address
+	.4byte	.LFE4-.LFB4	@ Length
+	.4byte	0
+	.4byte	0
+	.section	.debug_ranges,"",%progbits
+.Ldebug_ranges0:
+	.4byte	.Ltext0	@ Offset 0
+	.4byte	.Letext0
+	.4byte	.LFB4	@ Offset 0x8
+	.4byte	.LFE4
+	.4byte	0
+	.4byte	0
+	.section	.debug_line,"",%progbits
+.Ldebug_line0:
+	.section	.debug_str,"MS",%progbits,1
+.LASF2:
+	.ascii	"tailcall-only.c\000"
+.LASF5:
+	.ascii	"answer\000"
+.LASF1:
+	.ascii	"GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu"
+	.ascii	"=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -O2 -fstack"
+	.ascii	"-protector-strong\000"
+.LASF4:
+	.ascii	"main\000"
+.LASF6:
+	.ascii	"bar_1\000"
+.LASF3:
+	.ascii	"/home/ubuntu/development/gdb/binutils-gdb/gdb/tests"
+	.ascii	"uite/gdb.btrace\000"
+.LASF0:
+	.ascii	"foo_1\000"
+	.ident	"GCC: (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1) 7.4.0"
+	.section	.note.GNU-stack,"",%progbits
diff --git a/gdb/testsuite/gdb.btrace/arm-tailcall.S b/gdb/testsuite/gdb.btrace/arm-tailcall.S
new file mode 100644
index 00000000000..2f72dd12296
--- /dev/null
+++ b/gdb/testsuite/gdb.btrace/arm-tailcall.S
@@ -0,0 +1,390 @@
+ /* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2021 Free Software Foundation, Inc.
+
+   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 3 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/>.
+
+   This file has been generated on an armv7 machine using:
+   gcc -S -O2 -dA -g tailcall.c -o arm-tailcall.S  */
+
+	.eabi_attribute 28, 1	@ Tag_ABI_VFP_args
+	.eabi_attribute 20, 1	@ Tag_ABI_FP_denormal
+	.eabi_attribute 21, 1	@ Tag_ABI_FP_exceptions
+	.eabi_attribute 23, 3	@ Tag_ABI_FP_number_model
+	.eabi_attribute 24, 1	@ Tag_ABI_align8_needed
+	.eabi_attribute 25, 1	@ Tag_ABI_align8_preserved
+	.eabi_attribute 26, 2	@ Tag_ABI_enum_size
+	.eabi_attribute 30, 2	@ Tag_ABI_optimization_goals
+	.eabi_attribute 34, 1	@ Tag_CPU_unaligned_access
+	.eabi_attribute 18, 4	@ Tag_ABI_PCS_wchar_t
+	.file	"tailcall.c"
+	.text
+.Ltext0:
+	.cfi_sections	.debug_frame
+	.align	1
+	.p2align 2,,3
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	bar, %function
+bar:
+.LFB0:
+	.file 1 "tailcall.c"
+	@ tailcall.c:22
+	.loc 1 22 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+	@ link register save eliminated.
+@ BLOCK 2 freq:10000 seq:0
+@ PRED: ENTRY [100.0%]  (FALLTHRU)
+	@ tailcall.c:24
+	.loc 1 24 0
+	movs	r0, #42
+@ SUCC: EXIT [100.0%] 
+	bx	lr
+	.cfi_endproc
+.LFE0:
+	.size	bar, .-bar
+	.align	1
+	.p2align 2,,3
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	foo, %function
+foo:
+.LFB1:
+	@ tailcall.c:28
+	.loc 1 28 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+	@ link register save eliminated.
+@ BLOCK 2 freq:10000 seq:0
+@ PRED: ENTRY [100.0%]  (FALLTHRU)
+@ SUCC: EXIT [100.0%]  (ABNORMAL,SIBCALL)
+	@ tailcall.c:29
+	.loc 1 29 0
+	b	bar(PLT)
+.LVL0:
+	.cfi_endproc
+.LFE1:
+	.size	foo, .-foo
+	.section	.text.startup,"ax",%progbits
+	.align	1
+	.p2align 2,,3
+	.global	main
+	.syntax unified
+	.thumb
+	.thumb_func
+	.fpu vfpv3-d16
+	.type	main, %function
+main:
+.LFB2:
+	@ tailcall.c:34
+	.loc 1 34 0
+	.cfi_startproc
+	@ args = 0, pretend = 0, frame = 0
+	@ frame_needed = 0, uses_anonymous_args = 0
+@ BLOCK 2 freq:10000 seq:0
+@ PRED: ENTRY [100.0%]  (FALLTHRU)
+	push	{r3, lr}
+	.cfi_def_cfa_offset 8
+	.cfi_offset 3, -8
+	.cfi_offset 14, -4
+	@ tailcall.c:37
+	.loc 1 37 0
+	bl	foo(PLT)
+.LVL1:
+	@ tailcall.c:41
+	.loc 1 41 0
+	adds	r0, r0, #1
+.LVL2:
+@ SUCC: EXIT [100.0%] 
+	pop	{r3, pc}
+	.cfi_endproc
+.LFE2:
+	.size	main, .-main
+	.text
+.Letext0:
+	.section	.debug_info,"",%progbits
+.Ldebug_info0:
+	.4byte	0x93	@ Length of Compilation Unit Info
+	.2byte	0x4	@ DWARF version number
+	.4byte	.Ldebug_abbrev0	@ Offset Into Abbrev. Section
+	.byte	0x4	@ Pointer Size (in bytes)
+	.uleb128 0x1	@ (DIE (0xb) DW_TAG_compile_unit)
+	.4byte	.LASF0	@ DW_AT_producer: "GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -O2 -fstack-protector-strong"
+	.byte	0xc	@ DW_AT_language
+	.4byte	.LASF1	@ DW_AT_name: "tailcall.c"
+	.4byte	.LASF2	@ DW_AT_comp_dir: "/home/ubuntu/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
+	.4byte	.Ldebug_ranges0+0	@ DW_AT_ranges
+	.4byte	0	@ DW_AT_low_pc
+	.4byte	.Ldebug_line0	@ DW_AT_stmt_list
+	.uleb128 0x2	@ (DIE (0x25) DW_TAG_subprogram)
+			@ DW_AT_external
+	.4byte	.LASF3	@ DW_AT_name: "main"
+	.byte	0x1	@ DW_AT_decl_file (tailcall.c)
+	.byte	0x21	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	0x57	@ DW_AT_type
+	.4byte	.LFB2	@ DW_AT_low_pc
+	.4byte	.LFE2-.LFB2	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_call_sites
+	.4byte	0x57	@ DW_AT_sibling
+	.uleb128 0x3	@ (DIE (0x3e) DW_TAG_variable)
+	.4byte	.LASF4	@ DW_AT_name: "answer"
+	.byte	0x1	@ DW_AT_decl_file (tailcall.c)
+	.byte	0x23	@ DW_AT_decl_line
+	.4byte	0x57	@ DW_AT_type
+	.4byte	.LLST0	@ DW_AT_location
+	.uleb128 0x4	@ (DIE (0x4d) DW_TAG_GNU_call_site)
+	.4byte	.LVL1	@ DW_AT_low_pc
+	.4byte	0x5e	@ DW_AT_abstract_origin
+	.byte	0	@ end of children of DIE 0x25
+	.uleb128 0x5	@ (DIE (0x57) DW_TAG_base_type)
+	.byte	0x4	@ DW_AT_byte_size
+	.byte	0x5	@ DW_AT_encoding
+	.ascii "int\0"	@ DW_AT_name
+	.uleb128 0x6	@ (DIE (0x5e) DW_TAG_subprogram)
+	.ascii "foo\0"	@ DW_AT_name
+	.byte	0x1	@ DW_AT_decl_file (tailcall.c)
+	.byte	0x1b	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	0x57	@ DW_AT_type
+	.4byte	.LFB1	@ DW_AT_low_pc
+	.4byte	.LFE1-.LFB1	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_call_sites
+	.4byte	0x81	@ DW_AT_sibling
+	.uleb128 0x7	@ (DIE (0x77) DW_TAG_GNU_call_site)
+	.4byte	.LVL0	@ DW_AT_low_pc
+			@ DW_AT_GNU_tail_call
+	.4byte	0x81	@ DW_AT_abstract_origin
+	.byte	0	@ end of children of DIE 0x5e
+	.uleb128 0x8	@ (DIE (0x81) DW_TAG_subprogram)
+	.ascii "bar\0"	@ DW_AT_name
+	.byte	0x1	@ DW_AT_decl_file (tailcall.c)
+	.byte	0x15	@ DW_AT_decl_line
+			@ DW_AT_prototyped
+	.4byte	0x57	@ DW_AT_type
+	.4byte	.LFB0	@ DW_AT_low_pc
+	.4byte	.LFE0-.LFB0	@ DW_AT_high_pc
+	.uleb128 0x1	@ DW_AT_frame_base
+	.byte	0x9c	@ DW_OP_call_frame_cfa
+			@ DW_AT_GNU_all_call_sites
+	.byte	0	@ end of children of DIE 0xb
+	.section	.debug_abbrev,"",%progbits
+.Ldebug_abbrev0:
+	.uleb128 0x1	@ (abbrev code)
+	.uleb128 0x11	@ (TAG: DW_TAG_compile_unit)
+	.byte	0x1	@ DW_children_yes
+	.uleb128 0x25	@ (DW_AT_producer)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x13	@ (DW_AT_language)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x1b	@ (DW_AT_comp_dir)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x55	@ (DW_AT_ranges)
+	.uleb128 0x17	@ (DW_FORM_sec_offset)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x10	@ (DW_AT_stmt_list)
+	.uleb128 0x17	@ (DW_FORM_sec_offset)
+	.byte	0
+	.byte	0
+	.uleb128 0x2	@ (abbrev code)
+	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
+	.byte	0x1	@ DW_children_yes
+	.uleb128 0x3f	@ (DW_AT_external)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x27	@ (DW_AT_prototyped)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x49	@ (DW_AT_type)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x12	@ (DW_AT_high_pc)
+	.uleb128 0x6	@ (DW_FORM_data4)
+	.uleb128 0x40	@ (DW_AT_frame_base)
+	.uleb128 0x18	@ (DW_FORM_exprloc)
+	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x1	@ (DW_AT_sibling)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x3	@ (abbrev code)
+	.uleb128 0x34	@ (TAG: DW_TAG_variable)
+	.byte	0	@ DW_children_no
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0xe	@ (DW_FORM_strp)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x49	@ (DW_AT_type)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.uleb128 0x2	@ (DW_AT_location)
+	.uleb128 0x17	@ (DW_FORM_sec_offset)
+	.byte	0
+	.byte	0
+	.uleb128 0x4	@ (abbrev code)
+	.uleb128 0x4109	@ (TAG: DW_TAG_GNU_call_site)
+	.byte	0	@ DW_children_no
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x31	@ (DW_AT_abstract_origin)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x5	@ (abbrev code)
+	.uleb128 0x24	@ (TAG: DW_TAG_base_type)
+	.byte	0	@ DW_children_no
+	.uleb128 0xb	@ (DW_AT_byte_size)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3e	@ (DW_AT_encoding)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0x8	@ (DW_FORM_string)
+	.byte	0
+	.byte	0
+	.uleb128 0x6	@ (abbrev code)
+	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
+	.byte	0x1	@ DW_children_yes
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0x8	@ (DW_FORM_string)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x27	@ (DW_AT_prototyped)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x49	@ (DW_AT_type)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x12	@ (DW_AT_high_pc)
+	.uleb128 0x6	@ (DW_FORM_data4)
+	.uleb128 0x40	@ (DW_AT_frame_base)
+	.uleb128 0x18	@ (DW_FORM_exprloc)
+	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x1	@ (DW_AT_sibling)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x7	@ (abbrev code)
+	.uleb128 0x4109	@ (TAG: DW_TAG_GNU_call_site)
+	.byte	0	@ DW_children_no
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x2115	@ (DW_AT_GNU_tail_call)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x31	@ (DW_AT_abstract_origin)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.byte	0
+	.byte	0
+	.uleb128 0x8	@ (abbrev code)
+	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
+	.byte	0	@ DW_children_no
+	.uleb128 0x3	@ (DW_AT_name)
+	.uleb128 0x8	@ (DW_FORM_string)
+	.uleb128 0x3a	@ (DW_AT_decl_file)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x3b	@ (DW_AT_decl_line)
+	.uleb128 0xb	@ (DW_FORM_data1)
+	.uleb128 0x27	@ (DW_AT_prototyped)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.uleb128 0x49	@ (DW_AT_type)
+	.uleb128 0x13	@ (DW_FORM_ref4)
+	.uleb128 0x11	@ (DW_AT_low_pc)
+	.uleb128 0x1	@ (DW_FORM_addr)
+	.uleb128 0x12	@ (DW_AT_high_pc)
+	.uleb128 0x6	@ (DW_FORM_data4)
+	.uleb128 0x40	@ (DW_AT_frame_base)
+	.uleb128 0x18	@ (DW_FORM_exprloc)
+	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
+	.uleb128 0x19	@ (DW_FORM_flag_present)
+	.byte	0
+	.byte	0
+	.byte	0
+	.section	.debug_loc,"",%progbits
+.Ldebug_loc0:
+.LLST0:
+	.4byte	.LVL1	@ Location list begin address (*.LLST0)
+	.4byte	.LVL2	@ Location list end address (*.LLST0)
+	.2byte	0x3	@ Location expression size
+	.byte	0x70	@ DW_OP_breg0
+	.sleb128 1
+	.byte	0x9f	@ DW_OP_stack_value
+	.4byte	.LVL2	@ Location list begin address (*.LLST0)
+	.4byte	.LFE2	@ Location list end address (*.LLST0)
+	.2byte	0x1	@ Location expression size
+	.byte	0x50	@ DW_OP_reg0
+	.4byte	0	@ Location list terminator begin (*.LLST0)
+	.4byte	0	@ Location list terminator end (*.LLST0)
+	.section	.debug_aranges,"",%progbits
+	.4byte	0x24	@ Length of Address Ranges Info
+	.2byte	0x2	@ DWARF Version
+	.4byte	.Ldebug_info0	@ Offset of Compilation Unit Info
+	.byte	0x4	@ Size of Address
+	.byte	0	@ Size of Segment Descriptor
+	.2byte	0	@ Pad to 8 byte boundary
+	.2byte	0
+	.4byte	.Ltext0	@ Address
+	.4byte	.Letext0-.Ltext0	@ Length
+	.4byte	.LFB2	@ Address
+	.4byte	.LFE2-.LFB2	@ Length
+	.4byte	0
+	.4byte	0
+	.section	.debug_ranges,"",%progbits
+.Ldebug_ranges0:
+	.4byte	.Ltext0	@ Offset 0
+	.4byte	.Letext0
+	.4byte	.LFB2	@ Offset 0x8
+	.4byte	.LFE2
+	.4byte	0
+	.4byte	0
+	.section	.debug_line,"",%progbits
+.Ldebug_line0:
+	.section	.debug_str,"MS",%progbits,1
+.LASF0:
+	.ascii	"GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu"
+	.ascii	"=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -O2 -fstack"
+	.ascii	"-protector-strong\000"
+.LASF4:
+	.ascii	"answer\000"
+.LASF1:
+	.ascii	"tailcall.c\000"
+.LASF2:
+	.ascii	"/home/ubuntu/development/gdb/binutils-gdb/gdb/tests"
+	.ascii	"uite/gdb.btrace\000"
+.LASF3:
+	.ascii	"main\000"
+	.ident	"GCC: (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1) 7.4.0"
+	.section	.note.GNU-stack,"",%progbits
diff --git a/gdb/testsuite/gdb.btrace/buffer-size.exp b/gdb/testsuite/gdb.btrace/buffer-size.exp
index ea4e36c1593..23f859dce9c 100644
--- a/gdb/testsuite/gdb.btrace/buffer-size.exp
+++ b/gdb/testsuite/gdb.btrace/buffer-size.exp
@@ -32,10 +32,17 @@ if ![runto_main] {
     return -1
 }
 
-gdb_test_no_output "set record btrace bts buffer-size 1"
-gdb_test_no_output "set record btrace pt buffer-size 1"
-gdb_test "show record btrace bts buffer-size" "The record/replay bts buffer size is 1\.\r"
-gdb_test "show record btrace pt buffer-size" "The record/replay pt buffer size is 1\.\r"
+set btrace_type ""
+if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+    set btrace_type { "bts" "pt" }
+} elseif {[istarget "arm*-*-*"]|| [istarget "aarch64*-*-*"]} {
+    set btrace_type { "etm" } 
+}
+
+foreach_with_prefix format $btrace_type {
+    gdb_test_no_output "set record btrace $format buffer-size 1"
+    gdb_test "show record btrace $format buffer-size" "The record/replay $format buffer size is 1\.\r"
+}
 
 gdb_test_no_output "record btrace"
 gdb_test "info record" [multi_line \
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
index 403085c083f..3a6e7362f74 100644
--- a/gdb/testsuite/gdb.btrace/instruction_history.exp
+++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
@@ -22,7 +22,17 @@ if { [skip_btrace_tests] } {
     return -1
 }
 
-standard_testfile .c .S
+set test_prefix ""
+if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+    set test_prefix "x86"
+} elseif {[istarget "arm*-*-*"]} {
+    set test_prefix "arm"
+} elseif {[istarget "aarch64*-*-*"]} {
+    set test_prefix "aarch64"
+}
+
+standard_testfile instruction_history.c $test_prefix-instruction_history.S
+
 if [prepare_for_testing "failed to prepare" $testfile "$srcfile $srcfile2" {debug}] {
     return -1
 }
@@ -67,40 +77,108 @@ if { $traced != 11 } {
     pass $message
 }
 
-# test that we see the expected instructions
-gdb_test "record instruction-history 3,7" [multi_line \
+if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+  set test_pattern(0) "[multi_line \
       "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
       "4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
       "5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
       "6\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
       "7\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
-  ]
-
-gdb_test "record instruction-history /f 3,+5" [multi_line \
+    ]"
+  set test_pattern(1) "[multi_line \
       "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
       "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
       "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
       "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
       "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
-  ]
-
-gdb_test "record instruction-history /p 7,-5" [multi_line \
+      ]"
+  set test_pattern(2) "[multi_line \
       "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
       "4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
       "5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
       "6\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
       "7\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
-  ]
-
-gdb_test "record instruction-history /pf 3,7" [multi_line \
+      ]"
+  set test_pattern(3) "[multi_line \
       "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
       "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
       "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
       "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
       "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
-  ]
+      ]"
+  set test_pattern(4) "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+} elseif {[istarget "arm*-*-*"]} {
+  set test_pattern(0) "[multi_line \
+      "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>" \
+      "4\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tsubs\tr0, r0, #1" \
+      "5\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>" \
+      "6\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tcmp\tr0, #0" \
+      "7\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>\r" \
+      ]"
+  set test_pattern(1) "[multi_line \
+      "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>" \
+      "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tsubs\tr0, r0, #1" \
+      "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>" \
+      "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp\tr0, #0" \
+      "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>\r" \
+      ]"
+  set test_pattern(2) "[multi_line \
+      "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>" \
+      "4\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tsubs\tr0, r0, #1" \
+      "5\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>" \
+      "6\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tcmp\tr0, #0" \
+      "7\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>\r" \
+      ]"
+  set test_pattern(3)  "[multi_line \
+      "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>" \
+      "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tsubs\tr0, r0, #1" \
+      "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>" \
+      "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp\tr0, #0" \
+      "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>\r" \
+      ]"
+  set test_pattern(4) "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>\r"
+} elseif {[istarget "aarch64*-*-*"]} {
+  set test_pattern(0) "[multi_line \
+      "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*" \
+      "4\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tsubs\tx0, x0, #0x1.*" \
+      "5\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>.*" \
+      "6\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tcmp\tx0, #0x0.*" \
+      "7\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*\r" \
+    ]"
+  set test_pattern(1) "[multi_line \
+      "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*" \
+      "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tsubs\tx0, x0, #0x1.*" \
+      "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>.*" \
+      "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp\tx0, #0x0.*" \
+      "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*\r" \
+      ]"
+  set test_pattern(2) "[multi_line \
+      "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*" \
+      "4\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tsubs\tx0, x0, #0x1.*" \
+      "5\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>.*" \
+      "6\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tcmp\tx0, #0x0.*" \
+      "7\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*\r" \
+      ]"
+  set test_pattern(3) "[multi_line \
+      "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\\.eq\t0x\[0-9a-f\]+ <L2>.*" \
+      "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tsubs\tx0, x0, #0x1.*" \
+      "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>.*" \
+      "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp\tx0, #0x0.*" \
+      "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\\.eq\t0x\[0-9a-f\]+ <L2>.*\r" \
+      ]"
+  set test_pattern(4) "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>  // b\.none\r"
+}
+
+# test that we see the expected instructions
+gdb_test "record instruction-history 3,7" $test_pattern(0)
+
+gdb_test "record instruction-history /f 3,+5" $test_pattern(1)
+
+gdb_test "record instruction-history /p 7,-5" $test_pattern(2)
+
+gdb_test "record instruction-history /pf 3,7"  $test_pattern(3)
 
-gdb_test "record instruction-history 3,3" "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
+gdb_test "record instruction-history 3,3" $test_pattern(4)
 
 # the following tests are checking the iterators
 # to avoid lots of regexps, we just check the number of lines that
diff --git a/gdb/testsuite/gdb.btrace/non-stop.exp b/gdb/testsuite/gdb.btrace/non-stop.exp
index e509d65d660..fbc4cda7dd6 100644
--- a/gdb/testsuite/gdb.btrace/non-stop.exp
+++ b/gdb/testsuite/gdb.btrace/non-stop.exp
@@ -31,6 +31,12 @@ save_vars { GDBFLAGS } {
     clean_restart $testfile
 }
 
+if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+    set loop_position 2
+} elseif {[istarget "arm*-*-*"] || [istarget "aarch64*-*-*"]} {
+    set loop_position 3
+}
+
 if ![runto_main] {
     untested "failed to run to main"
     return -1
@@ -111,87 +117,99 @@ gdb_test "thread apply all info rec" ".*"
 gdb_test "info threads" ".*"
 
 with_test_prefix "navigate" {
-    gdb_test "thread apply 1 record goto 3" "$loop_line"
-    gdb_test "thread apply 2 record goto 4" "$loop_line"
+    gdb_test "thread apply 1 record goto [expr {$loop_position + 1}]" "$loop_line"
+    gdb_test "thread apply 2 record goto [expr {$loop_position + 2}]" "$loop_line"
     gdb_test "thread apply 1 info record" \
-        ".*Replay in progress\.  At instruction 3\." "thread 1 at insn 3"
+        ".*Replay in progress\.  At instruction [expr {$loop_position + 1}]\." "thread 1 at insn $loop_position"
     gdb_test "thread apply 2 info record" \
-        ".*Replay in progress\.  At instruction 4\." "thread 2 at insn 4"
+        ".*Replay in progress\.  At instruction [expr {$loop_position + 2}]\." "thread 2 at insn [expr {$loop_position + 2}]"
 
-    gdb_test "thread apply all record goto 5" "$loop_line"
+    gdb_test "thread apply all record goto [expr {$loop_position + 3}]" "$loop_line"
     gdb_test "thread apply 1 info record" \
-        ".*Replay in progress\.  At instruction 5\." "thread 1 at insn 5"
+        ".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\." "thread 1 at insn [expr {$loop_position + 3}]"
     gdb_test "thread apply 2 info record" \
-        ".*Replay in progress\.  At instruction 5\." "thread 2 at insn 5"
+        ".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\." "thread 2 at insn [expr {$loop_position + 3}]"
 }
 
 with_test_prefix "step" {
+    with_test_prefix "fixture" {
+	gdb_test "thread apply 1 record goto [expr {$loop_position + 3}]" ".*"
+	gdb_test "thread apply 2 record goto [expr {$loop_position + 3}]" ".*"
+    }
     with_test_prefix "thread 1" {
         gdb_test "thread apply 1 stepi 2" "$loop_line"
         gdb_test "thread apply 1 info record" \
-            ".*Replay in progress\.  At instruction 7\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 5}]\."
         gdb_test "thread apply 2 info record" \
-            ".*Replay in progress\.  At instruction 5\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\."
     }
 
     with_test_prefix "thread 2" {
         gdb_test "thread apply 2 stepi 3" "$loop_line"
         gdb_test "thread apply 1 info record" \
-            ".*Replay in progress\.  At instruction 7\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 5}]\."
         gdb_test "thread apply 2 info record" \
-            ".*Replay in progress\.  At instruction 8\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 6}]\."
     }
 
     with_test_prefix "all" {
         gdb_cont_to all "stepi 4" "$loop_line" 2
         gdb_test "thread apply 1 info record" \
-            ".*Replay in progress\.  At instruction 11\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 9}]\."
         gdb_test "thread apply 2 info record" \
-            ".*Replay in progress\.  At instruction 12\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 10}]\."
     }
 }
 
 with_test_prefix "reverse-step" {
+    with_test_prefix "fixture" {
+        gdb_test "thread apply 1 record goto [expr {$loop_position + 9}]" ".*"
+        gdb_test "thread apply 2 record goto [expr {$loop_position + 10}]" ".*"
+    }
     with_test_prefix "thread 1" {
         gdb_test "thread apply 1 reverse-stepi 2" "$loop_line"
         gdb_test "thread apply 1 info record" \
-            ".*Replay in progress\.  At instruction 9\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 7}]\."
         gdb_test "thread apply 2 info record" \
-            ".*Replay in progress\.  At instruction 12\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 10}]\."
     }
 
     with_test_prefix "thread 2" {
         gdb_test "thread apply 2 reverse-stepi 3" "$loop_line"
         gdb_test "thread apply 1 info record" \
-            ".*Replay in progress\.  At instruction 9\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 7}]\."
         gdb_test "thread apply 2 info record" \
-            ".*Replay in progress\.  At instruction 9\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 7}]\."
     }
 
     with_test_prefix "all" {
         gdb_cont_to all "reverse-stepi 4" "$loop_line" 2
         gdb_test "thread apply 1 info record" \
-            ".*Replay in progress\.  At instruction 5\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\."
         gdb_test "thread apply 2 info record" \
-            ".*Replay in progress\.  At instruction 5\."
+            ".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\."
     }
 }
 
 with_test_prefix "continue" {
+    with_test_prefix "fixture" {
+	gdb_test "thread apply 1 record goto [expr {$loop_position + 3}]" ".*"
+	gdb_test "thread apply 2 record goto [expr {$loop_position + 3}]" ".*"
+    }
     with_test_prefix "thread 1" {
 	with_test_prefix "continue" {
 	    gdb_cont_to_no_history 1 "continue" 1
 	    gdb_test "thread apply 1 info record" \
 		".*Recorded \[0-9\]+ instructions \[^\\\r\\\n\]*"
 	    gdb_test "thread apply 2 info record" \
-		".*Replay in progress\.  At instruction 5\."
+		".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\."
 	}
 	with_test_prefix "reverse-continue" {
 	    gdb_cont_to_no_history 1 "reverse-continue" 1
 	    gdb_test "thread apply 1 info record" \
 		".*Replay in progress\.  At instruction 1\."
 	    gdb_test "thread apply 2 info record" \
-		".*Replay in progress\.  At instruction 5\."
+		".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\."
 	}
     }
 
diff --git a/gdb/testsuite/gdb.btrace/record_goto.exp b/gdb/testsuite/gdb.btrace/record_goto.exp
index 75d76da1c7f..267d4b412bc 100644
--- a/gdb/testsuite/gdb.btrace/record_goto.exp
+++ b/gdb/testsuite/gdb.btrace/record_goto.exp
@@ -35,21 +35,149 @@ if [info exists COMPILE] {
     # make check RUNTESTFLAGS="gdb.btrace/record_goto.exp COMPILE=1"
     standard_testfile record_goto.c
     lappend opts debug
-} elseif {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+} else {
+  set test_prefix ""
+  if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
     if {[is_amd64_regs_target]} {
-		standard_testfile x86_64-record_goto.S
+      set test_prefix "x86_64"
     } else {
-		standard_testfile i686-record_goto.S
+      set test_prefix "i686"
     }
-} else {
+  } elseif {[istarget "arm*-*-*"]} {
+      set test_prefix "arm"
+  } elseif {[istarget "aarch64*-*-*"]} {
+    set test_prefix "aarch64"
+  } else {
     unsupported "target architecture not supported"
     return -1
+  }
+  standard_testfile $test_prefix-record_goto.S
 }
 
 if [prepare_for_testing "failed to prepare" $testfile $srcfile $opts] {
     return -1
 }
 
+if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+    set function_positions(0) 19
+    set function_positions(1) 27
+    set function_positions(2) 2
+    set function_positions(end) 40
+    set function_positions(3) 39
+
+    set sequence_begin(1) 1
+    set sequence_end(1) 1
+    set sequence_begin(2) 2
+    set sequence_end(2) 4
+    set sequence_begin(3) 5
+    set sequence_end(3) 8
+    set sequence_begin(4) 9
+    set sequence_end(4) 9
+    set sequence_begin(5) 10
+    set sequence_end(5) 12
+    set sequence_begin(6) 13
+    set sequence_end(6) 16
+    set sequence_begin(7) 17
+    set sequence_end(7) 18
+    set sequence_begin(8) 19
+    set sequence_end(8) 19
+    set sequence_begin(9) 20
+    set sequence_end(9) 22
+    set sequence_begin(10) 23
+    set sequence_end(10) 26
+    set sequence_begin(11) 27
+    set sequence_end(11) 27
+    set sequence_begin(12) 28
+    set sequence_end(12) 30
+    set sequence_begin(13) 31
+    set sequence_end(13) 34
+    set sequence_begin(14) 35
+    set sequence_end(14) 36
+    set sequence_begin(15) 37
+    set sequence_end(15) 38
+    set sequence_begin(16) 39
+    set sequence_end(16) 40
+
+} elseif {[istarget "arm*-*-*"]} {
+    set function_positions(0) 23
+    set function_positions(1) 33
+    set function_positions(2) 2
+    set function_positions(end) 48
+    set function_positions(3) 47
+
+    set sequence_begin(1) 1
+    set sequence_end(1) 1
+    set sequence_begin(2) 2
+    set sequence_end(2) 4
+    set sequence_begin(3) 5
+    set sequence_end(3) 10
+    set sequence_begin(4) 11
+    set sequence_end(4) 11
+    set sequence_begin(5) 12
+    set sequence_end(5) 14
+    set sequence_begin(6) 15
+    set sequence_end(6) 20
+    set sequence_begin(7) 21
+    set sequence_end(7) 22
+    set sequence_begin(8) 23
+    set sequence_end(8) 23
+    set sequence_begin(9) 24
+    set sequence_end(9) 26
+    set sequence_begin(10) 27
+    set sequence_end(10) 32
+    set sequence_begin(11) 33
+    set sequence_end(11) 33
+    set sequence_begin(12) 34
+    set sequence_end(12) 36
+    set sequence_begin(13) 37
+    set sequence_end(13) 42
+    set sequence_begin(14) 43
+    set sequence_end(14) 44
+    set sequence_begin(15) 45
+    set sequence_end(15) 46
+    set sequence_begin(16) 47
+    set sequence_end(16) 48
+} elseif {[istarget "aarch64*-*-*"]} {
+    set function_positions(0) 16
+    set function_positions(1) 22
+    set function_positions(2) 2
+    set function_positions(end) 36
+    set function_positions(3) 35
+
+    set sequence_begin(1) 1
+    set sequence_end(1) 1
+    set sequence_begin(2) 2
+    set sequence_end(2) 4
+    set sequence_begin(3) 5
+    set sequence_end(3) 6
+    set sequence_begin(4) 7
+    set sequence_end(4) 7
+    set sequence_begin(5) 8
+    set sequence_end(5) 10
+    set sequence_begin(6) 11
+    set sequence_end(6) 12
+    set sequence_begin(7) 13
+    set sequence_end(7) 15
+    set sequence_begin(8) 16
+    set sequence_end(8) 16
+    set sequence_begin(9) 17
+    set sequence_end(9) 19
+    set sequence_begin(10) 20
+    set sequence_end(10) 21
+    set sequence_begin(11) 22
+    set sequence_end(11) 22
+    set sequence_begin(12) 23
+    set sequence_end(12) 25
+    set sequence_begin(13) 26
+    set sequence_end(13) 27
+    set sequence_begin(14) 28
+    set sequence_end(14) 30
+    set sequence_begin(15) 31
+    set sequence_end(15) 33
+    set sequence_begin(16) 34
+    set sequence_end(16) 36
+}
+
 if ![runto_main] {
     untested "failed to run to main"
     return -1
@@ -65,43 +193,43 @@ gdb_test "next"
 
 # start by listing all functions
 gdb_test "record function-call-history /ci 1, +20" [multi_line \
-  "1\tmain\tinst 1,1" \
-  "2\t  fun4\tinst 2,4" \
-  "3\t    fun1\tinst 5,8" \
-  "4\t  fun4\tinst 9,9" \
-  "5\t    fun2\tinst 10,12" \
-  "6\t      fun1\tinst 13,16" \
-  "7\t    fun2\tinst 17,18" \
-  "8\t  fun4\tinst 19,19" \
-  "9\t    fun3\tinst 20,22" \
-  "10\t      fun1\tinst 23,26" \
-  "11\t    fun3\tinst 27,27" \
-  "12\t      fun2\tinst 28,30" \
-  "13\t        fun1\tinst 31,34" \
-  "14\t      fun2\tinst 35,36" \
-  "15\t    fun3\tinst 37,38" \
-  "16\t  fun4\tinst 39,40" \
+  "1\tmain\tinst $sequence_begin(1),$sequence_end(1)" \
+  "2\t  fun4\tinst $sequence_begin(2),$sequence_end(2)" \
+  "3\t    fun1\tinst $sequence_begin(3),$sequence_end(3)" \
+  "4\t  fun4\tinst $sequence_begin(4),$sequence_end(4)" \
+  "5\t    fun2\tinst $sequence_begin(5),$sequence_end(5)" \
+  "6\t      fun1\tinst $sequence_begin(6),$sequence_end(6)" \
+  "7\t    fun2\tinst $sequence_begin(7),$sequence_end(7)" \
+  "8\t  fun4\tinst $sequence_begin(8),$sequence_end(8)" \
+  "9\t    fun3\tinst $sequence_begin(9),$sequence_end(9)" \
+  "10\t      fun1\tinst $sequence_begin(10),$sequence_end(10)" \
+  "11\t    fun3\tinst $sequence_begin(11),$sequence_end(11)" \
+  "12\t      fun2\tinst $sequence_begin(12),$sequence_end(12)" \
+  "13\t        fun1\tinst $sequence_begin(13),$sequence_end(13)" \
+  "14\t      fun2\tinst $sequence_begin(14),$sequence_end(14)" \
+  "15\t    fun3\tinst $sequence_begin(15),$sequence_end(15)" \
+  "16\t  fun4\tinst $sequence_begin(16),$sequence_end(16)" \
   ]
 
 # let's see if we can go back in history
-gdb_test "record goto 19" ".*fun4 \\(\\) at record_goto.c:43.*"
+gdb_test "record goto $function_positions(0)" ".*fun4 \\(\\) at record_goto.c:43.*"
 
 # the function call history should start at the new location
 gdb_test "record function-call-history /ci" [multi_line \
-  "8\t  fun4\tinst 19,19" \
-  "9\t    fun3\tinst 20,22" \
-  "10\t      fun1\tinst 23,26" \
-  ] "function-call-history from 19 forwards"
+  "8\t  fun4\tinst $sequence_begin(8),$sequence_end(8)" \
+  "9\t    fun3\tinst $sequence_begin(9),$sequence_end(9)" \
+  "10\t      fun1\tinst $sequence_begin(10),$sequence_end(10)" \
+  ] "function-call-history from $function_positions(0) forwards"
 
 # the instruction history should start at the new location
 gdb_test "record instruction-history" [multi_line \
-  "19.*" \
-  "20.*" \
-  "21.*" \
-  ] "instruction-history from 19 forwards"
+  "$function_positions(0).*" \
+  "[expr {$function_positions(0) + 1}].*" \
+  "[expr {$function_positions(0) + 2}].*" \
+  ] "instruction-history from $function_positions(0) forwards"
 
 # let's go to another place in the history
-gdb_test "record goto 27" ".*fun3 \\(\\) at record_goto.c:35.*"
+gdb_test "record goto $function_positions(1)" ".*fun3 \\(\\) at record_goto.c:35.*"
 
 # check the back trace at that location
 gdb_test "backtrace" [multi_line \
@@ -117,26 +245,26 @@ gdb_test "up" ".*main.*at record_goto.c:49.*" "up to main"
 
 # the function call history should start at the new location
 gdb_test "record function-call-history /ci -" [multi_line \
-  "9\t    fun3\tinst 20,22" \
-  "10\t      fun1\tinst 23,26" \
-  "11\t    fun3\tinst 27,27" \
-  ] "function-call-history from 27 backwards"
+  "9\t    fun3\tinst $sequence_begin(9),$sequence_end(9)" \
+  "10\t      fun1\tinst $sequence_begin(10),$sequence_end(10)" \
+  "11\t    fun3\tinst $sequence_begin(11),$sequence_end(11)" \
+  ] "function-call-history from $function_positions(1) backwards"
 
 # the instruction history should start at the new location
 gdb_test "record instruction-history -" [multi_line \
-  "25.*" \
-  "26.*" \
-  "27.*" \
-  ] "instruction-history from 27 backwards"
+  "[expr {$function_positions(1) - 2}].*" \
+  "[expr {$function_positions(1) - 1}].*" \
+  "$function_positions(1).*" \
+  ] "instruction-history from $function_positions(1) backwards"
 
 # test that we can go to the begin of the trace
 gdb_test "record goto begin" ".*main \\(\\) at record_goto.c:49.*"
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci -" [multi_line \
-  "1\tmain\tinst 1,1" \
-  "2\t  fun4\tinst 2,4" \
-  "3\t    fun1\tinst 5,8" \
+  "1\tmain\tinst $sequence_begin(1),$sequence_end(1)" \
+  "2\t  fun4\tinst $sequence_begin(2),$sequence_end(2)" \
+  "3\t    fun1\tinst $sequence_begin(3),$sequence_end(3)" \
   ] "function-call-history from begin backwards"
 
 # check that we're filling up the context correctly
@@ -147,52 +275,53 @@ gdb_test "record instruction-history -" [multi_line \
   ] "instruction-history from begin backwards"
 
 # we should get the exact same history from the first instruction
-gdb_test "record goto 2" ".*fun4 \\(\\) at record_goto.c:40.*"
+gdb_test "record goto $function_positions(2)" ".*fun4 \\(\\) at record_goto.c:40.*"
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci -" [multi_line \
-  "1\tmain\tinst 1,1" \
-  "2\t  fun4\tinst 2,4" \
-  "3\t    fun1\tinst 5,8\r" \
-  ] "function-call-history from 2 backwards"
+  "1\tmain\tinst $sequence_begin(1),$sequence_end(1)" \
+  "2\t  fun4\tinst $sequence_begin(2),$sequence_end(2)" \
+  "3\t    fun1\tinst $sequence_begin(3),$sequence_end(3)\r" \
+  ] "function-call-history from $function_positions(2) backwards"
 
 # check that we're filling up the context correctly
 gdb_test "record instruction-history -" [multi_line \
   "1.*" \
   "2.*" \
   "3.*" \
-  ] "instruction-history from 2 backwards"
+  ] "instruction-history from $function_positions(2) backwards"
 
 # check that we can go to the end of the trace
 gdb_test "record goto end" ".*main \\(\\) at record_goto.c:50.*"
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci" [multi_line \
-  "14\t      fun2\tinst 35,36" \
-  "15\t    fun3\tinst 37,38" \
-  "16\t  fun4\tinst 39,40" \
+  "14\t      fun2\tinst $sequence_begin(14),$sequence_end(14)" \
+  "15\t    fun3\tinst $sequence_begin(15),$sequence_end(15)" \
+  "16\t  fun4\tinst $sequence_begin(16),$sequence_end(16)" \
   ] "function-call-history from end forwards"
 
 # check that we're filling up the context correctly
+#adapt it for arm, last instruction is  at pos 48
 gdb_test "record instruction-history" [multi_line \
-  "38.*" \
-  "39.*" \
-  "40.*\r" \
+  "[expr {$function_positions(end) - 2}].*" \
+  "[expr {$function_positions(end) - 1}].*" \
+  "$function_positions(end).*\r" \
   ] "instruction-history from end forwards"
 
 # we should get the exact same history from the second to last instruction
-gdb_test "record goto 39" ".*fun4 \\(\\) at record_goto.c:44.*"
+gdb_test "record goto $function_positions(3)" ".*fun4 \\(\\) at record_goto.c:44.*"
 
 # check that we're filling up the context correctly
 gdb_test "record function-call-history /ci" [multi_line \
-  "14\t      fun2\tinst 35,36" \
-  "15\t    fun3\tinst 37,38" \
-  "16\t  fun4\tinst 39,40\r" \
-  ] "function-call-history from 39 forwards"
+  "14\t      fun2\tinst $sequence_begin(14),$sequence_end(14)" \
+  "15\t    fun3\tinst $sequence_begin(15),$sequence_end(15)" \
+  "16\t  fun4\tinst $sequence_begin(16),$sequence_end(16)\r" \
+  ] "function-call-history from $function_positions(3) forwards"
 
 # check that we're filling up the context correctly
 gdb_test "record instruction-history" [multi_line \
-  "38.*" \
-  "39.*" \
-  "40.*\r" \
-  ] "instruction-history from 39 forwards"
+  "[expr {$function_positions(3) - 1}].*" \
+  "$function_positions(3).*" \
+  "[expr {$function_positions(3) + 1}].*\r" \
+  ] "instruction-history from $function_positions(3) forwards"
diff --git a/gdb/testsuite/gdb.btrace/stepi.exp b/gdb/testsuite/gdb.btrace/stepi.exp
index 480c08c4875..f07961228e0 100644
--- a/gdb/testsuite/gdb.btrace/stepi.exp
+++ b/gdb/testsuite/gdb.btrace/stepi.exp
@@ -33,21 +33,79 @@ if [info exists COMPILE] {
     # make check RUNTESTFLAGS="gdb.btrace/stepi.exp COMPILE=1"
     standard_testfile record_goto.c
     lappend opts debug
-} elseif {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+} else {
+  set test_prefix ""
+  if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
     if {[is_amd64_regs_target]} {
-		standard_testfile x86_64-record_goto.S
+      set test_prefix "x86_64"
     } else {
-		standard_testfile i686-record_goto.S
+      set test_prefix "i686"
     }
-} else {
+  } elseif {[istarget "arm*-*-*"]} {
+      set test_prefix "arm"
+  } elseif {[istarget "aarch64*-*-*"]} {
+    set test_prefix "aarch64"
+  } else {
     unsupported "target architecture not supported"
     return -1
+  }
+  standard_testfile $test_prefix-record_goto.S
 }
 
 if [prepare_for_testing "failed to prepare" $testfile $srcfile {}] {
     return -1
 }
 
+if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+    set instructions_count 40
+    set instruction_position(0) 39
+    set instruction_position(1) 40
+    set instruction_position(2) 1
+    set instruction_position(3) 1
+    set instruction_position(4) 22
+    set instruction_position(5) 23
+    set instruction_position(6) 22
+    set instruction_position(7) 27
+    set instruction_position(8) 22
+    set instruction_position(9) 1
+    set instruction_position(10) 1
+    set instruction_position(11) 1
+    set instruction_position(12) 2
+    set instruction_position(13) 1
+} elseif {[istarget "arm*-*-*"]} {
+    set instructions_count 48
+    set instruction_position(0) 47
+    set instruction_position(1) 48
+    set instruction_position(2) 1
+    set instruction_position(3) 1
+    set instruction_position(4) 26
+    set instruction_position(5) 27
+    set instruction_position(6) 26
+    set instruction_position(7) 33
+    set instruction_position(8) 26
+    set instruction_position(9) 1
+    set instruction_position(10) 1
+    set instruction_position(11) 1
+    set instruction_position(12) 2
+    set instruction_position(13) 1
+} elseif {[istarget "aarch64*-*-*"]} {
+    set instructions_count 36
+    set instruction_position(0) 35
+    set instruction_position(1) 36
+    set instruction_position(2) 1
+    set instruction_position(3) 1
+    set instruction_position(4) 19
+    set instruction_position(5) 20
+    set instruction_position(6) 19
+    set instruction_position(7) 22
+    set instruction_position(8) 19
+    set instruction_position(9) 1
+    set instruction_position(10) 1
+    set instruction_position(11) 1
+    set instruction_position(12) 2
+    set instruction_position(13) 1
+}
+
 if ![runto_main] {
     untested "failed to run to main"
     return -1
@@ -56,10 +114,11 @@ if ![runto_main] {
 global gdb_prompt
 
 proc check_replay_at { insn } {
+  global instructions_count
   gdb_test "info record" [multi_line \
     "Active record target: record-btrace" \
     ".*" \
-    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
+    "Recorded $instructions_count instructions in 16 functions \\\(0 gaps\\\) for .*" \
     "Replay in progress\.  At instruction $insn\." \
     ] "check replay at $insn"
 }
@@ -74,15 +133,14 @@ with_test_prefix "record" {
 with_test_prefix "fetch" {
     gdb_test "reverse-stepi" ".*fun4\.5.*" "reverse-stepi.1"
     gdb_test "reverse-stepi" ".*fun4\.5.*" "reverse-stepi.2"
-
     # let's check where we are in the trace
-    check_replay_at 39
+    check_replay_at $instruction_position(0) 
 }
 
 # let's step forward and check again
 with_test_prefix "stepi" {
     gdb_test "stepi" ".*fun4\.5.*"
-    check_replay_at 40
+    check_replay_at $instruction_position(1) 
 }
 
 # with the next step, we stop replaying
@@ -91,14 +149,17 @@ with_test_prefix "end" {
     gdb_test "info record" [multi_line \
       "Active record target: record-btrace" \
       ".*" \
-      "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
+      "Recorded $instructions_count instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
   ] 
 }
 
+#recover from a missing lr register in arm
+gdb_test "record goto end"
+
 # let's try nexti
 with_test_prefix "reverse-nexti.1" {
     gdb_test "reverse-nexti" ".*main\.2.*"
-    check_replay_at 1
+    check_replay_at $instruction_position(2)
 }
 
 # we can't reverse-nexti any further
@@ -106,7 +167,7 @@ with_test_prefix "reverse-nexti.2" {
     gdb_test "reverse-nexti" \
 	"No more reverse-execution history\.\r\n.*main\.2.*" \
 	"reverse-nexti.2"
-    check_replay_at 1
+    check_replay_at $instruction_position(3)
 }
 
 # but we can step back again
@@ -115,32 +176,32 @@ with_test_prefix "nexti" {
     gdb_test "info record" [multi_line \
       "Active record target: record-btrace" \
       ".*" \
-      "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
+      "Recorded $instructions_count instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
 			       ]
 }
 
 # let's step from a goto position somewhere in the middle
 with_test_prefix "goto" {
-    gdb_test "record goto 22" ".*fun3\.2.*"
-    with_test_prefix "goto 22" { check_replay_at 22 }
+    gdb_test "record goto $instruction_position(4) " ".*fun3\.2.*"
+    with_test_prefix "goto $instruction_position(4) " { check_replay_at $instruction_position(4)  }
 
     gdb_test "stepi" ".*fun1\.1.*" "stepi.3"
-    with_test_prefix "stepi to 23" { check_replay_at 23 }
+    with_test_prefix "stepi to $instruction_position(5)" { check_replay_at $instruction_position(5) }
 
     gdb_test "reverse-stepi" ".*fun3\.2.*" "reverse-stepi.3"
-    with_test_prefix "reverse-stepi to 22" { check_replay_at 22 }
+    with_test_prefix "reverse-stepi to $instruction_position(6)" { check_replay_at $instruction_position(6) }
 
     gdb_test "nexti" ".*fun3\.3.*"
-    with_test_prefix "nexti to 27" { check_replay_at 27 }
+    with_test_prefix "nexti to $instruction_position(7) " { check_replay_at $instruction_position(7)  }
 
     gdb_test "reverse-nexti" ".*fun3\.2.*" "reverse-nexti.3"
-    with_test_prefix "reverse-nexti to 22" { check_replay_at 22 }
+    with_test_prefix "reverse-nexti to $instruction_position(8)" { check_replay_at $instruction_position(8) }
 }
 
 # let's try to step off the left end
 with_test_prefix "goto begin" {
     gdb_test "record goto begin" ".*main\.2.*"
-    check_replay_at 1
+    check_replay_at $instruction_position(9)
 
     with_test_prefix "reverse-stepi" {
 	gdb_test "reverse-stepi" \
@@ -149,7 +210,7 @@ with_test_prefix "goto begin" {
 	gdb_test "reverse-stepi" \
 	    "No more reverse-execution history\.\r\n.*main\.2.*" \
 	    "reverse-stepi.2"
-	check_replay_at 1
+	check_replay_at $instruction_position(10)
     }
 
     with_test_prefix "reverse-nexti" {
@@ -159,13 +220,13 @@ with_test_prefix "goto begin" {
 	gdb_test "reverse-nexti" \
 	    "No more reverse-execution history\.\r\n.*main\.2.*" \
 	    "reverse-nexti.2"
-	check_replay_at 1
+	check_replay_at $instruction_position(11)
     }
 
     # we can step forward, though
     with_test_prefix "stepi" {
 	gdb_test "stepi" ".*fun4\.1.*"
-	check_replay_at 2
+	check_replay_at $instruction_position(12)
     }
 }
 
@@ -178,5 +239,5 @@ with_test_prefix "reverse-stepi" {
     gdb_test "reverse-stepi" \
 	"No more reverse-execution history\.\r\n.*main\.2.*" \
 	"reverse-stepi.3"
-    check_replay_at 1
+    check_replay_at $instruction_position(13)
 }
diff --git a/gdb/testsuite/gdb.btrace/tailcall-only.exp b/gdb/testsuite/gdb.btrace/tailcall-only.exp
index 510f90c9d5e..036da4a2020 100644
--- a/gdb/testsuite/gdb.btrace/tailcall-only.exp
+++ b/gdb/testsuite/gdb.btrace/tailcall-only.exp
@@ -37,15 +37,23 @@ if [info exists COMPILE] {
     # make check RUNTESTFLAGS="gdb.btrace/tailcall-only.exp COMPILE=1"
     standard_testfile tailcall-only.c
     lappend opts debug optimize=-O2
-} elseif {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+} else {
+  set test_prefix ""
+  if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
     if {[is_amd64_regs_target]} {
-		standard_testfile x86_64-tailcall-only.S
+      set test_prefix "x86_64"
     } else {
-		standard_testfile i686-tailcall-only.S
+      set test_prefix "i686"
     }
-} else {
+  } elseif {[istarget "arm*-*-*"]} {
+      set test_prefix "arm"
+  } elseif {[istarget "aarch64*-*-*"]} {
+    set test_prefix "aarch64"
+  } else {
     unsupported "target architecture not supported"
     return -1
+  }
+  standard_testfile $test_prefix-tailcall-only.S
 }
 
 if [prepare_for_testing "failed to prepare" $testfile $srcfile $opts] {
@@ -62,6 +70,10 @@ gdb_test_no_output "set record function-call-history-size 0"
 
 # trace foo
 gdb_test "step" ".*" "prepare for recording"
+# make sure we get out of function epilogue
+if { [istarget "arm*-*-*"] } {
+  gdb_test "stepi"
+}
 gdb_test_no_output "record btrace"
 gdb_test "stepi 4" ".*" "record branch trace"
 
diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp
index 07a3ec103f4..70fda8a5670 100644
--- a/gdb/testsuite/gdb.btrace/tailcall.exp
+++ b/gdb/testsuite/gdb.btrace/tailcall.exp
@@ -34,20 +34,42 @@ if [info exists COMPILE] {
     # make check RUNTESTFLAGS="gdb.btrace/tailcall.exp COMPILE=1"
     standard_testfile tailcall.c
     lappend opts debug optimize=-O2
-} elseif {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+} else {
+  set test_prefix ""
+  if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
     if {[is_amd64_regs_target]} {
-		standard_testfile x86_64-tailcall.S
+      set test_prefix "x86_64"
     } else {
-		standard_testfile i686-tailcall.S
+      set test_prefix "i686"
     }
-} else {
+  } elseif {[istarget "arm*-*-*"]} {
+      set test_prefix "arm"
+  } elseif {[istarget "aarch64*-*-*"]} {
+    set test_prefix "aarch64"
+  } else {
     unsupported "target architecture not supported"
     return -1
+  }
+  standard_testfile $test_prefix-tailcall.S
 }
 
 if [prepare_for_testing "failed to prepare" $testfile $srcfile $opts] {
     return -1
 }
+
+if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+    set bar_return_line 24
+    set bar_return_position 4
+    set main_return_line 38
+} elseif {[istarget "arm*-*-*"]} {
+    set bar_return_line 24
+    set bar_return_position 5
+    set main_return_line 41
+    } elseif {[istarget "aarch64*-*-*"]} {
+    set bar_return_line 23
+    set bar_return_position 6
+    set main_return_line 40
+}
 if ![runto_main] {
     untested "failed to run to main"
     return -1
@@ -58,7 +80,17 @@ gdb_test_no_output "set record function-call-history-size 0"
 
 # trace the call to foo
 gdb_test_no_output "record btrace"
-gdb_test "next 2"
+
+# make sure we get out of function epilogue
+if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
+    gdb_test "next 2"
+} elseif {[istarget "arm*-*-*"]} {
+  gdb_test "next 2"
+  gdb_test "stepi"
+} elseif {[istarget "aarch64*-*-*"]} {
+  gdb_test "next"
+  gdb_test "stepi 2"
+}
 
 # show the flat branch trace
 gdb_test "record function-call-history 1" [multi_line \
@@ -77,11 +109,11 @@ gdb_test "record function-call-history /c 1" [multi_line \
   ] "indented"
 
 # go into bar
-gdb_test "record goto 4" ".*bar \\(\\) at .*tailcall.c:24\r\n.*"
+gdb_test "record goto $bar_return_position" ".*bar \\(\\) at .*tailcall.c:$bar_return_line\r\n.*"
 
 # check the backtrace
 gdb_test "backtrace" [multi_line \
-  "#0.*bar \\(\\) at tailcall.c:24" \
+  "#0.*bar \\(\\) at tailcall.c:$bar_return_line" \
   "#1.*foo \\(\\) at tailcall.c:29" \
   "#2.*main \\(\\) at tailcall.c:37" \
   "Backtrace stopped: not enough registers or memory available to unwind further" \
@@ -93,23 +125,23 @@ gdb_test "up" "#2\[^\r\n\]*main \\(\\) at tailcall.c:37\r\n.*" "up to main"
 gdb_test "down" "#1\[^\r\n\]*foo \\(\\) at tailcall.c:29\r\n.*" "down to foo"
 
 # test stepping into and out of tailcalls.
-gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:38\r\n.*" \
+gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:$main_return_line\r\n.*" \
     "finish.1"
-gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at tailcall.c:24\r\n.*" \
+gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at tailcall.c:$bar_return_line\r\n.*" \
     "reverse-step.1"
 gdb_test "reverse-finish" "\[^\r\n\]*foo \\(\\) at tailcall.c:29\r\n.*" \
     "reverse-finish.1"
 gdb_test "reverse-step" "\[^\r\n\]*main \\(\\) at tailcall.c:37\r\n.*" \
     "reverse-step.2"
-gdb_test "next" "\[^\r\n\]*38.*" \
+gdb_test "next" "\[^\r\n\]*$main_return_line.*" \
     "next.1"
 gdb_test "reverse-next" "\[^\r\n\]*main \\(\\) at tailcall.c:37\r\n.*" \
     "reverse-next.1"
 gdb_test "step" "\[^\r\n\]*foo \\(\\) at tailcall.c:29\r\n.*" \
     "step.1"
-gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:38\r\n.*" \
+gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:$main_return_line\r\n.*" \
     "finish.2"
-gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at tailcall.c:24\r\n.*" \
+gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at tailcall.c:$bar_return_line\r\n.*" \
     "reverse-step.3"
-gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:38\r\n.*" \
+gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:$main_return_line\r\n.*" \
     "finish.3"
diff --git a/gdb/testsuite/gdb.btrace/instruction_history.S b/gdb/testsuite/gdb.btrace/x86-instruction_history.S
similarity index 100%
rename from gdb/testsuite/gdb.btrace/instruction_history.S
rename to gdb/testsuite/gdb.btrace/x86-instruction_history.S
diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
index 36a5fd4feb7..02cf8a3cec6 100644
--- a/gdb/testsuite/lib/gdb.exp
+++ b/gdb/testsuite/lib/gdb.exp
@@ -3248,7 +3248,7 @@ gdb_caching_proc skip_btrace_tests {
     global srcdir subdir gdb_prompt inferior_exited_re
 
     set me "skip_btrace_tests"
-    if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } {
+    if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] && ![istarget "arm*-*-*"] && ![istarget "aarch64*-*-*"]} {
         verbose "$me:  target does not support btrace, returning 1" 2
         return 1
     }
-- 
2.25.1


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 2/7] add btrace coresight related commands
  2021-05-31 21:33 ` [PATCH v6 2/7] add btrace coresight related commands Zied Guermazi
@ 2021-06-01 12:07   ` Eli Zaretskii
  2021-06-01 15:47     ` Zied Guermazi
  2021-06-30 12:26   ` Luis Machado
  1 sibling, 1 reply; 35+ messages in thread
From: Eli Zaretskii @ 2021-06-01 12:07 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches, markus.t.metzger

> From: Zied Guermazi <zied.guermazi@trande.de>
> Date: Mon, 31 May 2021 23:33:02 +0200
> Cc: Zied Guermazi <zied.guermazi@trande.de>
> 
> This patch extends the commands needed for using branch tracing
> with ARM CoreSight traces.
> Those commands are:
> set record btrace etm sink
> set record btrace etm buffer-size
> record btrace etm
> 
> gdb/ChangeLog
> 
> 	* NEWS: list new commands for extending btrace
> 	to support using ARM CoreSight Traces.
> 	* record-btrace.c (record_btrace_print_etm_conf): New.
> 	(record_btrace_print_conf): handle BTRACE_FORMAT_ETM.
> 	(cmd_record_btrace_etm_start): New.
> 	(cmd_record_btrace_start): handle starting ETM tracing.
> 	(cmd_show_record_btrace_cpu): extend for ARM cpus.
> 	(show_record_etm_buffer_size_value): New.
> 	(_initialize_record_btrace): add commands for ETM traces.
> 	(record_start): add starting ETM traces.
> 
> gdb/doc/ChangeLog
> 
> 	* gdb.texinfo (Process Record and Replay): Document extending
> 	GDB btrace commands to support using ARM CoreSight traces.
> 
> gdbsupport/ChangeLog
> 
> 	* btrace-common.h (btrace_format): add BTRACE_FORMAT_ETM
> 	to the enum.
> 	(btrace_config_etm): new struct.
> 	(btrace_config): add btrace_config_etm etm.
> 	* btrace-common.cc (btrace_format_string): add BTRACE_FORMAT_ETM.
> 	(btrace_format_short_string): add BTRACE_FORMAT_ETM.

Are there any changes in the documentation parts since the last time?
If not, I think I already approved the doc parts.

Thanks.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 6/7] add support for coresight btrace via remote protocol
  2021-05-31 21:33 ` [PATCH v6 6/7] add support for coresight btrace via remote protocol Zied Guermazi
@ 2021-06-01 12:08   ` Eli Zaretskii
  2021-06-23 10:59   ` Metzger, Markus T
  1 sibling, 0 replies; 35+ messages in thread
From: Eli Zaretskii @ 2021-06-01 12:08 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches, markus.t.metzger

> From: Zied Guermazi <zied.guermazi@trande.de>
> Date: Mon, 31 May 2021 23:33:06 +0200
> Cc: Zied Guermazi <zied.guermazi@trande.de>
> 
> This patch extends the remote protocol to use ARM CoreSight traces for btrace.
> This patch adds the capabilities enumeration as well as the required protocol
> extentions for configuring traces collection, starting and stopping tracing,
> and transferring the traces as well as the parameters needed for decoding them.
> 
> gdb/ChangeLog
> 
> 	* NEWS: list new remote packets for extending btrace
> 	to support using ARM CoreSight Traces.
> 	* btrace.c (check_xml_btrace_version): add version 1.1
> 	(parse_xml_btrace_etm_config_source_config_cpu_etmv4_config): New.
> 	(parse_xml_btrace_etm_config_source_config_cpu_etmv3_config): New.
> 	(parse_xml_btrace_etm_config_source_config_cpu_etm_config): New.
> 	(parse_xml_btrace_etm_config_source_config): New.
> 	(parse_xml_btrace_etm_config_source_config_end): New
> 	(parse_xml_btrace_etm_config_sink_config): New.
> 	(parse_xml_btrace_etm_raw): New.
> 	(parse_xml_btrace_etm): New.
> 	(parse_xml_btrace_conf_etm): New.
> 	* btrace.dtd: add etm data and decoding parameters.
> 	* btrace-conf.dtd: add etm configuration.
> 	* remote.c (remote_target::btrace_sync_conf): add etm configuration.
> 	(remote_target::remote_btrace_maybe_reopen): warn if ETM was disabled.
> 	(remote_target::enable_btrace): add coresight etm.
> 	(_initialize_remote): add etm related packets.
> 
> gdb/doc/ChangeLog
> 
> 	* gdb.texinfo (General Query Packets) Document extending GDB remote
> 	protocol packets to support using ARM CoreSight traces for btrace.
> 
> gdbserver/ChangeLog
> 
> 	* configure.srv: add btrace for aarch64*-*-linux* and arm*-*-linux*.
> 	* linux-low.c (linux_low_encode_etm_config): New.
> 	(linux_process_target::read_btrace): encode CoreSight traces
> 	and related decoding parameters.
> 	(linux_process_target::read_btrace_conf): encode CoreSight
> 	configuration.
> 	* server.cc (handle_btrace_enable_etm): New.
> 	(handle_btrace_general_set): add etm handling.
> 	(handle_btrace_conf_general_set): add etm handling.
> 	(supported_btrace_packets): add etm related packets.

Likewise here: if there were no changes since v5 in the documentation
parts, I believe I already approved that.

Thanks.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 2/7] add btrace coresight related commands
  2021-06-01 12:07   ` Eli Zaretskii
@ 2021-06-01 15:47     ` Zied Guermazi
  0 siblings, 0 replies; 35+ messages in thread
From: Zied Guermazi @ 2021-06-01 15:47 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, markus.t.metzger

Hi Eli,

there is no change in documentation part. so we can consider this part 
as approved.

Kind Regards

Zied Guermazi

On 01.06.21 14:07, Eli Zaretskii wrote:
>> From: Zied Guermazi <zied.guermazi@trande.de>
>> Date: Mon, 31 May 2021 23:33:02 +0200
>> Cc: Zied Guermazi <zied.guermazi@trande.de>
>>
>> This patch extends the commands needed for using branch tracing
>> with ARM CoreSight traces.
>> Those commands are:
>> set record btrace etm sink
>> set record btrace etm buffer-size
>> record btrace etm
>>
>> gdb/ChangeLog
>>
>> 	* NEWS: list new commands for extending btrace
>> 	to support using ARM CoreSight Traces.
>> 	* record-btrace.c (record_btrace_print_etm_conf): New.
>> 	(record_btrace_print_conf): handle BTRACE_FORMAT_ETM.
>> 	(cmd_record_btrace_etm_start): New.
>> 	(cmd_record_btrace_start): handle starting ETM tracing.
>> 	(cmd_show_record_btrace_cpu): extend for ARM cpus.
>> 	(show_record_etm_buffer_size_value): New.
>> 	(_initialize_record_btrace): add commands for ETM traces.
>> 	(record_start): add starting ETM traces.
>>
>> gdb/doc/ChangeLog
>>
>> 	* gdb.texinfo (Process Record and Replay): Document extending
>> 	GDB btrace commands to support using ARM CoreSight traces.
>>
>> gdbsupport/ChangeLog
>>
>> 	* btrace-common.h (btrace_format): add BTRACE_FORMAT_ETM
>> 	to the enum.
>> 	(btrace_config_etm): new struct.
>> 	(btrace_config): add btrace_config_etm etm.
>> 	* btrace-common.cc (btrace_format_string): add BTRACE_FORMAT_ETM.
>> 	(btrace_format_short_string): add BTRACE_FORMAT_ETM.
> Are there any changes in the documentation parts since the last time?
> If not, I think I already approved the doc parts.
>
> Thanks.


^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant
  2021-05-31 21:33 ` [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant Zied Guermazi
@ 2021-06-22 14:59   ` Metzger, Markus T
  2022-04-07 16:33     ` Zied Guermazi
  2021-06-30 12:54   ` Luis Machado
  1 sibling, 1 reply; 35+ messages in thread
From: Metzger, Markus T @ 2021-06-22 14:59 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches

Hello Zied,

>This patch extend branch tracing by adding the functions needed
>to collect parameters for decoding ETM traces and decoding them.

Looks good, overall.  Comments are mostly about simplifications.
Shall we discuss them in this email thread before going to v7?


>diff --git a/gdb/btrace.c b/gdb/btrace.c
>index 5e689c11d4b..2676389b63e 100644
>--- a/gdb/btrace.c
>+++ b/gdb/btrace.c
>@@ -671,6 +672,38 @@ ftrace_update_insns (struct btrace_function *bfun,
>const btrace_insn &insn)
>     ftrace_debug (bfun, "update insn");
> }
>
>+#if defined (HAVE_LIBOPENCSD_C_API)
>+/* Remove last instruction from BFUN's list.
>+   This function is not generic and does not undo functions chaining.
>+   When adding an instruction after using it, the caller must ensure
>+   that the instruction produces the same chaining.
>+   An example of good case, is when the same removed instruction
>+   is added later.  */

This function is called in one place and the way I understood it, we remove
an undefined instruction that was used as a breakpoint.  I assume the behavior
is that this undefined instruction is counted as executed in the trace and we
want to fix that up.

We don't add that instruction back, though, do we?

And if we wanted to support that remove-then-re-insert use-case,
wouldn't we want to return the removed instruction?

I'd rather we document exactly that one use-case in which we need
this functionality.  This allows documenting exactly what we're doing
and why this is necessary.

>+
>+static void
>+ftrace_remove_last_insn (struct btrace_thread_info *btinfo)
>+{
>+  /* If we didn't have a function, we return.  */
>+  if (btinfo->functions.empty ())
>+    return;

Should this be an error?

>+
>+  struct btrace_function *bfun = &btinfo->functions.back ();
>+  /* If we had a gap before, we return.  */
>+  if (bfun->errcode != 0)
>+    return;

In which case can we have a gap?

>+
>+  if (!bfun->insn.empty ())
>+    bfun->insn.pop_back ();
>+  else
>+    {
>+      /* A valid function must have at least one instruction.  */
>+      internal_error (__FILE__, __LINE__,
>+		       _("Attempt to remove last instruction"
>+			 "from an empty function"));

We just removed an instruction in the then statement, which
could well result in BFUN->INSN to become empty().

The statement about valid functions above may be a bit too
generic.  Also, ftrace_new_* () may create empty function
segments.

Isn't it rather that in our use-case, the caller knows that the
function segment cannot be empty?


>+    case ocsd_isa_custom:
>+      return BTRACE_INSN_FLAG_ISA_CUSTOM;
>+
>+    case ocsd_isa_unknown:
>+      return BTRACE_INSN_FLAG_ISA_UNKNOWN;
>+
>+    default:
>+      internal_error (__FILE__, __LINE__,
>+		       _("Undefined elem->isa value returned by OpenCsd."));

This internal error kills GDB.  Should this be a normal error that just
stops trace processing for this unknown ISA?


>+  insn.iclass = BTRACE_INSN_OTHER;
>+  insn.pc = elem->st_addr;
>+  for (int i = 0; i< elem->num_instr_range; i++)
>+    {
>+      try
>+	{
>+	  insn.size = gdb_insn_length (gdbarch, insn.pc);
>+	}
>+      catch (const gdb_exception_error &err)
>+	{
>+	  error (_("Failed to get the size of the instruction."));

Isn't the original exception good enough?

>+	}
>+
>+      struct btrace_function *bfun = ftrace_update_function (btinfo, insn.pc);
>+      if (etm_decoder->arch_version == ARCH_V7)
>+	insn.flags = cs_etm_get_isa_flag (elem);

ELEM isn't changing so we could set this once outside of the loop.

>+
>+      if (i == elem->num_instr_range -1)
>+	insn.iclass = cs_etm_get_instruction_class (elem);
>+
>+      ftrace_update_insns (bfun, insn);
>+      insn.pc = insn.pc + insn.size;
>+    }
>+}
>+
>+/* Update btrace in the case of an exception.  */
>+
>+static void
>+cs_etm_update_btrace_with_exception (const struct cs_etm_decoder
>*etm_decoder,
>+				      const ocsd_generic_trace_elem *elem)
>+{
>+  gdb_assert (elem->elem_type == OCSD_GEN_TRC_ELEM_EXCEPTION);
>+
>+  struct thread_info *tp = etm_decoder->t_info;
>+  struct btrace_thread_info *btinfo = &tp->btrace;
>+
>+  /* Handle the implementation of breakpoints in gdb for arm (v7) architecture
>+     using undefined instructions.  */
>+  if (etm_decoder->arch_version == ARCH_V7)
>+    {
>+      if (elem->exception_number
>+	   == CS_ETMV3_4_CORTEX_A_R_EXCEPTION_UNDEFINED_INSTRUCTION)
>+	{
>+	  DEBUG ("handle breakpoints implementation in gdb for ARMv7");
>+	  ftrace_remove_last_insn (btinfo);

Here we just discard the 'breakpoint' instruction, correct?  We don't really plan
to re-insert it again.

Can we ensure that we actually inserted that instruction into the current BFUN?
We wouldn't need the helper function and all those checks would become asserts.

I like little helper functions but if we just removed the insn here it would be more
obvious why we're doing it.

>+	}
>+    }
>+}
>+
>+/* Update btrace in the case of a trace on.  */
>+
>+static void
>+cs_etm_update_btrace_with_trace_on (const struct cs_etm_decoder
>*etm_decoder,
>+				     const ocsd_generic_trace_elem *elem)
>+{
>+  gdb_assert (elem->elem_type == OCSD_GEN_TRC_ELEM_TRACE_ON);
>+
>+  if (elem->trace_on_reason != TRACE_ON_NORMAL)
>+    {
>+      struct thread_info *tp = etm_decoder->t_info;
>+      struct btrace_thread_info *btinfo = &tp->btrace;
>+      ftrace_new_gap (btinfo, elem->trace_on_reason, etm_decoder->gaps);
>+    }

Even for normal trace off/on pairs, we'd want a gap if PC is moving.


>+}
>+
>+/* Callback function when a ocsd_generic_trace_elem is emitted.  */
>+
>+static ocsd_datapath_resp_t
>+cs_etm_trace_element_callback (const void *context,
>+				const ocsd_trc_index_t index,
>+				const uint8_t trace_chan_id,
>+				const ocsd_generic_trace_elem *elem)
>+{
>+  if (record_debug != 0)
>+    {
>+      char str_buffer[128];
>+      if (ocsd_gen_elem_str (elem, str_buffer, 128) == OCSD_OK)

sizeof (str_buffer)


>+/* Callback to print error log.  */
>+
>+static void cs_etm_print_error_log (const void *p_context, const char
>*psz_msg_str, const int str_len)
>+{
>+    char string_buffer[128];
>+    memcpy (string_buffer, psz_msg_str, std::min(str_len, 127));
>+    if (str_len >127)
>+      string_buffer[127] = 0;
>+    else
>+      string_buffer[str_len] = 0;

We can simply adjust str_len before the memcpy.  There's no reason to declare
it const; it is passed by-value.


>+  uint8_t csid;
>+  errcode = ocsd_dt_create_decoder (decoder->dcd_tree, decoder_name,
>+			      OCSD_CREATE_FLG_FULL_DECODER,
>+			      trace_config, &csid);
>+  if (errcode != OCSD_OK)
>+    {
>+      warning (_("ocsd_dt_create_decoder failed with error: %d"), errcode);
>+      return errcode;
>+    }
>+
>+  errcode = ocsd_dt_set_gen_elem_outfn (decoder->dcd_tree,
>+					 cs_etm_trace_element_callback,
>+					 decoder);
>+  if (errcode != OCSD_OK)
>+    {
>+      warning (_("ocsd_dt_set_gen_elem_outfn failed failed with error: %d"),
>+		   errcode);
>+      return errcode;
>+    }
>+
>+  errcode = ocsd_def_errlog_init (OCSD_ERR_SEV_ERROR, 1);
>+  if (errcode != OCSD_OK)
>+    {
>+      warning (_("ocsd_def_errlog_init failed failed with error: %d"),
>+		   errcode);
>+      return errcode;
>+    }
>+
>+  /* Initialize error printer.  */
>+  errcode = ocsd_def_errlog_config_output (C_API_MSGLOGOUT_FLG_NONE,
>nullptr);
>+  if (errcode != OCSD_OK)
>+    {
>+      warning (_("ocsd_def_errlog_init failed failed with error: %d"),
>+		   errcode);
>+      return errcode;
>+    }
>+
>+  errcode = ocsd_def_errlog_set_strprint_cb (decoder->dcd_tree, nullptr,
>+					      cs_etm_print_error_log);
>+  if (errcode != OCSD_OK)
>+    {
>+      warning (_("ocsd_def_errlog_set_strprint_cb failed failed with error: %d"),
>+		   errcode);
>+      return errcode;
>+    }
>+
>+  decoder->prev_return = OCSD_RESP_CONT;

There are several error returns that leave this field uninitialized.  I assume that
the decoder is not working in those cases and will not be used.  Don't we need to
undo anything before returning with an error?

I see that we call cs_etm_free_decoder () below on errors, which probably takes
care of cleaning up partially complete decoders.  It still feels odd to return an error
and leave a decoder partially initialized.

Let's at least note that in the comment that on error, the caller is expected to
call cs_etm_free_decoder ().


>+  struct cs_etm_decoder *decoder;
>+
>+  decoder = (struct cs_etm_decoder*) xmalloc (sizeof (struct cs_etm_decoder));
>+  decoder->dcd_tree = dcdtree_handle;
>+
>+  for (int i = 0; i < cpu_count; i++)
>+    {
>+      ocsd_err_t errcode = cs_etm_create_decoder (&(t_params->at (i)), decoder);
>+      if (errcode != OCSD_OK)
>+	{
>+	  cs_etm_free_decoder (decoder);
>+	  return nullptr;
>+	}
>+    }


>+/* Process an etm traces data block.
>+   In case of an error it resets the decoder and tries to process further.  */
>+
>+static void
>+cs_etm_process_data_block (struct btrace_thread_info *btinfo,
>+			   struct cs_etm_decoder *decoder,
>+			   uint64_t index, const uint8_t *buf,
>+			   size_t len, size_t *consumed)
>+{
>+  ocsd_datapath_resp_t data_path_return = OCSD_RESP_CONT;
>+  size_t processed = 0;
>+  uint32_t count;
>+
>+  while (processed < len)
>+    {
>+      if (OCSD_DATA_RESP_IS_CONT (data_path_return))
>+	{
>+	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
>+					OCSD_OP_DATA,
>+					index + processed, len - processed,
>+					&buf[processed], &count);
>+	  processed += count;
>+
>+	}

There seems to be an extra empty line.

Should we assert COUNT > 0 || ! OCSD_DATA_RESP_IS_CONT (data_path_return)
to ensure forward progress?

>+      else if (OCSD_DATA_RESP_IS_WAIT (data_path_return))
>+	{
>+	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
>+					OCSD_OP_FLUSH,
>+					0, 0, nullptr, nullptr);

Same here for ! OCSD_DATA_RESP_IS_WAIT (data_path_return), although we
still wouldn't be able to detect switching back and forth between those two.

>+	}
>+      else
>+	{
>+	  warning (_("error %d in ocsd_dt_process_data after processing %zu"),
>+		      data_path_return, processed);
>+	  ftrace_new_gap (btinfo, data_path_return, decoder->gaps);
>+	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
>+					OCSD_OP_RESET,
>+					0, 0, nullptr, nullptr);
>+	  //todo: shall we increase processed? by 1, or 4 do we need to manually
>align to 16 bytes?

Please use /* */ for comments.  Most people use FIXME instead of todo.

We may end up in an infinite loop if trying to reset the decoder triggers another
error indefinitely.

We should still be able to interrupt GDB itself using ^C on the CLI if we ever actually
run into an infinite decode loop.  So maybe this is all overkill.



>+  ocsd_err_t ocsd_error
>+   = cs_etm_add_mem_access_callback (decoder,
>+				      (CORE_ADDR) 0x0L, (CORE_ADDR) -1L,
>+				      btrace_etm_readmem_callback);
>+  if (ocsd_error != OCSD_OK)
>+    error (_("Failed to add CoreSight Trace decoder memory access callback."));

Could we print ICSD_ERROR in the error message?  Even printing it as an int would help.


>diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
>index 16ffb76272b..57b9c8ec487 100644
>--- a/gdb/record-btrace.c
>+++ b/gdb/record-btrace.c
>@@ -1557,6 +1558,38 @@ record_btrace_target::remove_breakpoint (struct
>gdbarch *gdbarch,
>   return ret;
> }
>
>+/* Reconstruct the instruction set state bits of CPSR register
>+   according to instruction flags.
>+   See Table A2-1 in DDI0406B_arm_architecture_reference_manual
>+   for more details.  */

So we do not need to store CPSR after all?  We can infer the relevant bits
from ISA information we get from the trace?  Nice.

>+
>+static unsigned int
>+cs_etm_reconstruct_cpsr_iset_state (const struct btrace_insn *insn)
>+{
>+  switch (insn->flags & BTRACE_INSN_FLAG_ISA_MASK)
>+    {
>+    case BTRACE_INSN_FLAG_ISA_ARM:
>+      /* ARM state: J and T bits are not set.  */
>+      return 0;
>+
>+    case BTRACE_INSN_FLAG_ISA_THUMB2:
>+      /* THUMB state: J bit is not set, T bit is set.  */
>+      return 0x20;
>+
>+    case BTRACE_INSN_FLAG_ISA_TEE:
>+      /* THUMB EE state: J and T bits are set.  */
>+      return 0x1000020;
>+
>+    case BTRACE_INSN_FLAG_ISA_JAZELLE:
>+      /* JAZELLE state: J bit is set, T bit is not set.  */
>+      return 0x1000000;
>+
>+    default:
>+      /* Default is ARM mode.  */
>+      return 0;

How can we run into this default case?  Should this be an error
in case we add new ISA modes in the future?

>+    }
>+}
>+
> /* The fetch_registers method of target record-btrace.  */
>
> void
>@@ -1581,16 +1614,32 @@ record_btrace_target::fetch_registers (struct
>regcache *regcache, int regno)
>       pcreg = gdbarch_pc_regnum (gdbarch);
>       if (pcreg < 0)
> 	return;
>-
>-      /* We can only provide the PC register.  */
>-      if (regno >= 0 && regno != pcreg)
>+      /* We can only provide the PC or CPSR registers here.  */

Let's extend the comment to say that CPSR is ARM and that we're checking
the architecture below to avoid redundant checks when we want to fetch
other registers.

>+      if (regno >= 0 && !(regno == pcreg || regno == ARM_PS_REGNUM))
> 	return;
>
>       insn = btrace_insn_get (replay);
>-      gdb_assert (insn != NULL);
>+      gdb_assert (insn != nullptr);
>
>+      if ((regno < 0) || (regno == pcreg))
>+	{
> 	  regcache->raw_supply (regno, &insn->pc);
> 	}
>+      if ((regno < 0) || (regno == ARM_PS_REGNUM))
>+	{
>+	  /*  Provide CPSR register in the case of an armv7 target.  */
>+	  const struct target_desc *tdesc = gdbarch_target_desc (gdbarch);
>+
>+	  const char *tdesc_name = tdesc_architecture_name (tdesc);
>+	  if (strcmp (tdesc_name, "arm") == 0)
>+	    {
>+	      int cpsr;
>+	      cpsr = cs_etm_reconstruct_cpsr_iset_state (insn);

Please initialize in the declaration.

>+	      regcache->raw_supply (regno, &cpsr);
>+	    }
>+	}
>+      return;
>+    }
>   else
>     this->beneath ()->fetch_registers (regcache, regno);
> }
>diff --git a/gdbsupport/btrace-common.h b/gdbsupport/btrace-common.h
>index 153b977723a..ee05ecb8b10 100644
>--- a/gdbsupport/btrace-common.h
>+++ b/gdbsupport/btrace-common.h


>+/* Parameters of trace source.  */
>+struct cs_etm_trace_params
>+{
>+  /* Architecture version of trace source.  */
>+  int arch_ver;
>+  /* Core profile of the trace source.  */
>+  int core_profile;
>+  /* Traces protocol.  */
>+  int protocol;
>+  union {
>+    struct cs_etmv3_trace_params etmv3;
>+    struct cs_etmv4_trace_params etmv4;
>+  };
>+};

Please add empty lines between members.

>+
>+/* Configuration information to go with the etm trace data.  */
>+struct btrace_data_etm_config
>+{
>+  /* Count of the CPUs (trace sources).  */
>+  int    cpu_count;
>+  /* List of traces sources parameters.  */
>+  std::vector<struct cs_etm_trace_params> *etm_trace_params;
>+  /* Trace sink parameters.  */
>+  struct cs_etm_decoder_params etm_decoder_params;
>+};

Also here.

>+
>+/* Branch trace in ARM Processor Trace format.  */
>+struct btrace_data_etm

Is that the correct term?


Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 7/7] adapt btrace testcases for arm target
  2021-05-31 21:33 ` [PATCH v6 7/7] adapt btrace testcases for arm target Zied Guermazi
@ 2021-06-22 21:28   ` Lancelot SIX
  2021-06-23 14:16   ` Metzger, Markus T
  2022-05-13 11:08   ` Richard Earnshaw
  2 siblings, 0 replies; 35+ messages in thread
From: Lancelot SIX @ 2021-06-22 21:28 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches, markus.t.metzger

On Mon, May 31, 2021 at 11:33:07PM +0200, Zied Guermazi wrote:
> This patch extends the test suite for btrace to cover using
> ARM CoreSight traces.
> 
> gdb/ChangeLog
> 	* testsuite/lib/gdb.exp (skip_btrace_tests): enable btrace tests
> 	for arm.
> 	* testsuite/gdb.btrace/buffer-size.exp: enable btrace tests
> 	and adapt test for arm.
> 	* testsuite/gdb.btrace/delta.exp (check_trace): enable btrace tests
> 	and adapt test for arm.
> 	* testsuite/gdb.btrace/instruction_history.exp: enable btrace tests
> 	and adapt tests for arm.
> 	* testsuite/gdb.btrace/instruction_history.S: renamed to
> 	x86-instruction_history.S.
> 	* testsuite/gdb.btrace/aarch64-instruction_history.S: New.
> 	* testsuite/gdb.btrace/arm-instruction_history.S: New.
> 	* testsuite/gdb.btrace/non-stop.exp: enable btrace tests
> 	and adapt tests for arm.
> 	* testsuite/gdb.btrace/reconnect.exp: enable btrace tests
> 	and adapt tests for arm.
> 	* testsuite/gdb.btrace/record_goto.exp: enable btrace tests
> 	and adapt tests for arm.
> 	* testsuite/gdb.btrace/aarch64-record_goto.S: New.
> 	* testsuite/gdb.btrace/arm-record_goto.S: New.
> 	* testsuite/gdb.btrace/stepi.exp: enable btrace tests
> 	and adapt tests for arm.
> 	* testsuite/gdb.btrace/tailcall.exp: enable btrace tests
> 	and adapt tests for arm.
> 	* testsuite/gdb.btrace/aarch64-tailcall.S: New.
> 	* testsuite/gdb.btrace/arm-tailcall.S: New.
> 	* testsuite/gdb.btrace/tailcall-only.exp: enable btrace tests
> 	and adapt tests for arm.
> 	* testsuite/gdb.btrace/aarch64-tailcall-only.S: New.
> 	* testsuite/gdb.btrace/arm-tailcall-only.S: New.

Hi,

I only skimmed through and do not have much to say about the content of the
patch itself, but I guess this should go in gdb/testsuite/ChangeLog, not
in gdb/ChangeLog.

Best
Lancelot.

> ---
>  .../gdb.btrace/aarch64-instruction_history.S  |  31 ++
>  .../gdb.btrace/aarch64-record_goto.S          | 399 ++++++++++++++
>  .../gdb.btrace/aarch64-tailcall-only.S        | 516 ++++++++++++++++++
>  gdb/testsuite/gdb.btrace/aarch64-tailcall.S   | 408 ++++++++++++++
>  .../gdb.btrace/arm-instruction_history.S      |  31 ++
>  gdb/testsuite/gdb.btrace/arm-record_goto.S    | 432 +++++++++++++++
>  gdb/testsuite/gdb.btrace/arm-tailcall-only.S  | 503 +++++++++++++++++
>  gdb/testsuite/gdb.btrace/arm-tailcall.S       | 390 +++++++++++++
>  gdb/testsuite/gdb.btrace/buffer-size.exp      |  15 +-
>  .../gdb.btrace/instruction_history.exp        | 106 +++-
>  gdb/testsuite/gdb.btrace/non-stop.exp         |  60 +-
>  gdb/testsuite/gdb.btrace/record_goto.exp      | 253 ++++++---
>  gdb/testsuite/gdb.btrace/stepi.exp            | 107 +++-
>  gdb/testsuite/gdb.btrace/tailcall-only.exp    |  20 +-
>  gdb/testsuite/gdb.btrace/tailcall.exp         |  58 +-
>  ...on_history.S => x86-instruction_history.S} |   0
>  gdb/testsuite/lib/gdb.exp                     |   2 +-
>  17 files changed, 3189 insertions(+), 142 deletions(-)
>  create mode 100644 gdb/testsuite/gdb.btrace/aarch64-instruction_history.S
>  create mode 100644 gdb/testsuite/gdb.btrace/aarch64-record_goto.S
>  create mode 100644 gdb/testsuite/gdb.btrace/aarch64-tailcall-only.S
>  create mode 100644 gdb/testsuite/gdb.btrace/aarch64-tailcall.S
>  create mode 100644 gdb/testsuite/gdb.btrace/arm-instruction_history.S
>  create mode 100644 gdb/testsuite/gdb.btrace/arm-record_goto.S
>  create mode 100644 gdb/testsuite/gdb.btrace/arm-tailcall-only.S
>  create mode 100644 gdb/testsuite/gdb.btrace/arm-tailcall.S
>  rename gdb/testsuite/gdb.btrace/{instruction_history.S => x86-instruction_history.S} (100%)
> 
> diff --git a/gdb/testsuite/gdb.btrace/aarch64-instruction_history.S b/gdb/testsuite/gdb.btrace/aarch64-instruction_history.S
> new file mode 100644
> index 00000000000..0ccf43a1cf4
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/aarch64-instruction_history.S
> @@ -0,0 +1,31 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2021 Free Software Foundation, Inc.
> +
> +   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 3 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/>.  */
> +.arch armv8-a
> +.text
> +.globl loop
> +.type  loop, %function
> +loop:
> +	mov x0, #2 /* bp.1 */
> +L1:
> +	cmp x0, #0
> +	beq L2
> +	subs x0, x0, #1
> +	b L1
> +L2:
> +	ret /* bp.2 */
> +
> +
> diff --git a/gdb/testsuite/gdb.btrace/aarch64-record_goto.S b/gdb/testsuite/gdb.btrace/aarch64-record_goto.S
> new file mode 100644
> index 00000000000..282f8d41a3c
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/aarch64-record_goto.S
> @@ -0,0 +1,399 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2021 Free Software Foundation, Inc.
> +
> +   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 3 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/>.
> +
> +   This file has been generated on an armv8 machine using:
> +   gcc -S -dA -g record_goto.c -o aarch64-record_goto.S  */
> +
> +	.arch armv8-a
> +	.file	"record_goto.c"
> +	.text
> +.Ltext0:
> +	.align	2
> +	.global	fun1
> +	//.tune generic
> +	.type	fun1, %function
> +fun1:
> +.LFB0:
> +	.file 1 "record_goto.c"
> +	// record_goto.c:22:1
> +	.loc 1 22 1
> +	.cfi_startproc
> +// BLOCK 2 seq:0
> +// PRED: ENTRY (FALLTHRU)
> +	// record_goto.c:23:1
> +	.loc 1 23 1
> +	nop
> +// SUCC: EXIT [always] 
> +	ret
> +	.cfi_endproc
> +.LFE0:
> +	.size	fun1, .-fun1
> +	.align	2
> +	.global	fun2
> +	.type	fun2, %function
> +fun2:
> +.LFB1:
> +	// record_goto.c:27:1
> +	.loc 1 27 1
> +	.cfi_startproc
> +// BLOCK 2 seq:0
> +// PRED: ENTRY (FALLTHRU)
> +	stp	x29, x30, [sp, -16]!
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 29, -16
> +	.cfi_offset 30, -8
> +	mov	x29, sp
> +	// record_goto.c:28:3
> +	.loc 1 28 3
> +	bl	fun1
> +	// record_goto.c:29:1
> +	.loc 1 29 1
> +	nop
> +	ldp	x29, x30, [sp], 16
> +	.cfi_restore 30
> +	.cfi_restore 29
> +	.cfi_def_cfa_offset 0
> +// SUCC: EXIT [always] 
> +	ret
> +	.cfi_endproc
> +.LFE1:
> +	.size	fun2, .-fun2
> +	.align	2
> +	.global	fun3
> +	.type	fun3, %function
> +fun3:
> +.LFB2:
> +	// record_goto.c:33:1
> +	.loc 1 33 1
> +	.cfi_startproc
> +// BLOCK 2 seq:0
> +// PRED: ENTRY (FALLTHRU)
> +	stp	x29, x30, [sp, -16]!
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 29, -16
> +	.cfi_offset 30, -8
> +	mov	x29, sp
> +	// record_goto.c:34:3
> +	.loc 1 34 3
> +	bl	fun1
> +	// record_goto.c:35:3
> +	.loc 1 35 3
> +	bl	fun2
> +	// record_goto.c:36:1
> +	.loc 1 36 1
> +	nop
> +	ldp	x29, x30, [sp], 16
> +	.cfi_restore 30
> +	.cfi_restore 29
> +	.cfi_def_cfa_offset 0
> +// SUCC: EXIT [always] 
> +	ret
> +	.cfi_endproc
> +.LFE2:
> +	.size	fun3, .-fun3
> +	.align	2
> +	.global	fun4
> +	.type	fun4, %function
> +fun4:
> +.LFB3:
> +	// record_goto.c:40:1
> +	.loc 1 40 1
> +	.cfi_startproc
> +// BLOCK 2 seq:0
> +// PRED: ENTRY (FALLTHRU)
> +	stp	x29, x30, [sp, -16]!
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 29, -16
> +	.cfi_offset 30, -8
> +	mov	x29, sp
> +	// record_goto.c:41:3
> +	.loc 1 41 3
> +	bl	fun1
> +	// record_goto.c:42:3
> +	.loc 1 42 3
> +	bl	fun2
> +	// record_goto.c:43:3
> +	.loc 1 43 3
> +	bl	fun3
> +	// record_goto.c:44:1
> +	.loc 1 44 1
> +	nop
> +	ldp	x29, x30, [sp], 16
> +	.cfi_restore 30
> +	.cfi_restore 29
> +	.cfi_def_cfa_offset 0
> +// SUCC: EXIT [always] 
> +	ret
> +	.cfi_endproc
> +.LFE3:
> +	.size	fun4, .-fun4
> +	.align	2
> +	.global	main
> +	.type	main, %function
> +main:
> +.LFB4:
> +	// record_goto.c:48:1
> +	.loc 1 48 1
> +	.cfi_startproc
> +// BLOCK 2 seq:0
> +// PRED: ENTRY (FALLTHRU)
> +	stp	x29, x30, [sp, -16]!
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 29, -16
> +	.cfi_offset 30, -8
> +	mov	x29, sp
> +	// record_goto.c:49:3
> +	.loc 1 49 3
> +	bl	fun4
> +	// record_goto.c:50:10
> +	.loc 1 50 10
> +	mov	w0, 0
> +	// record_goto.c:51:1
> +	.loc 1 51 1
> +	ldp	x29, x30, [sp], 16
> +	.cfi_restore 30
> +	.cfi_restore 29
> +	.cfi_def_cfa_offset 0
> +// SUCC: EXIT [always] 
> +	ret
> +	.cfi_endproc
> +.LFE4:
> +	.size	main, .-main
> +.Letext0:
> +	.section	.debug_info,"",@progbits
> +.Ldebug_info0:
> +	.4byte	0xb7	// Length of Compilation Unit Info
> +	.2byte	0x4	// DWARF version number
> +	.4byte	.Ldebug_abbrev0	// Offset Into Abbrev. Section
> +	.byte	0x8	// Pointer Size (in bytes)
> +	.uleb128 0x1	// (DIE (0xb) DW_TAG_compile_unit)
> +	.4byte	.LASF4	// DW_AT_producer: "GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -fasynchronous-unwind-tables"
> +	.byte	0xc	// DW_AT_language
> +	.4byte	.LASF5	// DW_AT_name: "record_goto.c"
> +	.4byte	.LASF6	// DW_AT_comp_dir: "/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
> +	.8byte	.Ltext0	// DW_AT_low_pc
> +	.8byte	.Letext0-.Ltext0	// DW_AT_high_pc
> +	.4byte	.Ldebug_line0	// DW_AT_stmt_list
> +	.uleb128 0x2	// (DIE (0x2d) DW_TAG_subprogram)
> +			// DW_AT_external
> +	.4byte	.LASF7	// DW_AT_name: "main"
> +	.byte	0x1	// DW_AT_decl_file (record_goto.c)
> +	.byte	0x2f	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.4byte	0x4b	// DW_AT_type
> +	.8byte	.LFB4	// DW_AT_low_pc
> +	.8byte	.LFE4-.LFB4	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_tail_call_sites
> +	.uleb128 0x3	// (DIE (0x4b) DW_TAG_base_type)
> +	.byte	0x4	// DW_AT_byte_size
> +	.byte	0x5	// DW_AT_encoding
> +	.ascii "int\0"	// DW_AT_name
> +	.uleb128 0x4	// (DIE (0x52) DW_TAG_subprogram)
> +			// DW_AT_external
> +	.4byte	.LASF0	// DW_AT_name: "fun4"
> +	.byte	0x1	// DW_AT_decl_file (record_goto.c)
> +	.byte	0x27	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.8byte	.LFB3	// DW_AT_low_pc
> +	.8byte	.LFE3-.LFB3	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_tail_call_sites
> +	.uleb128 0x4	// (DIE (0x6c) DW_TAG_subprogram)
> +			// DW_AT_external
> +	.4byte	.LASF1	// DW_AT_name: "fun3"
> +	.byte	0x1	// DW_AT_decl_file (record_goto.c)
> +	.byte	0x20	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.8byte	.LFB2	// DW_AT_low_pc
> +	.8byte	.LFE2-.LFB2	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_tail_call_sites
> +	.uleb128 0x4	// (DIE (0x86) DW_TAG_subprogram)
> +			// DW_AT_external
> +	.4byte	.LASF2	// DW_AT_name: "fun2"
> +	.byte	0x1	// DW_AT_decl_file (record_goto.c)
> +	.byte	0x1a	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.8byte	.LFB1	// DW_AT_low_pc
> +	.8byte	.LFE1-.LFB1	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_tail_call_sites
> +	.uleb128 0x5	// (DIE (0xa0) DW_TAG_subprogram)
> +			// DW_AT_external
> +	.4byte	.LASF3	// DW_AT_name: "fun1"
> +	.byte	0x1	// DW_AT_decl_file (record_goto.c)
> +	.byte	0x15	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.8byte	.LFB0	// DW_AT_low_pc
> +	.8byte	.LFE0-.LFB0	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_call_sites
> +	.byte	0	// end of children of DIE 0xb
> +	.section	.debug_abbrev,"",@progbits
> +.Ldebug_abbrev0:
> +	.uleb128 0x1	// (abbrev code)
> +	.uleb128 0x11	// (TAG: DW_TAG_compile_unit)
> +	.byte	0x1	// DW_children_yes
> +	.uleb128 0x25	// (DW_AT_producer)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x13	// (DW_AT_language)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x1b	// (DW_AT_comp_dir)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x12	// (DW_AT_high_pc)
> +	.uleb128 0x7	// (DW_FORM_data8)
> +	.uleb128 0x10	// (DW_AT_stmt_list)
> +	.uleb128 0x17	// (DW_FORM_sec_offset)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x2	// (abbrev code)
> +	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
> +	.byte	0	// DW_children_no
> +	.uleb128 0x3f	// (DW_AT_external)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x27	// (DW_AT_prototyped)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x49	// (DW_AT_type)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x12	// (DW_AT_high_pc)
> +	.uleb128 0x7	// (DW_FORM_data8)
> +	.uleb128 0x40	// (DW_AT_frame_base)
> +	.uleb128 0x18	// (DW_FORM_exprloc)
> +	.uleb128 0x2116	// (DW_AT_GNU_all_tail_call_sites)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x3	// (abbrev code)
> +	.uleb128 0x24	// (TAG: DW_TAG_base_type)
> +	.byte	0	// DW_children_no
> +	.uleb128 0xb	// (DW_AT_byte_size)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3e	// (DW_AT_encoding)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0x8	// (DW_FORM_string)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x4	// (abbrev code)
> +	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
> +	.byte	0	// DW_children_no
> +	.uleb128 0x3f	// (DW_AT_external)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x27	// (DW_AT_prototyped)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x12	// (DW_AT_high_pc)
> +	.uleb128 0x7	// (DW_FORM_data8)
> +	.uleb128 0x40	// (DW_AT_frame_base)
> +	.uleb128 0x18	// (DW_FORM_exprloc)
> +	.uleb128 0x2116	// (DW_AT_GNU_all_tail_call_sites)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x5	// (abbrev code)
> +	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
> +	.byte	0	// DW_children_no
> +	.uleb128 0x3f	// (DW_AT_external)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x27	// (DW_AT_prototyped)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x12	// (DW_AT_high_pc)
> +	.uleb128 0x7	// (DW_FORM_data8)
> +	.uleb128 0x40	// (DW_AT_frame_base)
> +	.uleb128 0x18	// (DW_FORM_exprloc)
> +	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.byte	0
> +	.byte	0
> +	.byte	0
> +	.section	.debug_aranges,"",@progbits
> +	.4byte	0x2c	// Length of Address Ranges Info
> +	.2byte	0x2	// DWARF aranges version
> +	.4byte	.Ldebug_info0	// Offset of Compilation Unit Info
> +	.byte	0x8	// Size of Address
> +	.byte	0	// Size of Segment Descriptor
> +	.2byte	0	// Pad to 16 byte boundary
> +	.2byte	0
> +	.8byte	.Ltext0	// Address
> +	.8byte	.Letext0-.Ltext0	// Length
> +	.8byte	0
> +	.8byte	0
> +	.section	.debug_line,"",@progbits
> +.Ldebug_line0:
> +	.section	.debug_str,"MS",@progbits,1
> +.LASF5:
> +	.string	"record_goto.c"
> +.LASF4:
> +	.string	"GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -fasynchronous-unwind-tables"
> +.LASF3:
> +	.string	"fun1"
> +.LASF2:
> +	.string	"fun2"
> +.LASF0:
> +	.string	"fun4"
> +.LASF6:
> +	.string	"/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
> +.LASF7:
> +	.string	"main"
> +.LASF1:
> +	.string	"fun3"
> +	.ident	"GCC: (Debian 10.2.1-3) 10.2.1 20201224"
> +	.section	.note.GNU-stack,"",@progbits
> diff --git a/gdb/testsuite/gdb.btrace/aarch64-tailcall-only.S b/gdb/testsuite/gdb.btrace/aarch64-tailcall-only.S
> new file mode 100644
> index 00000000000..f3e12ef2bc8
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/aarch64-tailcall-only.S
> @@ -0,0 +1,516 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2021 Free Software Foundation, Inc.
> +
> +   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 3 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/>.
> +
> +   This file has been generated on an armv8 machine using:
> +   gcc -m32 -S -O2 -dA -g tailcall-only.c -o aarch64-tailcall-only.S
> + */
> +
> +	.arch armv8-a
> +	.file	"tailcall-only.c"
> +	.text
> +.Ltext0:
> +	.align	2
> +	.p2align 4,,11
> +	//.tune generic
> +	.type	bar_1, %function
> +bar_1:
> +.LFB0:
> +	.file 1 "tailcall-only.c"
> +	// tailcall-only.c:22:1
> +	.loc 1 22 1 view -0
> +	.cfi_startproc
> +// BLOCK 2, count:1073741824 (estimated locally) seq:0
> +// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
> +	// tailcall-only.c:23:3
> +	.loc 1 23 3 view .LVU1
> +	// tailcall-only.c:24:1
> +	.loc 1 24 1 is_stmt 0 view .LVU2
> +	mov	w0, 42
> +// SUCC: EXIT [always]  count:1073741824 (estimated locally)
> +	ret
> +	.cfi_endproc
> +.LFE0:
> +	.size	bar_1, .-bar_1
> +	.align	2
> +	.p2align 4,,11
> +	.type	bar, %function
> +bar:
> +.LFB1:
> +	// tailcall-only.c:28:1
> +	.loc 1 28 1 is_stmt 1 view -0
> +	.cfi_startproc
> +// BLOCK 2, count:1073741824 (estimated locally) seq:0
> +// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
> +	// tailcall-only.c:29:3
> +	.loc 1 29 3 view .LVU4
> +// SUCC: EXIT [always]  count:1073741824 (estimated locally) (ABNORMAL,SIBCALL)
> +	// tailcall-only.c:29:10
> +	.loc 1 29 10 is_stmt 0 view .LVU5
> +	b	bar_1
> +.LVL0:
> +	.cfi_endproc
> +.LFE1:
> +	.size	bar, .-bar
> +	.align	2
> +	.p2align 4,,11
> +	.type	foo_1, %function
> +foo_1:
> +.LFB2:
> +	// tailcall-only.c:34:1
> +	.loc 1 34 1 is_stmt 1 view -0
> +	.cfi_startproc
> +// BLOCK 2, count:1073741824 (estimated locally) seq:0
> +// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
> +	// tailcall-only.c:35:3
> +	.loc 1 35 3 view .LVU7
> +// SUCC: EXIT [always]  count:1073741824 (estimated locally) (ABNORMAL,SIBCALL)
> +	// tailcall-only.c:35:10
> +	.loc 1 35 10 is_stmt 0 view .LVU8
> +	b	bar
> +.LVL1:
> +	.cfi_endproc
> +.LFE2:
> +	.size	foo_1, .-foo_1
> +	.align	2
> +	.p2align 4,,11
> +	.type	foo, %function
> +foo:
> +.LFB3:
> +	// tailcall-only.c:40:1
> +	.loc 1 40 1 is_stmt 1 view -0
> +	.cfi_startproc
> +// BLOCK 2, count:1073741824 (estimated locally) seq:0
> +// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
> +	// tailcall-only.c:41:3
> +	.loc 1 41 3 view .LVU10
> +// SUCC: EXIT [always]  count:1073741824 (estimated locally) (ABNORMAL,SIBCALL)
> +	// tailcall-only.c:41:10
> +	.loc 1 41 10 is_stmt 0 view .LVU11
> +	b	foo_1
> +.LVL2:
> +	.cfi_endproc
> +.LFE3:
> +	.size	foo, .-foo
> +	.section	.text.startup,"ax",@progbits
> +	.align	2
> +	.p2align 4,,11
> +	.global	main
> +	.type	main, %function
> +main:
> +.LFB4:
> +	// tailcall-only.c:46:1
> +	.loc 1 46 1 is_stmt 1 view -0
> +	.cfi_startproc
> +// BLOCK 2, count:1073741824 (estimated locally) seq:0
> +// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
> +	// tailcall-only.c:47:3
> +	.loc 1 47 3 view .LVU13
> +	// tailcall-only.c:49:3
> +	.loc 1 49 3 view .LVU14
> +	// tailcall-only.c:46:1
> +	.loc 1 46 1 is_stmt 0 view .LVU15
> +	stp	x29, x30, [sp, -16]!
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 29, -16
> +	.cfi_offset 30, -8
> +	mov	x29, sp
> +	// tailcall-only.c:49:12
> +	.loc 1 49 12 view .LVU16
> +	bl	foo
> +.LVL3:
> +	// DEBUG answer => x0
> +	// tailcall-only.c:50:3
> +	.loc 1 50 3 is_stmt 1 view .LVU17
> +	// DEBUG answer => x0+0x1
> +	// tailcall-only.c:52:3
> +	.loc 1 52 3 view .LVU18
> +	// tailcall-only.c:53:1
> +	.loc 1 53 1 is_stmt 0 view .LVU19
> +	add	w0, w0, 1
> +.LVL4:
> +	// DEBUG answer => x0
> +	// tailcall-only.c:53:1
> +	.loc 1 53 1 view .LVU20
> +	ldp	x29, x30, [sp], 16
> +	.cfi_restore 30
> +	.cfi_restore 29
> +	.cfi_def_cfa_offset 0
> +// SUCC: EXIT [always]  count:1073741824 (estimated locally)
> +	ret
> +	.cfi_endproc
> +.LFE4:
> +	.size	main, .-main
> +	.text
> +.Letext0:
> +	.section	.debug_info,"",@progbits
> +.Ldebug_info0:
> +	.4byte	0x11f	// Length of Compilation Unit Info
> +	.2byte	0x4	// DWARF version number
> +	.4byte	.Ldebug_abbrev0	// Offset Into Abbrev. Section
> +	.byte	0x8	// Pointer Size (in bytes)
> +	.uleb128 0x1	// (DIE (0xb) DW_TAG_compile_unit)
> +	.4byte	.LASF1	// DW_AT_producer: "GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -O2 -fasynchronous-unwind-tables"
> +	.byte	0xc	// DW_AT_language
> +	.4byte	.LASF2	// DW_AT_name: "tailcall-only.c"
> +	.4byte	.LASF3	// DW_AT_comp_dir: "/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
> +	.4byte	.Ldebug_ranges0+0	// DW_AT_ranges
> +	.8byte	0	// DW_AT_low_pc
> +	.4byte	.Ldebug_line0	// DW_AT_stmt_list
> +	.uleb128 0x2	// (DIE (0x29) DW_TAG_subprogram)
> +			// DW_AT_external
> +	.4byte	.LASF4	// DW_AT_name: "main"
> +	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x2d	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.4byte	0x6d	// DW_AT_type
> +	.8byte	.LFB4	// DW_AT_low_pc
> +	.8byte	.LFE4-.LFB4	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_call_sites
> +	.4byte	0x6d	// DW_AT_sibling
> +	.uleb128 0x3	// (DIE (0x4b) DW_TAG_variable)
> +	.4byte	.LASF5	// DW_AT_name: "answer"
> +	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x2f	// DW_AT_decl_line
> +	.byte	0x7	// DW_AT_decl_column
> +	.4byte	0x6d	// DW_AT_type
> +	.4byte	.LLST0	// DW_AT_location
> +	.4byte	.LVUS0	// DW_AT_GNU_locviews
> +	.uleb128 0x4	// (DIE (0x5f) DW_TAG_GNU_call_site)
> +	.8byte	.LVL3	// DW_AT_low_pc
> +	.4byte	0x74	// DW_AT_abstract_origin
> +	.byte	0	// end of children of DIE 0x29
> +	.uleb128 0x5	// (DIE (0x6d) DW_TAG_base_type)
> +	.byte	0x4	// DW_AT_byte_size
> +	.byte	0x5	// DW_AT_encoding
> +	.ascii "int\0"	// DW_AT_name
> +	.uleb128 0x6	// (DIE (0x74) DW_TAG_subprogram)
> +	.ascii "foo\0"	// DW_AT_name
> +	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x27	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.4byte	0x6d	// DW_AT_type
> +	.8byte	.LFB3	// DW_AT_low_pc
> +	.8byte	.LFE3-.LFB3	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_call_sites
> +	.4byte	0xa4	// DW_AT_sibling
> +	.uleb128 0x7	// (DIE (0x96) DW_TAG_GNU_call_site)
> +	.8byte	.LVL2	// DW_AT_low_pc
> +			// DW_AT_GNU_tail_call
> +	.4byte	0xa4	// DW_AT_abstract_origin
> +	.byte	0	// end of children of DIE 0x74
> +	.uleb128 0x8	// (DIE (0xa4) DW_TAG_subprogram)
> +	.4byte	.LASF0	// DW_AT_name: "foo_1"
> +	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x21	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.4byte	0x6d	// DW_AT_type
> +	.8byte	.LFB2	// DW_AT_low_pc
> +	.8byte	.LFE2-.LFB2	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_call_sites
> +	.4byte	0xd4	// DW_AT_sibling
> +	.uleb128 0x7	// (DIE (0xc6) DW_TAG_GNU_call_site)
> +	.8byte	.LVL1	// DW_AT_low_pc
> +			// DW_AT_GNU_tail_call
> +	.4byte	0xd4	// DW_AT_abstract_origin
> +	.byte	0	// end of children of DIE 0xa4
> +	.uleb128 0x6	// (DIE (0xd4) DW_TAG_subprogram)
> +	.ascii "bar\0"	// DW_AT_name
> +	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x1b	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.4byte	0x6d	// DW_AT_type
> +	.8byte	.LFB1	// DW_AT_low_pc
> +	.8byte	.LFE1-.LFB1	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_call_sites
> +	.4byte	0x104	// DW_AT_sibling
> +	.uleb128 0x7	// (DIE (0xf6) DW_TAG_GNU_call_site)
> +	.8byte	.LVL0	// DW_AT_low_pc
> +			// DW_AT_GNU_tail_call
> +	.4byte	0x104	// DW_AT_abstract_origin
> +	.byte	0	// end of children of DIE 0xd4
> +	.uleb128 0x9	// (DIE (0x104) DW_TAG_subprogram)
> +	.4byte	.LASF6	// DW_AT_name: "bar_1"
> +	.byte	0x1	// DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x15	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.4byte	0x6d	// DW_AT_type
> +	.8byte	.LFB0	// DW_AT_low_pc
> +	.8byte	.LFE0-.LFB0	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_call_sites
> +	.byte	0	// end of children of DIE 0xb
> +	.section	.debug_abbrev,"",@progbits
> +.Ldebug_abbrev0:
> +	.uleb128 0x1	// (abbrev code)
> +	.uleb128 0x11	// (TAG: DW_TAG_compile_unit)
> +	.byte	0x1	// DW_children_yes
> +	.uleb128 0x25	// (DW_AT_producer)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x13	// (DW_AT_language)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x1b	// (DW_AT_comp_dir)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x55	// (DW_AT_ranges)
> +	.uleb128 0x17	// (DW_FORM_sec_offset)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x10	// (DW_AT_stmt_list)
> +	.uleb128 0x17	// (DW_FORM_sec_offset)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x2	// (abbrev code)
> +	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
> +	.byte	0x1	// DW_children_yes
> +	.uleb128 0x3f	// (DW_AT_external)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x27	// (DW_AT_prototyped)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x49	// (DW_AT_type)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x12	// (DW_AT_high_pc)
> +	.uleb128 0x7	// (DW_FORM_data8)
> +	.uleb128 0x40	// (DW_AT_frame_base)
> +	.uleb128 0x18	// (DW_FORM_exprloc)
> +	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x1	// (DW_AT_sibling)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x3	// (abbrev code)
> +	.uleb128 0x34	// (TAG: DW_TAG_variable)
> +	.byte	0	// DW_children_no
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x49	// (DW_AT_type)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.uleb128 0x2	// (DW_AT_location)
> +	.uleb128 0x17	// (DW_FORM_sec_offset)
> +	.uleb128 0x2137	// (DW_AT_GNU_locviews)
> +	.uleb128 0x17	// (DW_FORM_sec_offset)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x4	// (abbrev code)
> +	.uleb128 0x4109	// (TAG: DW_TAG_GNU_call_site)
> +	.byte	0	// DW_children_no
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x31	// (DW_AT_abstract_origin)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x5	// (abbrev code)
> +	.uleb128 0x24	// (TAG: DW_TAG_base_type)
> +	.byte	0	// DW_children_no
> +	.uleb128 0xb	// (DW_AT_byte_size)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3e	// (DW_AT_encoding)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0x8	// (DW_FORM_string)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x6	// (abbrev code)
> +	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
> +	.byte	0x1	// DW_children_yes
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0x8	// (DW_FORM_string)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x27	// (DW_AT_prototyped)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x49	// (DW_AT_type)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x12	// (DW_AT_high_pc)
> +	.uleb128 0x7	// (DW_FORM_data8)
> +	.uleb128 0x40	// (DW_AT_frame_base)
> +	.uleb128 0x18	// (DW_FORM_exprloc)
> +	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x1	// (DW_AT_sibling)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x7	// (abbrev code)
> +	.uleb128 0x4109	// (TAG: DW_TAG_GNU_call_site)
> +	.byte	0	// DW_children_no
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x2115	// (DW_AT_GNU_tail_call)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x31	// (DW_AT_abstract_origin)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x8	// (abbrev code)
> +	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
> +	.byte	0x1	// DW_children_yes
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x27	// (DW_AT_prototyped)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x49	// (DW_AT_type)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x12	// (DW_AT_high_pc)
> +	.uleb128 0x7	// (DW_FORM_data8)
> +	.uleb128 0x40	// (DW_AT_frame_base)
> +	.uleb128 0x18	// (DW_FORM_exprloc)
> +	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x1	// (DW_AT_sibling)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x9	// (abbrev code)
> +	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
> +	.byte	0	// DW_children_no
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x27	// (DW_AT_prototyped)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x49	// (DW_AT_type)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x12	// (DW_AT_high_pc)
> +	.uleb128 0x7	// (DW_FORM_data8)
> +	.uleb128 0x40	// (DW_AT_frame_base)
> +	.uleb128 0x18	// (DW_FORM_exprloc)
> +	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.byte	0
> +	.byte	0
> +	.byte	0
> +	.section	.debug_loc,"",@progbits
> +.Ldebug_loc0:
> +.LVUS0:
> +	.uleb128 .LVU17	// View list begin (*.LVUS0)
> +	.uleb128 .LVU18	// View list end (*.LVUS0)
> +	.uleb128 .LVU18	// View list begin (*.LVUS0)
> +	.uleb128 .LVU20	// View list end (*.LVUS0)
> +	.uleb128 .LVU20	// View list begin (*.LVUS0)
> +	.uleb128 0	// View list end (*.LVUS0)
> +.LLST0:
> +	.8byte	.LVL3	// Location list begin address (*.LLST0)
> +	.8byte	.LVL3	// Location list end address (*.LLST0)
> +	.2byte	0x1	// Location expression size
> +	.byte	0x50	// DW_OP_reg0
> +	.8byte	.LVL3	// Location list begin address (*.LLST0)
> +	.8byte	.LVL4	// Location list end address (*.LLST0)
> +	.2byte	0x3	// Location expression size
> +	.byte	0x70	// DW_OP_breg0
> +	.sleb128 1
> +	.byte	0x9f	// DW_OP_stack_value
> +	.8byte	.LVL4	// Location list begin address (*.LLST0)
> +	.8byte	.LFE4	// Location list end address (*.LLST0)
> +	.2byte	0x1	// Location expression size
> +	.byte	0x50	// DW_OP_reg0
> +	.8byte	0	// Location list terminator begin (*.LLST0)
> +	.8byte	0	// Location list terminator end (*.LLST0)
> +	.section	.debug_aranges,"",@progbits
> +	.4byte	0x3c	// Length of Address Ranges Info
> +	.2byte	0x2	// DWARF aranges version
> +	.4byte	.Ldebug_info0	// Offset of Compilation Unit Info
> +	.byte	0x8	// Size of Address
> +	.byte	0	// Size of Segment Descriptor
> +	.2byte	0	// Pad to 16 byte boundary
> +	.2byte	0
> +	.8byte	.Ltext0	// Address
> +	.8byte	.Letext0-.Ltext0	// Length
> +	.8byte	.LFB4	// Address
> +	.8byte	.LFE4-.LFB4	// Length
> +	.8byte	0
> +	.8byte	0
> +	.section	.debug_ranges,"",@progbits
> +.Ldebug_ranges0:
> +	.8byte	.Ltext0	// Offset 0
> +	.8byte	.Letext0
> +	.8byte	.LFB4	// Offset 0x10
> +	.8byte	.LFE4
> +	.8byte	0
> +	.8byte	0
> +	.section	.debug_line,"",@progbits
> +.Ldebug_line0:
> +	.section	.debug_str,"MS",@progbits,1
> +.LASF6:
> +	.string	"bar_1"
> +.LASF2:
> +	.string	"tailcall-only.c"
> +.LASF3:
> +	.string	"/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
> +.LASF5:
> +	.string	"answer"
> +.LASF4:
> +	.string	"main"
> +.LASF1:
> +	.string	"GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -O2 -fasynchronous-unwind-tables"
> +.LASF0:
> +	.string	"foo_1"
> +	.ident	"GCC: (Debian 10.2.1-3) 10.2.1 20201224"
> +	.section	.note.GNU-stack,"",@progbits
> diff --git a/gdb/testsuite/gdb.btrace/aarch64-tailcall.S b/gdb/testsuite/gdb.btrace/aarch64-tailcall.S
> new file mode 100644
> index 00000000000..2023b878a1a
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/aarch64-tailcall.S
> @@ -0,0 +1,408 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2021 Free Software Foundation, Inc.
> +
> +   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 3 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/>.
> +
> +   This file has been generated on an armv8 machine using:
> +   gcc -S -O2 -dA -g tailcall.c -o aarch64-tailcall.S  */
> +
> +	.arch armv8-a
> +	.file	"tailcall.c"
> +	.text
> +.Ltext0:
> +	.align	2
> +	.p2align 4,,11
> +	//.tune generic
> +	.type	bar, %function
> +bar:
> +.LFB0:
> +	.file 1 "tailcall.c"
> +	// tailcall.c:22:1
> +	.loc 1 22 1 view -0
> +	.cfi_startproc
> +// BLOCK 2, count:1073741824 (estimated locally) seq:0
> +// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
> +	// tailcall.c:23:3
> +	.loc 1 23 3 view .LVU1
> +	// tailcall.c:24:1
> +	.loc 1 24 1 is_stmt 0 view .LVU2
> +	mov	w0, 42
> +// SUCC: EXIT [always]  count:1073741824 (estimated locally)
> +	ret
> +	.cfi_endproc
> +.LFE0:
> +	.size	bar, .-bar
> +	.align	2
> +	.p2align 4,,11
> +	.type	foo, %function
> +foo:
> +.LFB1:
> +	// tailcall.c:28:1
> +	.loc 1 28 1 is_stmt 1 view -0
> +	.cfi_startproc
> +// BLOCK 2, count:1073741824 (estimated locally) seq:0
> +// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
> +	// tailcall.c:29:3
> +	.loc 1 29 3 view .LVU4
> +// SUCC: EXIT [always]  count:1073741824 (estimated locally) (ABNORMAL,SIBCALL)
> +	// tailcall.c:29:10
> +	.loc 1 29 10 is_stmt 0 view .LVU5
> +	b	bar
> +.LVL0:
> +	.cfi_endproc
> +.LFE1:
> +	.size	foo, .-foo
> +	.section	.text.startup,"ax",@progbits
> +	.align	2
> +	.p2align 4,,11
> +	.global	main
> +	.type	main, %function
> +main:
> +.LFB2:
> +	// tailcall.c:34:1
> +	.loc 1 34 1 is_stmt 1 view -0
> +	.cfi_startproc
> +// BLOCK 2, count:1073741824 (estimated locally) seq:0
> +// PRED: ENTRY [always]  count:1073741824 (estimated locally) (FALLTHRU)
> +	// tailcall.c:35:3
> +	.loc 1 35 3 view .LVU7
> +	// tailcall.c:37:3
> +	.loc 1 37 3 view .LVU8
> +	// tailcall.c:34:1
> +	.loc 1 34 1 is_stmt 0 view .LVU9
> +	stp	x29, x30, [sp, -16]!
> +	.cfi_def_cfa_offset 16
> +	.cfi_offset 29, -16
> +	.cfi_offset 30, -8
> +	mov	x29, sp
> +	// tailcall.c:37:12
> +	.loc 1 37 12 view .LVU10
> +	bl	foo
> +.LVL1:
> +	// DEBUG answer => x0
> +	// tailcall.c:38:3
> +	.loc 1 38 3 is_stmt 1 view .LVU11
> +	// DEBUG answer => x0+0x1
> +	// tailcall.c:40:3
> +	.loc 1 40 3 view .LVU12
> +	// tailcall.c:41:1
> +	.loc 1 41 1 is_stmt 0 view .LVU13
> +	add	w0, w0, 1
> +.LVL2:
> +	// DEBUG answer => x0
> +	// tailcall.c:41:1
> +	.loc 1 41 1 view .LVU14
> +	ldp	x29, x30, [sp], 16
> +	.cfi_restore 30
> +	.cfi_restore 29
> +	.cfi_def_cfa_offset 0
> +// SUCC: EXIT [always]  count:1073741824 (estimated locally)
> +	ret
> +	.cfi_endproc
> +.LFE2:
> +	.size	main, .-main
> +	.text
> +.Letext0:
> +	.section	.debug_info,"",@progbits
> +.Ldebug_info0:
> +	.4byte	0xbf	// Length of Compilation Unit Info
> +	.2byte	0x4	// DWARF version number
> +	.4byte	.Ldebug_abbrev0	// Offset Into Abbrev. Section
> +	.byte	0x8	// Pointer Size (in bytes)
> +	.uleb128 0x1	// (DIE (0xb) DW_TAG_compile_unit)
> +	.4byte	.LASF0	// DW_AT_producer: "GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -O2 -fasynchronous-unwind-tables"
> +	.byte	0xc	// DW_AT_language
> +	.4byte	.LASF1	// DW_AT_name: "tailcall.c"
> +	.4byte	.LASF2	// DW_AT_comp_dir: "/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
> +	.4byte	.Ldebug_ranges0+0	// DW_AT_ranges
> +	.8byte	0	// DW_AT_low_pc
> +	.4byte	.Ldebug_line0	// DW_AT_stmt_list
> +	.uleb128 0x2	// (DIE (0x29) DW_TAG_subprogram)
> +			// DW_AT_external
> +	.4byte	.LASF3	// DW_AT_name: "main"
> +	.byte	0x1	// DW_AT_decl_file (tailcall.c)
> +	.byte	0x21	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.4byte	0x6d	// DW_AT_type
> +	.8byte	.LFB2	// DW_AT_low_pc
> +	.8byte	.LFE2-.LFB2	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_call_sites
> +	.4byte	0x6d	// DW_AT_sibling
> +	.uleb128 0x3	// (DIE (0x4b) DW_TAG_variable)
> +	.4byte	.LASF4	// DW_AT_name: "answer"
> +	.byte	0x1	// DW_AT_decl_file (tailcall.c)
> +	.byte	0x23	// DW_AT_decl_line
> +	.byte	0x7	// DW_AT_decl_column
> +	.4byte	0x6d	// DW_AT_type
> +	.4byte	.LLST0	// DW_AT_location
> +	.4byte	.LVUS0	// DW_AT_GNU_locviews
> +	.uleb128 0x4	// (DIE (0x5f) DW_TAG_GNU_call_site)
> +	.8byte	.LVL1	// DW_AT_low_pc
> +	.4byte	0x74	// DW_AT_abstract_origin
> +	.byte	0	// end of children of DIE 0x29
> +	.uleb128 0x5	// (DIE (0x6d) DW_TAG_base_type)
> +	.byte	0x4	// DW_AT_byte_size
> +	.byte	0x5	// DW_AT_encoding
> +	.ascii "int\0"	// DW_AT_name
> +	.uleb128 0x6	// (DIE (0x74) DW_TAG_subprogram)
> +	.ascii "foo\0"	// DW_AT_name
> +	.byte	0x1	// DW_AT_decl_file (tailcall.c)
> +	.byte	0x1b	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.4byte	0x6d	// DW_AT_type
> +	.8byte	.LFB1	// DW_AT_low_pc
> +	.8byte	.LFE1-.LFB1	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_call_sites
> +	.4byte	0xa4	// DW_AT_sibling
> +	.uleb128 0x7	// (DIE (0x96) DW_TAG_GNU_call_site)
> +	.8byte	.LVL0	// DW_AT_low_pc
> +			// DW_AT_GNU_tail_call
> +	.4byte	0xa4	// DW_AT_abstract_origin
> +	.byte	0	// end of children of DIE 0x74
> +	.uleb128 0x8	// (DIE (0xa4) DW_TAG_subprogram)
> +	.ascii "bar\0"	// DW_AT_name
> +	.byte	0x1	// DW_AT_decl_file (tailcall.c)
> +	.byte	0x15	// DW_AT_decl_line
> +	.byte	0x1	// DW_AT_decl_column
> +			// DW_AT_prototyped
> +	.4byte	0x6d	// DW_AT_type
> +	.8byte	.LFB0	// DW_AT_low_pc
> +	.8byte	.LFE0-.LFB0	// DW_AT_high_pc
> +	.uleb128 0x1	// DW_AT_frame_base
> +	.byte	0x9c	// DW_OP_call_frame_cfa
> +			// DW_AT_GNU_all_call_sites
> +	.byte	0	// end of children of DIE 0xb
> +	.section	.debug_abbrev,"",@progbits
> +.Ldebug_abbrev0:
> +	.uleb128 0x1	// (abbrev code)
> +	.uleb128 0x11	// (TAG: DW_TAG_compile_unit)
> +	.byte	0x1	// DW_children_yes
> +	.uleb128 0x25	// (DW_AT_producer)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x13	// (DW_AT_language)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x1b	// (DW_AT_comp_dir)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x55	// (DW_AT_ranges)
> +	.uleb128 0x17	// (DW_FORM_sec_offset)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x10	// (DW_AT_stmt_list)
> +	.uleb128 0x17	// (DW_FORM_sec_offset)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x2	// (abbrev code)
> +	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
> +	.byte	0x1	// DW_children_yes
> +	.uleb128 0x3f	// (DW_AT_external)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x27	// (DW_AT_prototyped)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x49	// (DW_AT_type)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x12	// (DW_AT_high_pc)
> +	.uleb128 0x7	// (DW_FORM_data8)
> +	.uleb128 0x40	// (DW_AT_frame_base)
> +	.uleb128 0x18	// (DW_FORM_exprloc)
> +	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x1	// (DW_AT_sibling)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x3	// (abbrev code)
> +	.uleb128 0x34	// (TAG: DW_TAG_variable)
> +	.byte	0	// DW_children_no
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0xe	// (DW_FORM_strp)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x49	// (DW_AT_type)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.uleb128 0x2	// (DW_AT_location)
> +	.uleb128 0x17	// (DW_FORM_sec_offset)
> +	.uleb128 0x2137	// (DW_AT_GNU_locviews)
> +	.uleb128 0x17	// (DW_FORM_sec_offset)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x4	// (abbrev code)
> +	.uleb128 0x4109	// (TAG: DW_TAG_GNU_call_site)
> +	.byte	0	// DW_children_no
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x31	// (DW_AT_abstract_origin)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x5	// (abbrev code)
> +	.uleb128 0x24	// (TAG: DW_TAG_base_type)
> +	.byte	0	// DW_children_no
> +	.uleb128 0xb	// (DW_AT_byte_size)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3e	// (DW_AT_encoding)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0x8	// (DW_FORM_string)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x6	// (abbrev code)
> +	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
> +	.byte	0x1	// DW_children_yes
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0x8	// (DW_FORM_string)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x27	// (DW_AT_prototyped)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x49	// (DW_AT_type)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x12	// (DW_AT_high_pc)
> +	.uleb128 0x7	// (DW_FORM_data8)
> +	.uleb128 0x40	// (DW_AT_frame_base)
> +	.uleb128 0x18	// (DW_FORM_exprloc)
> +	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x1	// (DW_AT_sibling)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x7	// (abbrev code)
> +	.uleb128 0x4109	// (TAG: DW_TAG_GNU_call_site)
> +	.byte	0	// DW_children_no
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x2115	// (DW_AT_GNU_tail_call)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x31	// (DW_AT_abstract_origin)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x8	// (abbrev code)
> +	.uleb128 0x2e	// (TAG: DW_TAG_subprogram)
> +	.byte	0	// DW_children_no
> +	.uleb128 0x3	// (DW_AT_name)
> +	.uleb128 0x8	// (DW_FORM_string)
> +	.uleb128 0x3a	// (DW_AT_decl_file)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x3b	// (DW_AT_decl_line)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x39	// (DW_AT_decl_column)
> +	.uleb128 0xb	// (DW_FORM_data1)
> +	.uleb128 0x27	// (DW_AT_prototyped)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.uleb128 0x49	// (DW_AT_type)
> +	.uleb128 0x13	// (DW_FORM_ref4)
> +	.uleb128 0x11	// (DW_AT_low_pc)
> +	.uleb128 0x1	// (DW_FORM_addr)
> +	.uleb128 0x12	// (DW_AT_high_pc)
> +	.uleb128 0x7	// (DW_FORM_data8)
> +	.uleb128 0x40	// (DW_AT_frame_base)
> +	.uleb128 0x18	// (DW_FORM_exprloc)
> +	.uleb128 0x2117	// (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	// (DW_FORM_flag_present)
> +	.byte	0
> +	.byte	0
> +	.byte	0
> +	.section	.debug_loc,"",@progbits
> +.Ldebug_loc0:
> +.LVUS0:
> +	.uleb128 .LVU11	// View list begin (*.LVUS0)
> +	.uleb128 .LVU12	// View list end (*.LVUS0)
> +	.uleb128 .LVU12	// View list begin (*.LVUS0)
> +	.uleb128 .LVU14	// View list end (*.LVUS0)
> +	.uleb128 .LVU14	// View list begin (*.LVUS0)
> +	.uleb128 0	// View list end (*.LVUS0)
> +.LLST0:
> +	.8byte	.LVL1	// Location list begin address (*.LLST0)
> +	.8byte	.LVL1	// Location list end address (*.LLST0)
> +	.2byte	0x1	// Location expression size
> +	.byte	0x50	// DW_OP_reg0
> +	.8byte	.LVL1	// Location list begin address (*.LLST0)
> +	.8byte	.LVL2	// Location list end address (*.LLST0)
> +	.2byte	0x3	// Location expression size
> +	.byte	0x70	// DW_OP_breg0
> +	.sleb128 1
> +	.byte	0x9f	// DW_OP_stack_value
> +	.8byte	.LVL2	// Location list begin address (*.LLST0)
> +	.8byte	.LFE2	// Location list end address (*.LLST0)
> +	.2byte	0x1	// Location expression size
> +	.byte	0x50	// DW_OP_reg0
> +	.8byte	0	// Location list terminator begin (*.LLST0)
> +	.8byte	0	// Location list terminator end (*.LLST0)
> +	.section	.debug_aranges,"",@progbits
> +	.4byte	0x3c	// Length of Address Ranges Info
> +	.2byte	0x2	// DWARF aranges version
> +	.4byte	.Ldebug_info0	// Offset of Compilation Unit Info
> +	.byte	0x8	// Size of Address
> +	.byte	0	// Size of Segment Descriptor
> +	.2byte	0	// Pad to 16 byte boundary
> +	.2byte	0
> +	.8byte	.Ltext0	// Address
> +	.8byte	.Letext0-.Ltext0	// Length
> +	.8byte	.LFB2	// Address
> +	.8byte	.LFE2-.LFB2	// Length
> +	.8byte	0
> +	.8byte	0
> +	.section	.debug_ranges,"",@progbits
> +.Ldebug_ranges0:
> +	.8byte	.Ltext0	// Offset 0
> +	.8byte	.Letext0
> +	.8byte	.LFB2	// Offset 0x10
> +	.8byte	.LFE2
> +	.8byte	0
> +	.8byte	0
> +	.section	.debug_line,"",@progbits
> +.Ldebug_line0:
> +	.section	.debug_str,"MS",@progbits,1
> +.LASF0:
> +	.string	"GNU C17 10.2.1 20201224 -mlittle-endian -mabi=lp64 -g -O2 -fasynchronous-unwind-tables"
> +.LASF2:
> +	.string	"/home/linaro/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
> +.LASF4:
> +	.string	"answer"
> +.LASF1:
> +	.string	"tailcall.c"
> +.LASF3:
> +	.string	"main"
> +	.ident	"GCC: (Debian 10.2.1-3) 10.2.1 20201224"
> +	.section	.note.GNU-stack,"",@progbits
> diff --git a/gdb/testsuite/gdb.btrace/arm-instruction_history.S b/gdb/testsuite/gdb.btrace/arm-instruction_history.S
> new file mode 100644
> index 00000000000..16ef0356993
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/arm-instruction_history.S
> @@ -0,0 +1,31 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2021 Free Software Foundation, Inc.
> +
> +   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 3 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/>.  */
> +
> +.arm
> +.text
> +.globl loop
> +.type  loop, %function
> +loop:
> +	movs r0, #2 /* bp.1 */
> +L1:
> +	cmp r0, #0
> +	beq L2
> +	subs r0, r0, #1
> +	b L1
> +L2:
> +	bx lr /* bp.2 */
> +
> diff --git a/gdb/testsuite/gdb.btrace/arm-record_goto.S b/gdb/testsuite/gdb.btrace/arm-record_goto.S
> new file mode 100644
> index 00000000000..d68f1187a74
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/arm-record_goto.S
> @@ -0,0 +1,432 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2021 Free Software Foundation, Inc.
> +
> +   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 3 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/>.
> +
> +   This file has been generated on an armv7 machine using:
> +   gcc -S -dA -g record_goto.c -o arm-record_goto.S  */
> +
> +	.arch armv7-a
> +	.eabi_attribute 28, 1	@ Tag_ABI_VFP_args
> +	.eabi_attribute 20, 1	@ Tag_ABI_FP_denormal
> +	.eabi_attribute 21, 1	@ Tag_ABI_FP_exceptions
> +	.eabi_attribute 23, 3	@ Tag_ABI_FP_number_model
> +	.eabi_attribute 24, 1	@ Tag_ABI_align8_needed
> +	.eabi_attribute 25, 1	@ Tag_ABI_align8_preserved
> +	.eabi_attribute 26, 2	@ Tag_ABI_enum_size
> +	.eabi_attribute 30, 6	@ Tag_ABI_optimization_goals
> +	.eabi_attribute 34, 1	@ Tag_CPU_unaligned_access
> +	.eabi_attribute 18, 4	@ Tag_ABI_PCS_wchar_t
> +	.file	"record_goto.c"
> +	.text
> +.Ltext0:
> +	.cfi_sections	.debug_frame
> +	.align	1
> +	.global	fun1
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	fun1, %function
> +fun1:
> +.LFB0:
> +	.file 1 "record_goto.c"
> +	@ record_goto.c:22
> +	.loc 1 22 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 1, uses_anonymous_args = 0
> +	@ link register save eliminated.
> +@ BLOCK 2 seq:0
> +@ PRED: ENTRY (FALLTHRU)
> +	push	{r7}
> +	.cfi_def_cfa_offset 4
> +	.cfi_offset 7, -4
> +	add	r7, sp, #0
> +	.cfi_def_cfa_register 7
> +	@ record_goto.c:23
> +	.loc 1 23 0
> +	nop
> +	mov	sp, r7
> +	.cfi_def_cfa_register 13
> +	@ sp needed
> +	ldr	r7, [sp], #4
> +	.cfi_restore 7
> +	.cfi_def_cfa_offset 0
> +@ SUCC: EXIT [100.0%] 
> +	bx	lr
> +	.cfi_endproc
> +.LFE0:
> +	.size	fun1, .-fun1
> +	.align	1
> +	.global	fun2
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	fun2, %function
> +fun2:
> +.LFB1:
> +	@ record_goto.c:27
> +	.loc 1 27 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 1, uses_anonymous_args = 0
> +@ BLOCK 2 seq:0
> +@ PRED: ENTRY (FALLTHRU)
> +	push	{r7, lr}
> +	.cfi_def_cfa_offset 8
> +	.cfi_offset 7, -8
> +	.cfi_offset 14, -4
> +	add	r7, sp, #0
> +	.cfi_def_cfa_register 7
> +	@ record_goto.c:28
> +	.loc 1 28 0
> +	bl	fun1(PLT)
> +	@ record_goto.c:29
> +	.loc 1 29 0
> +	nop
> +@ SUCC: EXIT [100.0%] 
> +	pop	{r7, pc}
> +	.cfi_endproc
> +.LFE1:
> +	.size	fun2, .-fun2
> +	.align	1
> +	.global	fun3
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	fun3, %function
> +fun3:
> +.LFB2:
> +	@ record_goto.c:33
> +	.loc 1 33 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 1, uses_anonymous_args = 0
> +@ BLOCK 2 seq:0
> +@ PRED: ENTRY (FALLTHRU)
> +	push	{r7, lr}
> +	.cfi_def_cfa_offset 8
> +	.cfi_offset 7, -8
> +	.cfi_offset 14, -4
> +	add	r7, sp, #0
> +	.cfi_def_cfa_register 7
> +	@ record_goto.c:34
> +	.loc 1 34 0
> +	bl	fun1(PLT)
> +	@ record_goto.c:35
> +	.loc 1 35 0
> +	bl	fun2(PLT)
> +	@ record_goto.c:36
> +	.loc 1 36 0
> +	nop
> +@ SUCC: EXIT [100.0%] 
> +	pop	{r7, pc}
> +	.cfi_endproc
> +.LFE2:
> +	.size	fun3, .-fun3
> +	.align	1
> +	.global	fun4
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	fun4, %function
> +fun4:
> +.LFB3:
> +	@ record_goto.c:40
> +	.loc 1 40 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 1, uses_anonymous_args = 0
> +@ BLOCK 2 seq:0
> +@ PRED: ENTRY (FALLTHRU)
> +	push	{r7, lr}
> +	.cfi_def_cfa_offset 8
> +	.cfi_offset 7, -8
> +	.cfi_offset 14, -4
> +	add	r7, sp, #0
> +	.cfi_def_cfa_register 7
> +	@ record_goto.c:41
> +	.loc 1 41 0
> +	bl	fun1(PLT)
> +	@ record_goto.c:42
> +	.loc 1 42 0
> +	bl	fun2(PLT)
> +	@ record_goto.c:43
> +	.loc 1 43 0
> +	bl	fun3(PLT)
> +	@ record_goto.c:44
> +	.loc 1 44 0
> +	nop
> +@ SUCC: EXIT [100.0%] 
> +	pop	{r7, pc}
> +	.cfi_endproc
> +.LFE3:
> +	.size	fun4, .-fun4
> +	.align	1
> +	.global	main
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	main, %function
> +main:
> +.LFB4:
> +	@ record_goto.c:48
> +	.loc 1 48 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 1, uses_anonymous_args = 0
> +@ BLOCK 2 seq:0
> +@ PRED: ENTRY (FALLTHRU)
> +	push	{r7, lr}
> +	.cfi_def_cfa_offset 8
> +	.cfi_offset 7, -8
> +	.cfi_offset 14, -4
> +	add	r7, sp, #0
> +	.cfi_def_cfa_register 7
> +	@ record_goto.c:49
> +	.loc 1 49 0
> +	bl	fun4(PLT)
> +	@ record_goto.c:50
> +	.loc 1 50 0
> +	movs	r3, #0
> +	@ record_goto.c:51
> +	.loc 1 51 0
> +	mov	r0, r3
> +@ SUCC: EXIT [100.0%] 
> +	pop	{r7, pc}
> +	.cfi_endproc
> +.LFE4:
> +	.size	main, .-main
> +.Letext0:
> +	.section	.debug_info,"",%progbits
> +.Ldebug_info0:
> +	.4byte	0x82	@ Length of Compilation Unit Info
> +	.2byte	0x4	@ DWARF version number
> +	.4byte	.Ldebug_abbrev0	@ Offset Into Abbrev. Section
> +	.byte	0x4	@ Pointer Size (in bytes)
> +	.uleb128 0x1	@ (DIE (0xb) DW_TAG_compile_unit)
> +	.4byte	.LASF4	@ DW_AT_producer: "GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -fstack-protector-strong"
> +	.byte	0xc	@ DW_AT_language
> +	.4byte	.LASF5	@ DW_AT_name: "record_goto.c"
> +	.4byte	.LASF6	@ DW_AT_comp_dir: "/home/ubuntu/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
> +	.4byte	.Ltext0	@ DW_AT_low_pc
> +	.4byte	.Letext0-.Ltext0	@ DW_AT_high_pc
> +	.4byte	.Ldebug_line0	@ DW_AT_stmt_list
> +	.uleb128 0x2	@ (DIE (0x25) DW_TAG_subprogram)
> +			@ DW_AT_external
> +	.4byte	.LASF7	@ DW_AT_name: "main"
> +	.byte	0x1	@ DW_AT_decl_file (record_goto.c)
> +	.byte	0x2f	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	0x3a	@ DW_AT_type
> +	.4byte	.LFB4	@ DW_AT_low_pc
> +	.4byte	.LFE4-.LFB4	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_tail_call_sites
> +	.uleb128 0x3	@ (DIE (0x3a) DW_TAG_base_type)
> +	.byte	0x4	@ DW_AT_byte_size
> +	.byte	0x5	@ DW_AT_encoding
> +	.ascii "int\0"	@ DW_AT_name
> +	.uleb128 0x4	@ (DIE (0x41) DW_TAG_subprogram)
> +			@ DW_AT_external
> +	.4byte	.LASF0	@ DW_AT_name: "fun4"
> +	.byte	0x1	@ DW_AT_decl_file (record_goto.c)
> +	.byte	0x27	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	.LFB3	@ DW_AT_low_pc
> +	.4byte	.LFE3-.LFB3	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_tail_call_sites
> +	.uleb128 0x4	@ (DIE (0x52) DW_TAG_subprogram)
> +			@ DW_AT_external
> +	.4byte	.LASF1	@ DW_AT_name: "fun3"
> +	.byte	0x1	@ DW_AT_decl_file (record_goto.c)
> +	.byte	0x20	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	.LFB2	@ DW_AT_low_pc
> +	.4byte	.LFE2-.LFB2	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_tail_call_sites
> +	.uleb128 0x4	@ (DIE (0x63) DW_TAG_subprogram)
> +			@ DW_AT_external
> +	.4byte	.LASF2	@ DW_AT_name: "fun2"
> +	.byte	0x1	@ DW_AT_decl_file (record_goto.c)
> +	.byte	0x1a	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	.LFB1	@ DW_AT_low_pc
> +	.4byte	.LFE1-.LFB1	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_tail_call_sites
> +	.uleb128 0x5	@ (DIE (0x74) DW_TAG_subprogram)
> +			@ DW_AT_external
> +	.4byte	.LASF3	@ DW_AT_name: "fun1"
> +	.byte	0x1	@ DW_AT_decl_file (record_goto.c)
> +	.byte	0x15	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	.LFB0	@ DW_AT_low_pc
> +	.4byte	.LFE0-.LFB0	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_call_sites
> +	.byte	0	@ end of children of DIE 0xb
> +	.section	.debug_abbrev,"",%progbits
> +.Ldebug_abbrev0:
> +	.uleb128 0x1	@ (abbrev code)
> +	.uleb128 0x11	@ (TAG: DW_TAG_compile_unit)
> +	.byte	0x1	@ DW_children_yes
> +	.uleb128 0x25	@ (DW_AT_producer)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x13	@ (DW_AT_language)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x1b	@ (DW_AT_comp_dir)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x12	@ (DW_AT_high_pc)
> +	.uleb128 0x6	@ (DW_FORM_data4)
> +	.uleb128 0x10	@ (DW_AT_stmt_list)
> +	.uleb128 0x17	@ (DW_FORM_sec_offset)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x2	@ (abbrev code)
> +	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0x3f	@ (DW_AT_external)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x27	@ (DW_AT_prototyped)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x49	@ (DW_AT_type)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x12	@ (DW_AT_high_pc)
> +	.uleb128 0x6	@ (DW_FORM_data4)
> +	.uleb128 0x40	@ (DW_AT_frame_base)
> +	.uleb128 0x18	@ (DW_FORM_exprloc)
> +	.uleb128 0x2116	@ (DW_AT_GNU_all_tail_call_sites)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x3	@ (abbrev code)
> +	.uleb128 0x24	@ (TAG: DW_TAG_base_type)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0xb	@ (DW_AT_byte_size)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3e	@ (DW_AT_encoding)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0x8	@ (DW_FORM_string)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x4	@ (abbrev code)
> +	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0x3f	@ (DW_AT_external)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x27	@ (DW_AT_prototyped)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x12	@ (DW_AT_high_pc)
> +	.uleb128 0x6	@ (DW_FORM_data4)
> +	.uleb128 0x40	@ (DW_AT_frame_base)
> +	.uleb128 0x18	@ (DW_FORM_exprloc)
> +	.uleb128 0x2116	@ (DW_AT_GNU_all_tail_call_sites)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x5	@ (abbrev code)
> +	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0x3f	@ (DW_AT_external)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x27	@ (DW_AT_prototyped)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x12	@ (DW_AT_high_pc)
> +	.uleb128 0x6	@ (DW_FORM_data4)
> +	.uleb128 0x40	@ (DW_AT_frame_base)
> +	.uleb128 0x18	@ (DW_FORM_exprloc)
> +	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.byte	0
> +	.byte	0
> +	.byte	0
> +	.section	.debug_aranges,"",%progbits
> +	.4byte	0x1c	@ Length of Address Ranges Info
> +	.2byte	0x2	@ DWARF Version
> +	.4byte	.Ldebug_info0	@ Offset of Compilation Unit Info
> +	.byte	0x4	@ Size of Address
> +	.byte	0	@ Size of Segment Descriptor
> +	.2byte	0	@ Pad to 8 byte boundary
> +	.2byte	0
> +	.4byte	.Ltext0	@ Address
> +	.4byte	.Letext0-.Ltext0	@ Length
> +	.4byte	0
> +	.4byte	0
> +	.section	.debug_line,"",%progbits
> +.Ldebug_line0:
> +	.section	.debug_str,"MS",%progbits,1
> +.LASF5:
> +	.ascii	"record_goto.c\000"
> +.LASF2:
> +	.ascii	"fun2\000"
> +.LASF6:
> +	.ascii	"/home/ubuntu/development/gdb/binutils-gdb/gdb/tests"
> +	.ascii	"uite/gdb.btrace\000"
> +.LASF4:
> +	.ascii	"GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu"
> +	.ascii	"=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -fstack-pro"
> +	.ascii	"tector-strong\000"
> +.LASF0:
> +	.ascii	"fun4\000"
> +.LASF3:
> +	.ascii	"fun1\000"
> +.LASF7:
> +	.ascii	"main\000"
> +.LASF1:
> +	.ascii	"fun3\000"
> +	.ident	"GCC: (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1) 7.4.0"
> +	.section	.note.GNU-stack,"",%progbits
> diff --git a/gdb/testsuite/gdb.btrace/arm-tailcall-only.S b/gdb/testsuite/gdb.btrace/arm-tailcall-only.S
> new file mode 100644
> index 00000000000..73a89c6e533
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/arm-tailcall-only.S
> @@ -0,0 +1,503 @@
> +/* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2016-2020 Free Software Foundation, Inc.
> +
> +   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 3 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/>.
> +
> +   This file has been generated on an armv7 machine using:
> +   gcc -S -O2 -dA -g tailcall-only.c -o arm-tailcall-only.S  */
> +
> +	.eabi_attribute 28, 1	@ Tag_ABI_VFP_args
> +	.eabi_attribute 20, 1	@ Tag_ABI_FP_denormal
> +	.eabi_attribute 21, 1	@ Tag_ABI_FP_exceptions
> +	.eabi_attribute 23, 3	@ Tag_ABI_FP_number_model
> +	.eabi_attribute 24, 1	@ Tag_ABI_align8_needed
> +	.eabi_attribute 25, 1	@ Tag_ABI_align8_preserved
> +	.eabi_attribute 26, 2	@ Tag_ABI_enum_size
> +	.eabi_attribute 30, 2	@ Tag_ABI_optimization_goals
> +	.eabi_attribute 34, 1	@ Tag_CPU_unaligned_access
> +	.eabi_attribute 18, 4	@ Tag_ABI_PCS_wchar_t
> +	.file	"tailcall-only.c"
> +	.text
> +.Ltext0:
> +	.cfi_sections	.debug_frame
> +	.align	1
> +	.p2align 2,,3
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	bar_1, %function
> +bar_1:
> +.LFB0:
> +	.file 1 "tailcall-only.c"
> +	@ tailcall-only.c:22
> +	.loc 1 22 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 0, uses_anonymous_args = 0
> +	@ link register save eliminated.
> +@ BLOCK 2 freq:10000 seq:0
> +@ PRED: ENTRY [100.0%]  (FALLTHRU)
> +	@ tailcall-only.c:24
> +	.loc 1 24 0
> +	movs	r0, #42
> +@ SUCC: EXIT [100.0%] 
> +	bx	lr
> +	.cfi_endproc
> +.LFE0:
> +	.size	bar_1, .-bar_1
> +	.align	1
> +	.p2align 2,,3
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	bar, %function
> +bar:
> +.LFB1:
> +	@ tailcall-only.c:28
> +	.loc 1 28 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 0, uses_anonymous_args = 0
> +	@ link register save eliminated.
> +@ BLOCK 2 freq:10000 seq:0
> +@ PRED: ENTRY [100.0%]  (FALLTHRU)
> +@ SUCC: EXIT [100.0%]  (ABNORMAL,SIBCALL)
> +	@ tailcall-only.c:29
> +	.loc 1 29 0
> +	b	bar_1(PLT)
> +.LVL0:
> +	.cfi_endproc
> +.LFE1:
> +	.size	bar, .-bar
> +	.align	1
> +	.p2align 2,,3
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	foo_1, %function
> +foo_1:
> +.LFB2:
> +	@ tailcall-only.c:34
> +	.loc 1 34 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 0, uses_anonymous_args = 0
> +	@ link register save eliminated.
> +@ BLOCK 2 freq:10000 seq:0
> +@ PRED: ENTRY [100.0%]  (FALLTHRU)
> +@ SUCC: EXIT [100.0%]  (ABNORMAL,SIBCALL)
> +	@ tailcall-only.c:35
> +	.loc 1 35 0
> +	b	bar(PLT)
> +.LVL1:
> +	.cfi_endproc
> +.LFE2:
> +	.size	foo_1, .-foo_1
> +	.align	1
> +	.p2align 2,,3
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	foo, %function
> +foo:
> +.LFB3:
> +	@ tailcall-only.c:40
> +	.loc 1 40 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 0, uses_anonymous_args = 0
> +	@ link register save eliminated.
> +@ BLOCK 2 freq:10000 seq:0
> +@ PRED: ENTRY [100.0%]  (FALLTHRU)
> +@ SUCC: EXIT [100.0%]  (ABNORMAL,SIBCALL)
> +	@ tailcall-only.c:41
> +	.loc 1 41 0
> +	b	foo_1(PLT)
> +.LVL2:
> +	.cfi_endproc
> +.LFE3:
> +	.size	foo, .-foo
> +	.section	.text.startup,"ax",%progbits
> +	.align	1
> +	.p2align 2,,3
> +	.global	main
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	main, %function
> +main:
> +.LFB4:
> +	@ tailcall-only.c:46
> +	.loc 1 46 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 0, uses_anonymous_args = 0
> +@ BLOCK 2 freq:10000 seq:0
> +@ PRED: ENTRY [100.0%]  (FALLTHRU)
> +	push	{r3, lr}
> +	.cfi_def_cfa_offset 8
> +	.cfi_offset 3, -8
> +	.cfi_offset 14, -4
> +	@ tailcall-only.c:49
> +	.loc 1 49 0
> +	bl	foo(PLT)
> +.LVL3:
> +	@ tailcall-only.c:53
> +	.loc 1 53 0
> +	adds	r0, r0, #1
> +.LVL4:
> +@ SUCC: EXIT [100.0%] 
> +	pop	{r3, pc}
> +	.cfi_endproc
> +.LFE4:
> +	.size	main, .-main
> +	.text
> +.Letext0:
> +	.section	.debug_info,"",%progbits
> +.Ldebug_info0:
> +	.4byte	0xd9	@ Length of Compilation Unit Info
> +	.2byte	0x4	@ DWARF version number
> +	.4byte	.Ldebug_abbrev0	@ Offset Into Abbrev. Section
> +	.byte	0x4	@ Pointer Size (in bytes)
> +	.uleb128 0x1	@ (DIE (0xb) DW_TAG_compile_unit)
> +	.4byte	.LASF1	@ DW_AT_producer: "GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -O2 -fstack-protector-strong"
> +	.byte	0xc	@ DW_AT_language
> +	.4byte	.LASF2	@ DW_AT_name: "tailcall-only.c"
> +	.4byte	.LASF3	@ DW_AT_comp_dir: "/home/ubuntu/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
> +	.4byte	.Ldebug_ranges0+0	@ DW_AT_ranges
> +	.4byte	0	@ DW_AT_low_pc
> +	.4byte	.Ldebug_line0	@ DW_AT_stmt_list
> +	.uleb128 0x2	@ (DIE (0x25) DW_TAG_subprogram)
> +			@ DW_AT_external
> +	.4byte	.LASF4	@ DW_AT_name: "main"
> +	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x2d	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	0x57	@ DW_AT_type
> +	.4byte	.LFB4	@ DW_AT_low_pc
> +	.4byte	.LFE4-.LFB4	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_call_sites
> +	.4byte	0x57	@ DW_AT_sibling
> +	.uleb128 0x3	@ (DIE (0x3e) DW_TAG_variable)
> +	.4byte	.LASF5	@ DW_AT_name: "answer"
> +	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x2f	@ DW_AT_decl_line
> +	.4byte	0x57	@ DW_AT_type
> +	.4byte	.LLST0	@ DW_AT_location
> +	.uleb128 0x4	@ (DIE (0x4d) DW_TAG_GNU_call_site)
> +	.4byte	.LVL3	@ DW_AT_low_pc
> +	.4byte	0x5e	@ DW_AT_abstract_origin
> +	.byte	0	@ end of children of DIE 0x25
> +	.uleb128 0x5	@ (DIE (0x57) DW_TAG_base_type)
> +	.byte	0x4	@ DW_AT_byte_size
> +	.byte	0x5	@ DW_AT_encoding
> +	.ascii "int\0"	@ DW_AT_name
> +	.uleb128 0x6	@ (DIE (0x5e) DW_TAG_subprogram)
> +	.ascii "foo\0"	@ DW_AT_name
> +	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x27	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	0x57	@ DW_AT_type
> +	.4byte	.LFB3	@ DW_AT_low_pc
> +	.4byte	.LFE3-.LFB3	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_call_sites
> +	.4byte	0x81	@ DW_AT_sibling
> +	.uleb128 0x7	@ (DIE (0x77) DW_TAG_GNU_call_site)
> +	.4byte	.LVL2	@ DW_AT_low_pc
> +			@ DW_AT_GNU_tail_call
> +	.4byte	0x81	@ DW_AT_abstract_origin
> +	.byte	0	@ end of children of DIE 0x5e
> +	.uleb128 0x8	@ (DIE (0x81) DW_TAG_subprogram)
> +	.4byte	.LASF0	@ DW_AT_name: "foo_1"
> +	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x21	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	0x57	@ DW_AT_type
> +	.4byte	.LFB2	@ DW_AT_low_pc
> +	.4byte	.LFE2-.LFB2	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_call_sites
> +	.4byte	0xa4	@ DW_AT_sibling
> +	.uleb128 0x7	@ (DIE (0x9a) DW_TAG_GNU_call_site)
> +	.4byte	.LVL1	@ DW_AT_low_pc
> +			@ DW_AT_GNU_tail_call
> +	.4byte	0xa4	@ DW_AT_abstract_origin
> +	.byte	0	@ end of children of DIE 0x81
> +	.uleb128 0x6	@ (DIE (0xa4) DW_TAG_subprogram)
> +	.ascii "bar\0"	@ DW_AT_name
> +	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x1b	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	0x57	@ DW_AT_type
> +	.4byte	.LFB1	@ DW_AT_low_pc
> +	.4byte	.LFE1-.LFB1	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_call_sites
> +	.4byte	0xc7	@ DW_AT_sibling
> +	.uleb128 0x7	@ (DIE (0xbd) DW_TAG_GNU_call_site)
> +	.4byte	.LVL0	@ DW_AT_low_pc
> +			@ DW_AT_GNU_tail_call
> +	.4byte	0xc7	@ DW_AT_abstract_origin
> +	.byte	0	@ end of children of DIE 0xa4
> +	.uleb128 0x9	@ (DIE (0xc7) DW_TAG_subprogram)
> +	.4byte	.LASF6	@ DW_AT_name: "bar_1"
> +	.byte	0x1	@ DW_AT_decl_file (tailcall-only.c)
> +	.byte	0x15	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	0x57	@ DW_AT_type
> +	.4byte	.LFB0	@ DW_AT_low_pc
> +	.4byte	.LFE0-.LFB0	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_call_sites
> +	.byte	0	@ end of children of DIE 0xb
> +	.section	.debug_abbrev,"",%progbits
> +.Ldebug_abbrev0:
> +	.uleb128 0x1	@ (abbrev code)
> +	.uleb128 0x11	@ (TAG: DW_TAG_compile_unit)
> +	.byte	0x1	@ DW_children_yes
> +	.uleb128 0x25	@ (DW_AT_producer)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x13	@ (DW_AT_language)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x1b	@ (DW_AT_comp_dir)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x55	@ (DW_AT_ranges)
> +	.uleb128 0x17	@ (DW_FORM_sec_offset)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x10	@ (DW_AT_stmt_list)
> +	.uleb128 0x17	@ (DW_FORM_sec_offset)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x2	@ (abbrev code)
> +	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
> +	.byte	0x1	@ DW_children_yes
> +	.uleb128 0x3f	@ (DW_AT_external)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x27	@ (DW_AT_prototyped)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x49	@ (DW_AT_type)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x12	@ (DW_AT_high_pc)
> +	.uleb128 0x6	@ (DW_FORM_data4)
> +	.uleb128 0x40	@ (DW_AT_frame_base)
> +	.uleb128 0x18	@ (DW_FORM_exprloc)
> +	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x1	@ (DW_AT_sibling)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x3	@ (abbrev code)
> +	.uleb128 0x34	@ (TAG: DW_TAG_variable)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x49	@ (DW_AT_type)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.uleb128 0x2	@ (DW_AT_location)
> +	.uleb128 0x17	@ (DW_FORM_sec_offset)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x4	@ (abbrev code)
> +	.uleb128 0x4109	@ (TAG: DW_TAG_GNU_call_site)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x31	@ (DW_AT_abstract_origin)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x5	@ (abbrev code)
> +	.uleb128 0x24	@ (TAG: DW_TAG_base_type)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0xb	@ (DW_AT_byte_size)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3e	@ (DW_AT_encoding)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0x8	@ (DW_FORM_string)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x6	@ (abbrev code)
> +	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
> +	.byte	0x1	@ DW_children_yes
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0x8	@ (DW_FORM_string)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x27	@ (DW_AT_prototyped)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x49	@ (DW_AT_type)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x12	@ (DW_AT_high_pc)
> +	.uleb128 0x6	@ (DW_FORM_data4)
> +	.uleb128 0x40	@ (DW_AT_frame_base)
> +	.uleb128 0x18	@ (DW_FORM_exprloc)
> +	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x1	@ (DW_AT_sibling)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x7	@ (abbrev code)
> +	.uleb128 0x4109	@ (TAG: DW_TAG_GNU_call_site)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x2115	@ (DW_AT_GNU_tail_call)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x31	@ (DW_AT_abstract_origin)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x8	@ (abbrev code)
> +	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
> +	.byte	0x1	@ DW_children_yes
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x27	@ (DW_AT_prototyped)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x49	@ (DW_AT_type)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x12	@ (DW_AT_high_pc)
> +	.uleb128 0x6	@ (DW_FORM_data4)
> +	.uleb128 0x40	@ (DW_AT_frame_base)
> +	.uleb128 0x18	@ (DW_FORM_exprloc)
> +	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x1	@ (DW_AT_sibling)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x9	@ (abbrev code)
> +	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x27	@ (DW_AT_prototyped)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x49	@ (DW_AT_type)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x12	@ (DW_AT_high_pc)
> +	.uleb128 0x6	@ (DW_FORM_data4)
> +	.uleb128 0x40	@ (DW_AT_frame_base)
> +	.uleb128 0x18	@ (DW_FORM_exprloc)
> +	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.byte	0
> +	.byte	0
> +	.byte	0
> +	.section	.debug_loc,"",%progbits
> +.Ldebug_loc0:
> +.LLST0:
> +	.4byte	.LVL3	@ Location list begin address (*.LLST0)
> +	.4byte	.LVL4	@ Location list end address (*.LLST0)
> +	.2byte	0x3	@ Location expression size
> +	.byte	0x70	@ DW_OP_breg0
> +	.sleb128 1
> +	.byte	0x9f	@ DW_OP_stack_value
> +	.4byte	.LVL4	@ Location list begin address (*.LLST0)
> +	.4byte	.LFE4	@ Location list end address (*.LLST0)
> +	.2byte	0x1	@ Location expression size
> +	.byte	0x50	@ DW_OP_reg0
> +	.4byte	0	@ Location list terminator begin (*.LLST0)
> +	.4byte	0	@ Location list terminator end (*.LLST0)
> +	.section	.debug_aranges,"",%progbits
> +	.4byte	0x24	@ Length of Address Ranges Info
> +	.2byte	0x2	@ DWARF Version
> +	.4byte	.Ldebug_info0	@ Offset of Compilation Unit Info
> +	.byte	0x4	@ Size of Address
> +	.byte	0	@ Size of Segment Descriptor
> +	.2byte	0	@ Pad to 8 byte boundary
> +	.2byte	0
> +	.4byte	.Ltext0	@ Address
> +	.4byte	.Letext0-.Ltext0	@ Length
> +	.4byte	.LFB4	@ Address
> +	.4byte	.LFE4-.LFB4	@ Length
> +	.4byte	0
> +	.4byte	0
> +	.section	.debug_ranges,"",%progbits
> +.Ldebug_ranges0:
> +	.4byte	.Ltext0	@ Offset 0
> +	.4byte	.Letext0
> +	.4byte	.LFB4	@ Offset 0x8
> +	.4byte	.LFE4
> +	.4byte	0
> +	.4byte	0
> +	.section	.debug_line,"",%progbits
> +.Ldebug_line0:
> +	.section	.debug_str,"MS",%progbits,1
> +.LASF2:
> +	.ascii	"tailcall-only.c\000"
> +.LASF5:
> +	.ascii	"answer\000"
> +.LASF1:
> +	.ascii	"GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu"
> +	.ascii	"=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -O2 -fstack"
> +	.ascii	"-protector-strong\000"
> +.LASF4:
> +	.ascii	"main\000"
> +.LASF6:
> +	.ascii	"bar_1\000"
> +.LASF3:
> +	.ascii	"/home/ubuntu/development/gdb/binutils-gdb/gdb/tests"
> +	.ascii	"uite/gdb.btrace\000"
> +.LASF0:
> +	.ascii	"foo_1\000"
> +	.ident	"GCC: (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1) 7.4.0"
> +	.section	.note.GNU-stack,"",%progbits
> diff --git a/gdb/testsuite/gdb.btrace/arm-tailcall.S b/gdb/testsuite/gdb.btrace/arm-tailcall.S
> new file mode 100644
> index 00000000000..2f72dd12296
> --- /dev/null
> +++ b/gdb/testsuite/gdb.btrace/arm-tailcall.S
> @@ -0,0 +1,390 @@
> + /* This testcase is part of GDB, the GNU debugger.
> +
> +   Copyright 2021 Free Software Foundation, Inc.
> +
> +   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 3 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/>.
> +
> +   This file has been generated on an armv7 machine using:
> +   gcc -S -O2 -dA -g tailcall.c -o arm-tailcall.S  */
> +
> +	.eabi_attribute 28, 1	@ Tag_ABI_VFP_args
> +	.eabi_attribute 20, 1	@ Tag_ABI_FP_denormal
> +	.eabi_attribute 21, 1	@ Tag_ABI_FP_exceptions
> +	.eabi_attribute 23, 3	@ Tag_ABI_FP_number_model
> +	.eabi_attribute 24, 1	@ Tag_ABI_align8_needed
> +	.eabi_attribute 25, 1	@ Tag_ABI_align8_preserved
> +	.eabi_attribute 26, 2	@ Tag_ABI_enum_size
> +	.eabi_attribute 30, 2	@ Tag_ABI_optimization_goals
> +	.eabi_attribute 34, 1	@ Tag_CPU_unaligned_access
> +	.eabi_attribute 18, 4	@ Tag_ABI_PCS_wchar_t
> +	.file	"tailcall.c"
> +	.text
> +.Ltext0:
> +	.cfi_sections	.debug_frame
> +	.align	1
> +	.p2align 2,,3
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	bar, %function
> +bar:
> +.LFB0:
> +	.file 1 "tailcall.c"
> +	@ tailcall.c:22
> +	.loc 1 22 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 0, uses_anonymous_args = 0
> +	@ link register save eliminated.
> +@ BLOCK 2 freq:10000 seq:0
> +@ PRED: ENTRY [100.0%]  (FALLTHRU)
> +	@ tailcall.c:24
> +	.loc 1 24 0
> +	movs	r0, #42
> +@ SUCC: EXIT [100.0%] 
> +	bx	lr
> +	.cfi_endproc
> +.LFE0:
> +	.size	bar, .-bar
> +	.align	1
> +	.p2align 2,,3
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	foo, %function
> +foo:
> +.LFB1:
> +	@ tailcall.c:28
> +	.loc 1 28 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 0, uses_anonymous_args = 0
> +	@ link register save eliminated.
> +@ BLOCK 2 freq:10000 seq:0
> +@ PRED: ENTRY [100.0%]  (FALLTHRU)
> +@ SUCC: EXIT [100.0%]  (ABNORMAL,SIBCALL)
> +	@ tailcall.c:29
> +	.loc 1 29 0
> +	b	bar(PLT)
> +.LVL0:
> +	.cfi_endproc
> +.LFE1:
> +	.size	foo, .-foo
> +	.section	.text.startup,"ax",%progbits
> +	.align	1
> +	.p2align 2,,3
> +	.global	main
> +	.syntax unified
> +	.thumb
> +	.thumb_func
> +	.fpu vfpv3-d16
> +	.type	main, %function
> +main:
> +.LFB2:
> +	@ tailcall.c:34
> +	.loc 1 34 0
> +	.cfi_startproc
> +	@ args = 0, pretend = 0, frame = 0
> +	@ frame_needed = 0, uses_anonymous_args = 0
> +@ BLOCK 2 freq:10000 seq:0
> +@ PRED: ENTRY [100.0%]  (FALLTHRU)
> +	push	{r3, lr}
> +	.cfi_def_cfa_offset 8
> +	.cfi_offset 3, -8
> +	.cfi_offset 14, -4
> +	@ tailcall.c:37
> +	.loc 1 37 0
> +	bl	foo(PLT)
> +.LVL1:
> +	@ tailcall.c:41
> +	.loc 1 41 0
> +	adds	r0, r0, #1
> +.LVL2:
> +@ SUCC: EXIT [100.0%] 
> +	pop	{r3, pc}
> +	.cfi_endproc
> +.LFE2:
> +	.size	main, .-main
> +	.text
> +.Letext0:
> +	.section	.debug_info,"",%progbits
> +.Ldebug_info0:
> +	.4byte	0x93	@ Length of Compilation Unit Info
> +	.2byte	0x4	@ DWARF version number
> +	.4byte	.Ldebug_abbrev0	@ Offset Into Abbrev. Section
> +	.byte	0x4	@ Pointer Size (in bytes)
> +	.uleb128 0x1	@ (DIE (0xb) DW_TAG_compile_unit)
> +	.4byte	.LASF0	@ DW_AT_producer: "GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -O2 -fstack-protector-strong"
> +	.byte	0xc	@ DW_AT_language
> +	.4byte	.LASF1	@ DW_AT_name: "tailcall.c"
> +	.4byte	.LASF2	@ DW_AT_comp_dir: "/home/ubuntu/development/gdb/binutils-gdb/gdb/testsuite/gdb.btrace"
> +	.4byte	.Ldebug_ranges0+0	@ DW_AT_ranges
> +	.4byte	0	@ DW_AT_low_pc
> +	.4byte	.Ldebug_line0	@ DW_AT_stmt_list
> +	.uleb128 0x2	@ (DIE (0x25) DW_TAG_subprogram)
> +			@ DW_AT_external
> +	.4byte	.LASF3	@ DW_AT_name: "main"
> +	.byte	0x1	@ DW_AT_decl_file (tailcall.c)
> +	.byte	0x21	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	0x57	@ DW_AT_type
> +	.4byte	.LFB2	@ DW_AT_low_pc
> +	.4byte	.LFE2-.LFB2	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_call_sites
> +	.4byte	0x57	@ DW_AT_sibling
> +	.uleb128 0x3	@ (DIE (0x3e) DW_TAG_variable)
> +	.4byte	.LASF4	@ DW_AT_name: "answer"
> +	.byte	0x1	@ DW_AT_decl_file (tailcall.c)
> +	.byte	0x23	@ DW_AT_decl_line
> +	.4byte	0x57	@ DW_AT_type
> +	.4byte	.LLST0	@ DW_AT_location
> +	.uleb128 0x4	@ (DIE (0x4d) DW_TAG_GNU_call_site)
> +	.4byte	.LVL1	@ DW_AT_low_pc
> +	.4byte	0x5e	@ DW_AT_abstract_origin
> +	.byte	0	@ end of children of DIE 0x25
> +	.uleb128 0x5	@ (DIE (0x57) DW_TAG_base_type)
> +	.byte	0x4	@ DW_AT_byte_size
> +	.byte	0x5	@ DW_AT_encoding
> +	.ascii "int\0"	@ DW_AT_name
> +	.uleb128 0x6	@ (DIE (0x5e) DW_TAG_subprogram)
> +	.ascii "foo\0"	@ DW_AT_name
> +	.byte	0x1	@ DW_AT_decl_file (tailcall.c)
> +	.byte	0x1b	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	0x57	@ DW_AT_type
> +	.4byte	.LFB1	@ DW_AT_low_pc
> +	.4byte	.LFE1-.LFB1	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_call_sites
> +	.4byte	0x81	@ DW_AT_sibling
> +	.uleb128 0x7	@ (DIE (0x77) DW_TAG_GNU_call_site)
> +	.4byte	.LVL0	@ DW_AT_low_pc
> +			@ DW_AT_GNU_tail_call
> +	.4byte	0x81	@ DW_AT_abstract_origin
> +	.byte	0	@ end of children of DIE 0x5e
> +	.uleb128 0x8	@ (DIE (0x81) DW_TAG_subprogram)
> +	.ascii "bar\0"	@ DW_AT_name
> +	.byte	0x1	@ DW_AT_decl_file (tailcall.c)
> +	.byte	0x15	@ DW_AT_decl_line
> +			@ DW_AT_prototyped
> +	.4byte	0x57	@ DW_AT_type
> +	.4byte	.LFB0	@ DW_AT_low_pc
> +	.4byte	.LFE0-.LFB0	@ DW_AT_high_pc
> +	.uleb128 0x1	@ DW_AT_frame_base
> +	.byte	0x9c	@ DW_OP_call_frame_cfa
> +			@ DW_AT_GNU_all_call_sites
> +	.byte	0	@ end of children of DIE 0xb
> +	.section	.debug_abbrev,"",%progbits
> +.Ldebug_abbrev0:
> +	.uleb128 0x1	@ (abbrev code)
> +	.uleb128 0x11	@ (TAG: DW_TAG_compile_unit)
> +	.byte	0x1	@ DW_children_yes
> +	.uleb128 0x25	@ (DW_AT_producer)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x13	@ (DW_AT_language)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x1b	@ (DW_AT_comp_dir)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x55	@ (DW_AT_ranges)
> +	.uleb128 0x17	@ (DW_FORM_sec_offset)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x10	@ (DW_AT_stmt_list)
> +	.uleb128 0x17	@ (DW_FORM_sec_offset)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x2	@ (abbrev code)
> +	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
> +	.byte	0x1	@ DW_children_yes
> +	.uleb128 0x3f	@ (DW_AT_external)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x27	@ (DW_AT_prototyped)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x49	@ (DW_AT_type)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x12	@ (DW_AT_high_pc)
> +	.uleb128 0x6	@ (DW_FORM_data4)
> +	.uleb128 0x40	@ (DW_AT_frame_base)
> +	.uleb128 0x18	@ (DW_FORM_exprloc)
> +	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x1	@ (DW_AT_sibling)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x3	@ (abbrev code)
> +	.uleb128 0x34	@ (TAG: DW_TAG_variable)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0xe	@ (DW_FORM_strp)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x49	@ (DW_AT_type)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.uleb128 0x2	@ (DW_AT_location)
> +	.uleb128 0x17	@ (DW_FORM_sec_offset)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x4	@ (abbrev code)
> +	.uleb128 0x4109	@ (TAG: DW_TAG_GNU_call_site)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x31	@ (DW_AT_abstract_origin)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x5	@ (abbrev code)
> +	.uleb128 0x24	@ (TAG: DW_TAG_base_type)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0xb	@ (DW_AT_byte_size)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3e	@ (DW_AT_encoding)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0x8	@ (DW_FORM_string)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x6	@ (abbrev code)
> +	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
> +	.byte	0x1	@ DW_children_yes
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0x8	@ (DW_FORM_string)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x27	@ (DW_AT_prototyped)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x49	@ (DW_AT_type)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x12	@ (DW_AT_high_pc)
> +	.uleb128 0x6	@ (DW_FORM_data4)
> +	.uleb128 0x40	@ (DW_AT_frame_base)
> +	.uleb128 0x18	@ (DW_FORM_exprloc)
> +	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x1	@ (DW_AT_sibling)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x7	@ (abbrev code)
> +	.uleb128 0x4109	@ (TAG: DW_TAG_GNU_call_site)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x2115	@ (DW_AT_GNU_tail_call)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x31	@ (DW_AT_abstract_origin)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.byte	0
> +	.byte	0
> +	.uleb128 0x8	@ (abbrev code)
> +	.uleb128 0x2e	@ (TAG: DW_TAG_subprogram)
> +	.byte	0	@ DW_children_no
> +	.uleb128 0x3	@ (DW_AT_name)
> +	.uleb128 0x8	@ (DW_FORM_string)
> +	.uleb128 0x3a	@ (DW_AT_decl_file)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x3b	@ (DW_AT_decl_line)
> +	.uleb128 0xb	@ (DW_FORM_data1)
> +	.uleb128 0x27	@ (DW_AT_prototyped)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.uleb128 0x49	@ (DW_AT_type)
> +	.uleb128 0x13	@ (DW_FORM_ref4)
> +	.uleb128 0x11	@ (DW_AT_low_pc)
> +	.uleb128 0x1	@ (DW_FORM_addr)
> +	.uleb128 0x12	@ (DW_AT_high_pc)
> +	.uleb128 0x6	@ (DW_FORM_data4)
> +	.uleb128 0x40	@ (DW_AT_frame_base)
> +	.uleb128 0x18	@ (DW_FORM_exprloc)
> +	.uleb128 0x2117	@ (DW_AT_GNU_all_call_sites)
> +	.uleb128 0x19	@ (DW_FORM_flag_present)
> +	.byte	0
> +	.byte	0
> +	.byte	0
> +	.section	.debug_loc,"",%progbits
> +.Ldebug_loc0:
> +.LLST0:
> +	.4byte	.LVL1	@ Location list begin address (*.LLST0)
> +	.4byte	.LVL2	@ Location list end address (*.LLST0)
> +	.2byte	0x3	@ Location expression size
> +	.byte	0x70	@ DW_OP_breg0
> +	.sleb128 1
> +	.byte	0x9f	@ DW_OP_stack_value
> +	.4byte	.LVL2	@ Location list begin address (*.LLST0)
> +	.4byte	.LFE2	@ Location list end address (*.LLST0)
> +	.2byte	0x1	@ Location expression size
> +	.byte	0x50	@ DW_OP_reg0
> +	.4byte	0	@ Location list terminator begin (*.LLST0)
> +	.4byte	0	@ Location list terminator end (*.LLST0)
> +	.section	.debug_aranges,"",%progbits
> +	.4byte	0x24	@ Length of Address Ranges Info
> +	.2byte	0x2	@ DWARF Version
> +	.4byte	.Ldebug_info0	@ Offset of Compilation Unit Info
> +	.byte	0x4	@ Size of Address
> +	.byte	0	@ Size of Segment Descriptor
> +	.2byte	0	@ Pad to 8 byte boundary
> +	.2byte	0
> +	.4byte	.Ltext0	@ Address
> +	.4byte	.Letext0-.Ltext0	@ Length
> +	.4byte	.LFB2	@ Address
> +	.4byte	.LFE2-.LFB2	@ Length
> +	.4byte	0
> +	.4byte	0
> +	.section	.debug_ranges,"",%progbits
> +.Ldebug_ranges0:
> +	.4byte	.Ltext0	@ Offset 0
> +	.4byte	.Letext0
> +	.4byte	.LFB2	@ Offset 0x8
> +	.4byte	.LFE2
> +	.4byte	0
> +	.4byte	0
> +	.section	.debug_line,"",%progbits
> +.Ldebug_line0:
> +	.section	.debug_str,"MS",%progbits,1
> +.LASF0:
> +	.ascii	"GNU C11 7.4.0 -march=armv7-a -mfloat-abi=hard -mfpu"
> +	.ascii	"=vfpv3-d16 -mthumb -mtls-dialect=gnu -g -O2 -fstack"
> +	.ascii	"-protector-strong\000"
> +.LASF4:
> +	.ascii	"answer\000"
> +.LASF1:
> +	.ascii	"tailcall.c\000"
> +.LASF2:
> +	.ascii	"/home/ubuntu/development/gdb/binutils-gdb/gdb/tests"
> +	.ascii	"uite/gdb.btrace\000"
> +.LASF3:
> +	.ascii	"main\000"
> +	.ident	"GCC: (Ubuntu/Linaro 7.4.0-1ubuntu1~18.04.1) 7.4.0"
> +	.section	.note.GNU-stack,"",%progbits
> diff --git a/gdb/testsuite/gdb.btrace/buffer-size.exp b/gdb/testsuite/gdb.btrace/buffer-size.exp
> index ea4e36c1593..23f859dce9c 100644
> --- a/gdb/testsuite/gdb.btrace/buffer-size.exp
> +++ b/gdb/testsuite/gdb.btrace/buffer-size.exp
> @@ -32,10 +32,17 @@ if ![runto_main] {
>      return -1
>  }
>  
> -gdb_test_no_output "set record btrace bts buffer-size 1"
> -gdb_test_no_output "set record btrace pt buffer-size 1"
> -gdb_test "show record btrace bts buffer-size" "The record/replay bts buffer size is 1\.\r"
> -gdb_test "show record btrace pt buffer-size" "The record/replay pt buffer size is 1\.\r"
> +set btrace_type ""
> +if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +    set btrace_type { "bts" "pt" }
> +} elseif {[istarget "arm*-*-*"]|| [istarget "aarch64*-*-*"]} {
> +    set btrace_type { "etm" } 
> +}
> +
> +foreach_with_prefix format $btrace_type {
> +    gdb_test_no_output "set record btrace $format buffer-size 1"
> +    gdb_test "show record btrace $format buffer-size" "The record/replay $format buffer size is 1\.\r"
> +}
>  
>  gdb_test_no_output "record btrace"
>  gdb_test "info record" [multi_line \
> diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp b/gdb/testsuite/gdb.btrace/instruction_history.exp
> index 403085c083f..3a6e7362f74 100644
> --- a/gdb/testsuite/gdb.btrace/instruction_history.exp
> +++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
> @@ -22,7 +22,17 @@ if { [skip_btrace_tests] } {
>      return -1
>  }
>  
> -standard_testfile .c .S
> +set test_prefix ""
> +if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +    set test_prefix "x86"
> +} elseif {[istarget "arm*-*-*"]} {
> +    set test_prefix "arm"
> +} elseif {[istarget "aarch64*-*-*"]} {
> +    set test_prefix "aarch64"
> +}
> +
> +standard_testfile instruction_history.c $test_prefix-instruction_history.S
> +
>  if [prepare_for_testing "failed to prepare" $testfile "$srcfile $srcfile2" {debug}] {
>      return -1
>  }
> @@ -67,40 +77,108 @@ if { $traced != 11 } {
>      pass $message
>  }
>  
> -# test that we see the expected instructions
> -gdb_test "record instruction-history 3,7" [multi_line \
> +if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +  set test_pattern(0) "[multi_line \
>        "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
>        "4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
>        "5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
>        "6\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
>        "7\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
> -  ]
> -
> -gdb_test "record instruction-history /f 3,+5" [multi_line \
> +    ]"
> +  set test_pattern(1) "[multi_line \
>        "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
>        "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
>        "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
>        "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
>        "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
> -  ]
> -
> -gdb_test "record instruction-history /p 7,-5" [multi_line \
> +      ]"
> +  set test_pattern(2) "[multi_line \
>        "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
>        "4\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tdec    %eax" \
>        "5\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
>        "6\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
>        "7\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
> -  ]
> -
> -gdb_test "record instruction-history /pf 3,7" [multi_line \
> +      ]"
> +  set test_pattern(3) "[multi_line \
>        "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
>        "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tdec    %eax" \
>        "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tjmp    0x\[0-9a-f\]+ <loop\\+\[0-9\]+>" \
>        "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp    \\\$0x0,%eax" \
>        "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r" \
> -  ]
> +      ]"
> +  set test_pattern(4) "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
> +} elseif {[istarget "arm*-*-*"]} {
> +  set test_pattern(0) "[multi_line \
> +      "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>" \
> +      "4\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tsubs\tr0, r0, #1" \
> +      "5\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>" \
> +      "6\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tcmp\tr0, #0" \
> +      "7\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>\r" \
> +      ]"
> +  set test_pattern(1) "[multi_line \
> +      "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>" \
> +      "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tsubs\tr0, r0, #1" \
> +      "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>" \
> +      "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp\tr0, #0" \
> +      "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>\r" \
> +      ]"
> +  set test_pattern(2) "[multi_line \
> +      "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>" \
> +      "4\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tsubs\tr0, r0, #1" \
> +      "5\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>" \
> +      "6\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tcmp\tr0, #0" \
> +      "7\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>\r" \
> +      ]"
> +  set test_pattern(3)  "[multi_line \
> +      "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>" \
> +      "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tsubs\tr0, r0, #1" \
> +      "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>" \
> +      "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp\tr0, #0" \
> +      "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>\r" \
> +      ]"
> +  set test_pattern(4) "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tbeq\t0x\[0-9a-f\]+ <L2>\r"
> +} elseif {[istarget "aarch64*-*-*"]} {
> +  set test_pattern(0) "[multi_line \
> +      "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*" \
> +      "4\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tsubs\tx0, x0, #0x1.*" \
> +      "5\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>.*" \
> +      "6\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tcmp\tx0, #0x0.*" \
> +      "7\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*\r" \
> +    ]"
> +  set test_pattern(1) "[multi_line \
> +      "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*" \
> +      "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tsubs\tx0, x0, #0x1.*" \
> +      "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>.*" \
> +      "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp\tx0, #0x0.*" \
> +      "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*\r" \
> +      ]"
> +  set test_pattern(2) "[multi_line \
> +      "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*" \
> +      "4\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tsubs\tx0, x0, #0x1.*" \
> +      "5\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>.*" \
> +      "6\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tcmp\tx0, #0x0.*" \
> +      "7\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>.*\r" \
> +      ]"
> +  set test_pattern(3) "[multi_line \
> +      "3\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\\.eq\t0x\[0-9a-f\]+ <L2>.*" \
> +      "4\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tsubs\tx0, x0, #0x1.*" \
> +      "5\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\t0x\[0-9a-f\]+ <L1>.*" \
> +      "6\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tcmp\tx0, #0x0.*" \
> +      "7\t   0x\[0-9a-f\]+ <\\+\[0-9\]+>:\tb\\.eq\t0x\[0-9a-f\]+ <L2>.*\r" \
> +      ]"
> +  set test_pattern(4) "3\t   0x\[0-9a-f\]+ <L1\\+\[0-9\]+>:\tb\.eq\t0x\[0-9a-f\]+ <L2>  // b\.none\r"
> +}
> +
> +# test that we see the expected instructions
> +gdb_test "record instruction-history 3,7" $test_pattern(0)
> +
> +gdb_test "record instruction-history /f 3,+5" $test_pattern(1)
> +
> +gdb_test "record instruction-history /p 7,-5" $test_pattern(2)
> +
> +gdb_test "record instruction-history /pf 3,7"  $test_pattern(3)
>  
> -gdb_test "record instruction-history 3,3" "3\t   0x\[0-9a-f\]+ <loop\\+\[0-9\]+>:\tje     0x\[0-9a-f\]+ <loop\\+\[0-9\]+>\r"
> +gdb_test "record instruction-history 3,3" $test_pattern(4)
>  
>  # the following tests are checking the iterators
>  # to avoid lots of regexps, we just check the number of lines that
> diff --git a/gdb/testsuite/gdb.btrace/non-stop.exp b/gdb/testsuite/gdb.btrace/non-stop.exp
> index e509d65d660..fbc4cda7dd6 100644
> --- a/gdb/testsuite/gdb.btrace/non-stop.exp
> +++ b/gdb/testsuite/gdb.btrace/non-stop.exp
> @@ -31,6 +31,12 @@ save_vars { GDBFLAGS } {
>      clean_restart $testfile
>  }
>  
> +if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +    set loop_position 2
> +} elseif {[istarget "arm*-*-*"] || [istarget "aarch64*-*-*"]} {
> +    set loop_position 3
> +}
> +
>  if ![runto_main] {
>      untested "failed to run to main"
>      return -1
> @@ -111,87 +117,99 @@ gdb_test "thread apply all info rec" ".*"
>  gdb_test "info threads" ".*"
>  
>  with_test_prefix "navigate" {
> -    gdb_test "thread apply 1 record goto 3" "$loop_line"
> -    gdb_test "thread apply 2 record goto 4" "$loop_line"
> +    gdb_test "thread apply 1 record goto [expr {$loop_position + 1}]" "$loop_line"
> +    gdb_test "thread apply 2 record goto [expr {$loop_position + 2}]" "$loop_line"
>      gdb_test "thread apply 1 info record" \
> -        ".*Replay in progress\.  At instruction 3\." "thread 1 at insn 3"
> +        ".*Replay in progress\.  At instruction [expr {$loop_position + 1}]\." "thread 1 at insn $loop_position"
>      gdb_test "thread apply 2 info record" \
> -        ".*Replay in progress\.  At instruction 4\." "thread 2 at insn 4"
> +        ".*Replay in progress\.  At instruction [expr {$loop_position + 2}]\." "thread 2 at insn [expr {$loop_position + 2}]"
>  
> -    gdb_test "thread apply all record goto 5" "$loop_line"
> +    gdb_test "thread apply all record goto [expr {$loop_position + 3}]" "$loop_line"
>      gdb_test "thread apply 1 info record" \
> -        ".*Replay in progress\.  At instruction 5\." "thread 1 at insn 5"
> +        ".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\." "thread 1 at insn [expr {$loop_position + 3}]"
>      gdb_test "thread apply 2 info record" \
> -        ".*Replay in progress\.  At instruction 5\." "thread 2 at insn 5"
> +        ".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\." "thread 2 at insn [expr {$loop_position + 3}]"
>  }
>  
>  with_test_prefix "step" {
> +    with_test_prefix "fixture" {
> +	gdb_test "thread apply 1 record goto [expr {$loop_position + 3}]" ".*"
> +	gdb_test "thread apply 2 record goto [expr {$loop_position + 3}]" ".*"
> +    }
>      with_test_prefix "thread 1" {
>          gdb_test "thread apply 1 stepi 2" "$loop_line"
>          gdb_test "thread apply 1 info record" \
> -            ".*Replay in progress\.  At instruction 7\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 5}]\."
>          gdb_test "thread apply 2 info record" \
> -            ".*Replay in progress\.  At instruction 5\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\."
>      }
>  
>      with_test_prefix "thread 2" {
>          gdb_test "thread apply 2 stepi 3" "$loop_line"
>          gdb_test "thread apply 1 info record" \
> -            ".*Replay in progress\.  At instruction 7\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 5}]\."
>          gdb_test "thread apply 2 info record" \
> -            ".*Replay in progress\.  At instruction 8\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 6}]\."
>      }
>  
>      with_test_prefix "all" {
>          gdb_cont_to all "stepi 4" "$loop_line" 2
>          gdb_test "thread apply 1 info record" \
> -            ".*Replay in progress\.  At instruction 11\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 9}]\."
>          gdb_test "thread apply 2 info record" \
> -            ".*Replay in progress\.  At instruction 12\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 10}]\."
>      }
>  }
>  
>  with_test_prefix "reverse-step" {
> +    with_test_prefix "fixture" {
> +        gdb_test "thread apply 1 record goto [expr {$loop_position + 9}]" ".*"
> +        gdb_test "thread apply 2 record goto [expr {$loop_position + 10}]" ".*"
> +    }
>      with_test_prefix "thread 1" {
>          gdb_test "thread apply 1 reverse-stepi 2" "$loop_line"
>          gdb_test "thread apply 1 info record" \
> -            ".*Replay in progress\.  At instruction 9\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 7}]\."
>          gdb_test "thread apply 2 info record" \
> -            ".*Replay in progress\.  At instruction 12\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 10}]\."
>      }
>  
>      with_test_prefix "thread 2" {
>          gdb_test "thread apply 2 reverse-stepi 3" "$loop_line"
>          gdb_test "thread apply 1 info record" \
> -            ".*Replay in progress\.  At instruction 9\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 7}]\."
>          gdb_test "thread apply 2 info record" \
> -            ".*Replay in progress\.  At instruction 9\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 7}]\."
>      }
>  
>      with_test_prefix "all" {
>          gdb_cont_to all "reverse-stepi 4" "$loop_line" 2
>          gdb_test "thread apply 1 info record" \
> -            ".*Replay in progress\.  At instruction 5\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\."
>          gdb_test "thread apply 2 info record" \
> -            ".*Replay in progress\.  At instruction 5\."
> +            ".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\."
>      }
>  }
>  
>  with_test_prefix "continue" {
> +    with_test_prefix "fixture" {
> +	gdb_test "thread apply 1 record goto [expr {$loop_position + 3}]" ".*"
> +	gdb_test "thread apply 2 record goto [expr {$loop_position + 3}]" ".*"
> +    }
>      with_test_prefix "thread 1" {
>  	with_test_prefix "continue" {
>  	    gdb_cont_to_no_history 1 "continue" 1
>  	    gdb_test "thread apply 1 info record" \
>  		".*Recorded \[0-9\]+ instructions \[^\\\r\\\n\]*"
>  	    gdb_test "thread apply 2 info record" \
> -		".*Replay in progress\.  At instruction 5\."
> +		".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\."
>  	}
>  	with_test_prefix "reverse-continue" {
>  	    gdb_cont_to_no_history 1 "reverse-continue" 1
>  	    gdb_test "thread apply 1 info record" \
>  		".*Replay in progress\.  At instruction 1\."
>  	    gdb_test "thread apply 2 info record" \
> -		".*Replay in progress\.  At instruction 5\."
> +		".*Replay in progress\.  At instruction [expr {$loop_position + 3}]\."
>  	}
>      }
>  
> diff --git a/gdb/testsuite/gdb.btrace/record_goto.exp b/gdb/testsuite/gdb.btrace/record_goto.exp
> index 75d76da1c7f..267d4b412bc 100644
> --- a/gdb/testsuite/gdb.btrace/record_goto.exp
> +++ b/gdb/testsuite/gdb.btrace/record_goto.exp
> @@ -35,21 +35,149 @@ if [info exists COMPILE] {
>      # make check RUNTESTFLAGS="gdb.btrace/record_goto.exp COMPILE=1"
>      standard_testfile record_goto.c
>      lappend opts debug
> -} elseif {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +} else {
> +  set test_prefix ""
> +  if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
>      if {[is_amd64_regs_target]} {
> -		standard_testfile x86_64-record_goto.S
> +      set test_prefix "x86_64"
>      } else {
> -		standard_testfile i686-record_goto.S
> +      set test_prefix "i686"
>      }
> -} else {
> +  } elseif {[istarget "arm*-*-*"]} {
> +      set test_prefix "arm"
> +  } elseif {[istarget "aarch64*-*-*"]} {
> +    set test_prefix "aarch64"
> +  } else {
>      unsupported "target architecture not supported"
>      return -1
> +  }
> +  standard_testfile $test_prefix-record_goto.S
>  }
>  
>  if [prepare_for_testing "failed to prepare" $testfile $srcfile $opts] {
>      return -1
>  }
>  
> +if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +    set function_positions(0) 19
> +    set function_positions(1) 27
> +    set function_positions(2) 2
> +    set function_positions(end) 40
> +    set function_positions(3) 39
> +
> +    set sequence_begin(1) 1
> +    set sequence_end(1) 1
> +    set sequence_begin(2) 2
> +    set sequence_end(2) 4
> +    set sequence_begin(3) 5
> +    set sequence_end(3) 8
> +    set sequence_begin(4) 9
> +    set sequence_end(4) 9
> +    set sequence_begin(5) 10
> +    set sequence_end(5) 12
> +    set sequence_begin(6) 13
> +    set sequence_end(6) 16
> +    set sequence_begin(7) 17
> +    set sequence_end(7) 18
> +    set sequence_begin(8) 19
> +    set sequence_end(8) 19
> +    set sequence_begin(9) 20
> +    set sequence_end(9) 22
> +    set sequence_begin(10) 23
> +    set sequence_end(10) 26
> +    set sequence_begin(11) 27
> +    set sequence_end(11) 27
> +    set sequence_begin(12) 28
> +    set sequence_end(12) 30
> +    set sequence_begin(13) 31
> +    set sequence_end(13) 34
> +    set sequence_begin(14) 35
> +    set sequence_end(14) 36
> +    set sequence_begin(15) 37
> +    set sequence_end(15) 38
> +    set sequence_begin(16) 39
> +    set sequence_end(16) 40
> +
> +} elseif {[istarget "arm*-*-*"]} {
> +    set function_positions(0) 23
> +    set function_positions(1) 33
> +    set function_positions(2) 2
> +    set function_positions(end) 48
> +    set function_positions(3) 47
> +
> +    set sequence_begin(1) 1
> +    set sequence_end(1) 1
> +    set sequence_begin(2) 2
> +    set sequence_end(2) 4
> +    set sequence_begin(3) 5
> +    set sequence_end(3) 10
> +    set sequence_begin(4) 11
> +    set sequence_end(4) 11
> +    set sequence_begin(5) 12
> +    set sequence_end(5) 14
> +    set sequence_begin(6) 15
> +    set sequence_end(6) 20
> +    set sequence_begin(7) 21
> +    set sequence_end(7) 22
> +    set sequence_begin(8) 23
> +    set sequence_end(8) 23
> +    set sequence_begin(9) 24
> +    set sequence_end(9) 26
> +    set sequence_begin(10) 27
> +    set sequence_end(10) 32
> +    set sequence_begin(11) 33
> +    set sequence_end(11) 33
> +    set sequence_begin(12) 34
> +    set sequence_end(12) 36
> +    set sequence_begin(13) 37
> +    set sequence_end(13) 42
> +    set sequence_begin(14) 43
> +    set sequence_end(14) 44
> +    set sequence_begin(15) 45
> +    set sequence_end(15) 46
> +    set sequence_begin(16) 47
> +    set sequence_end(16) 48
> +} elseif {[istarget "aarch64*-*-*"]} {
> +    set function_positions(0) 16
> +    set function_positions(1) 22
> +    set function_positions(2) 2
> +    set function_positions(end) 36
> +    set function_positions(3) 35
> +
> +    set sequence_begin(1) 1
> +    set sequence_end(1) 1
> +    set sequence_begin(2) 2
> +    set sequence_end(2) 4
> +    set sequence_begin(3) 5
> +    set sequence_end(3) 6
> +    set sequence_begin(4) 7
> +    set sequence_end(4) 7
> +    set sequence_begin(5) 8
> +    set sequence_end(5) 10
> +    set sequence_begin(6) 11
> +    set sequence_end(6) 12
> +    set sequence_begin(7) 13
> +    set sequence_end(7) 15
> +    set sequence_begin(8) 16
> +    set sequence_end(8) 16
> +    set sequence_begin(9) 17
> +    set sequence_end(9) 19
> +    set sequence_begin(10) 20
> +    set sequence_end(10) 21
> +    set sequence_begin(11) 22
> +    set sequence_end(11) 22
> +    set sequence_begin(12) 23
> +    set sequence_end(12) 25
> +    set sequence_begin(13) 26
> +    set sequence_end(13) 27
> +    set sequence_begin(14) 28
> +    set sequence_end(14) 30
> +    set sequence_begin(15) 31
> +    set sequence_end(15) 33
> +    set sequence_begin(16) 34
> +    set sequence_end(16) 36
> +}
> +
>  if ![runto_main] {
>      untested "failed to run to main"
>      return -1
> @@ -65,43 +193,43 @@ gdb_test "next"
>  
>  # start by listing all functions
>  gdb_test "record function-call-history /ci 1, +20" [multi_line \
> -  "1\tmain\tinst 1,1" \
> -  "2\t  fun4\tinst 2,4" \
> -  "3\t    fun1\tinst 5,8" \
> -  "4\t  fun4\tinst 9,9" \
> -  "5\t    fun2\tinst 10,12" \
> -  "6\t      fun1\tinst 13,16" \
> -  "7\t    fun2\tinst 17,18" \
> -  "8\t  fun4\tinst 19,19" \
> -  "9\t    fun3\tinst 20,22" \
> -  "10\t      fun1\tinst 23,26" \
> -  "11\t    fun3\tinst 27,27" \
> -  "12\t      fun2\tinst 28,30" \
> -  "13\t        fun1\tinst 31,34" \
> -  "14\t      fun2\tinst 35,36" \
> -  "15\t    fun3\tinst 37,38" \
> -  "16\t  fun4\tinst 39,40" \
> +  "1\tmain\tinst $sequence_begin(1),$sequence_end(1)" \
> +  "2\t  fun4\tinst $sequence_begin(2),$sequence_end(2)" \
> +  "3\t    fun1\tinst $sequence_begin(3),$sequence_end(3)" \
> +  "4\t  fun4\tinst $sequence_begin(4),$sequence_end(4)" \
> +  "5\t    fun2\tinst $sequence_begin(5),$sequence_end(5)" \
> +  "6\t      fun1\tinst $sequence_begin(6),$sequence_end(6)" \
> +  "7\t    fun2\tinst $sequence_begin(7),$sequence_end(7)" \
> +  "8\t  fun4\tinst $sequence_begin(8),$sequence_end(8)" \
> +  "9\t    fun3\tinst $sequence_begin(9),$sequence_end(9)" \
> +  "10\t      fun1\tinst $sequence_begin(10),$sequence_end(10)" \
> +  "11\t    fun3\tinst $sequence_begin(11),$sequence_end(11)" \
> +  "12\t      fun2\tinst $sequence_begin(12),$sequence_end(12)" \
> +  "13\t        fun1\tinst $sequence_begin(13),$sequence_end(13)" \
> +  "14\t      fun2\tinst $sequence_begin(14),$sequence_end(14)" \
> +  "15\t    fun3\tinst $sequence_begin(15),$sequence_end(15)" \
> +  "16\t  fun4\tinst $sequence_begin(16),$sequence_end(16)" \
>    ]
>  
>  # let's see if we can go back in history
> -gdb_test "record goto 19" ".*fun4 \\(\\) at record_goto.c:43.*"
> +gdb_test "record goto $function_positions(0)" ".*fun4 \\(\\) at record_goto.c:43.*"
>  
>  # the function call history should start at the new location
>  gdb_test "record function-call-history /ci" [multi_line \
> -  "8\t  fun4\tinst 19,19" \
> -  "9\t    fun3\tinst 20,22" \
> -  "10\t      fun1\tinst 23,26" \
> -  ] "function-call-history from 19 forwards"
> +  "8\t  fun4\tinst $sequence_begin(8),$sequence_end(8)" \
> +  "9\t    fun3\tinst $sequence_begin(9),$sequence_end(9)" \
> +  "10\t      fun1\tinst $sequence_begin(10),$sequence_end(10)" \
> +  ] "function-call-history from $function_positions(0) forwards"
>  
>  # the instruction history should start at the new location
>  gdb_test "record instruction-history" [multi_line \
> -  "19.*" \
> -  "20.*" \
> -  "21.*" \
> -  ] "instruction-history from 19 forwards"
> +  "$function_positions(0).*" \
> +  "[expr {$function_positions(0) + 1}].*" \
> +  "[expr {$function_positions(0) + 2}].*" \
> +  ] "instruction-history from $function_positions(0) forwards"
>  
>  # let's go to another place in the history
> -gdb_test "record goto 27" ".*fun3 \\(\\) at record_goto.c:35.*"
> +gdb_test "record goto $function_positions(1)" ".*fun3 \\(\\) at record_goto.c:35.*"
>  
>  # check the back trace at that location
>  gdb_test "backtrace" [multi_line \
> @@ -117,26 +245,26 @@ gdb_test "up" ".*main.*at record_goto.c:49.*" "up to main"
>  
>  # the function call history should start at the new location
>  gdb_test "record function-call-history /ci -" [multi_line \
> -  "9\t    fun3\tinst 20,22" \
> -  "10\t      fun1\tinst 23,26" \
> -  "11\t    fun3\tinst 27,27" \
> -  ] "function-call-history from 27 backwards"
> +  "9\t    fun3\tinst $sequence_begin(9),$sequence_end(9)" \
> +  "10\t      fun1\tinst $sequence_begin(10),$sequence_end(10)" \
> +  "11\t    fun3\tinst $sequence_begin(11),$sequence_end(11)" \
> +  ] "function-call-history from $function_positions(1) backwards"
>  
>  # the instruction history should start at the new location
>  gdb_test "record instruction-history -" [multi_line \
> -  "25.*" \
> -  "26.*" \
> -  "27.*" \
> -  ] "instruction-history from 27 backwards"
> +  "[expr {$function_positions(1) - 2}].*" \
> +  "[expr {$function_positions(1) - 1}].*" \
> +  "$function_positions(1).*" \
> +  ] "instruction-history from $function_positions(1) backwards"
>  
>  # test that we can go to the begin of the trace
>  gdb_test "record goto begin" ".*main \\(\\) at record_goto.c:49.*"
>  
>  # check that we're filling up the context correctly
>  gdb_test "record function-call-history /ci -" [multi_line \
> -  "1\tmain\tinst 1,1" \
> -  "2\t  fun4\tinst 2,4" \
> -  "3\t    fun1\tinst 5,8" \
> +  "1\tmain\tinst $sequence_begin(1),$sequence_end(1)" \
> +  "2\t  fun4\tinst $sequence_begin(2),$sequence_end(2)" \
> +  "3\t    fun1\tinst $sequence_begin(3),$sequence_end(3)" \
>    ] "function-call-history from begin backwards"
>  
>  # check that we're filling up the context correctly
> @@ -147,52 +275,53 @@ gdb_test "record instruction-history -" [multi_line \
>    ] "instruction-history from begin backwards"
>  
>  # we should get the exact same history from the first instruction
> -gdb_test "record goto 2" ".*fun4 \\(\\) at record_goto.c:40.*"
> +gdb_test "record goto $function_positions(2)" ".*fun4 \\(\\) at record_goto.c:40.*"
>  
>  # check that we're filling up the context correctly
>  gdb_test "record function-call-history /ci -" [multi_line \
> -  "1\tmain\tinst 1,1" \
> -  "2\t  fun4\tinst 2,4" \
> -  "3\t    fun1\tinst 5,8\r" \
> -  ] "function-call-history from 2 backwards"
> +  "1\tmain\tinst $sequence_begin(1),$sequence_end(1)" \
> +  "2\t  fun4\tinst $sequence_begin(2),$sequence_end(2)" \
> +  "3\t    fun1\tinst $sequence_begin(3),$sequence_end(3)\r" \
> +  ] "function-call-history from $function_positions(2) backwards"
>  
>  # check that we're filling up the context correctly
>  gdb_test "record instruction-history -" [multi_line \
>    "1.*" \
>    "2.*" \
>    "3.*" \
> -  ] "instruction-history from 2 backwards"
> +  ] "instruction-history from $function_positions(2) backwards"
>  
>  # check that we can go to the end of the trace
>  gdb_test "record goto end" ".*main \\(\\) at record_goto.c:50.*"
>  
>  # check that we're filling up the context correctly
>  gdb_test "record function-call-history /ci" [multi_line \
> -  "14\t      fun2\tinst 35,36" \
> -  "15\t    fun3\tinst 37,38" \
> -  "16\t  fun4\tinst 39,40" \
> +  "14\t      fun2\tinst $sequence_begin(14),$sequence_end(14)" \
> +  "15\t    fun3\tinst $sequence_begin(15),$sequence_end(15)" \
> +  "16\t  fun4\tinst $sequence_begin(16),$sequence_end(16)" \
>    ] "function-call-history from end forwards"
>  
>  # check that we're filling up the context correctly
> +#adapt it for arm, last instruction is  at pos 48
>  gdb_test "record instruction-history" [multi_line \
> -  "38.*" \
> -  "39.*" \
> -  "40.*\r" \
> +  "[expr {$function_positions(end) - 2}].*" \
> +  "[expr {$function_positions(end) - 1}].*" \
> +  "$function_positions(end).*\r" \
>    ] "instruction-history from end forwards"
>  
>  # we should get the exact same history from the second to last instruction
> -gdb_test "record goto 39" ".*fun4 \\(\\) at record_goto.c:44.*"
> +gdb_test "record goto $function_positions(3)" ".*fun4 \\(\\) at record_goto.c:44.*"
>  
>  # check that we're filling up the context correctly
>  gdb_test "record function-call-history /ci" [multi_line \
> -  "14\t      fun2\tinst 35,36" \
> -  "15\t    fun3\tinst 37,38" \
> -  "16\t  fun4\tinst 39,40\r" \
> -  ] "function-call-history from 39 forwards"
> +  "14\t      fun2\tinst $sequence_begin(14),$sequence_end(14)" \
> +  "15\t    fun3\tinst $sequence_begin(15),$sequence_end(15)" \
> +  "16\t  fun4\tinst $sequence_begin(16),$sequence_end(16)\r" \
> +  ] "function-call-history from $function_positions(3) forwards"
>  
>  # check that we're filling up the context correctly
>  gdb_test "record instruction-history" [multi_line \
> -  "38.*" \
> -  "39.*" \
> -  "40.*\r" \
> -  ] "instruction-history from 39 forwards"
> +  "[expr {$function_positions(3) - 1}].*" \
> +  "$function_positions(3).*" \
> +  "[expr {$function_positions(3) + 1}].*\r" \
> +  ] "instruction-history from $function_positions(3) forwards"
> diff --git a/gdb/testsuite/gdb.btrace/stepi.exp b/gdb/testsuite/gdb.btrace/stepi.exp
> index 480c08c4875..f07961228e0 100644
> --- a/gdb/testsuite/gdb.btrace/stepi.exp
> +++ b/gdb/testsuite/gdb.btrace/stepi.exp
> @@ -33,21 +33,79 @@ if [info exists COMPILE] {
>      # make check RUNTESTFLAGS="gdb.btrace/stepi.exp COMPILE=1"
>      standard_testfile record_goto.c
>      lappend opts debug
> -} elseif {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +} else {
> +  set test_prefix ""
> +  if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
>      if {[is_amd64_regs_target]} {
> -		standard_testfile x86_64-record_goto.S
> +      set test_prefix "x86_64"
>      } else {
> -		standard_testfile i686-record_goto.S
> +      set test_prefix "i686"
>      }
> -} else {
> +  } elseif {[istarget "arm*-*-*"]} {
> +      set test_prefix "arm"
> +  } elseif {[istarget "aarch64*-*-*"]} {
> +    set test_prefix "aarch64"
> +  } else {
>      unsupported "target architecture not supported"
>      return -1
> +  }
> +  standard_testfile $test_prefix-record_goto.S
>  }
>  
>  if [prepare_for_testing "failed to prepare" $testfile $srcfile {}] {
>      return -1
>  }
>  
> +if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +    set instructions_count 40
> +    set instruction_position(0) 39
> +    set instruction_position(1) 40
> +    set instruction_position(2) 1
> +    set instruction_position(3) 1
> +    set instruction_position(4) 22
> +    set instruction_position(5) 23
> +    set instruction_position(6) 22
> +    set instruction_position(7) 27
> +    set instruction_position(8) 22
> +    set instruction_position(9) 1
> +    set instruction_position(10) 1
> +    set instruction_position(11) 1
> +    set instruction_position(12) 2
> +    set instruction_position(13) 1
> +} elseif {[istarget "arm*-*-*"]} {
> +    set instructions_count 48
> +    set instruction_position(0) 47
> +    set instruction_position(1) 48
> +    set instruction_position(2) 1
> +    set instruction_position(3) 1
> +    set instruction_position(4) 26
> +    set instruction_position(5) 27
> +    set instruction_position(6) 26
> +    set instruction_position(7) 33
> +    set instruction_position(8) 26
> +    set instruction_position(9) 1
> +    set instruction_position(10) 1
> +    set instruction_position(11) 1
> +    set instruction_position(12) 2
> +    set instruction_position(13) 1
> +} elseif {[istarget "aarch64*-*-*"]} {
> +    set instructions_count 36
> +    set instruction_position(0) 35
> +    set instruction_position(1) 36
> +    set instruction_position(2) 1
> +    set instruction_position(3) 1
> +    set instruction_position(4) 19
> +    set instruction_position(5) 20
> +    set instruction_position(6) 19
> +    set instruction_position(7) 22
> +    set instruction_position(8) 19
> +    set instruction_position(9) 1
> +    set instruction_position(10) 1
> +    set instruction_position(11) 1
> +    set instruction_position(12) 2
> +    set instruction_position(13) 1
> +}
> +
>  if ![runto_main] {
>      untested "failed to run to main"
>      return -1
> @@ -56,10 +114,11 @@ if ![runto_main] {
>  global gdb_prompt
>  
>  proc check_replay_at { insn } {
> +  global instructions_count
>    gdb_test "info record" [multi_line \
>      "Active record target: record-btrace" \
>      ".*" \
> -    "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for .*" \
> +    "Recorded $instructions_count instructions in 16 functions \\\(0 gaps\\\) for .*" \
>      "Replay in progress\.  At instruction $insn\." \
>      ] "check replay at $insn"
>  }
> @@ -74,15 +133,14 @@ with_test_prefix "record" {
>  with_test_prefix "fetch" {
>      gdb_test "reverse-stepi" ".*fun4\.5.*" "reverse-stepi.1"
>      gdb_test "reverse-stepi" ".*fun4\.5.*" "reverse-stepi.2"
> -
>      # let's check where we are in the trace
> -    check_replay_at 39
> +    check_replay_at $instruction_position(0) 
>  }
>  
>  # let's step forward and check again
>  with_test_prefix "stepi" {
>      gdb_test "stepi" ".*fun4\.5.*"
> -    check_replay_at 40
> +    check_replay_at $instruction_position(1) 
>  }
>  
>  # with the next step, we stop replaying
> @@ -91,14 +149,17 @@ with_test_prefix "end" {
>      gdb_test "info record" [multi_line \
>        "Active record target: record-btrace" \
>        ".*" \
> -      "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
> +      "Recorded $instructions_count instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
>    ] 
>  }
>  
> +#recover from a missing lr register in arm
> +gdb_test "record goto end"
> +
>  # let's try nexti
>  with_test_prefix "reverse-nexti.1" {
>      gdb_test "reverse-nexti" ".*main\.2.*"
> -    check_replay_at 1
> +    check_replay_at $instruction_position(2)
>  }
>  
>  # we can't reverse-nexti any further
> @@ -106,7 +167,7 @@ with_test_prefix "reverse-nexti.2" {
>      gdb_test "reverse-nexti" \
>  	"No more reverse-execution history\.\r\n.*main\.2.*" \
>  	"reverse-nexti.2"
> -    check_replay_at 1
> +    check_replay_at $instruction_position(3)
>  }
>  
>  # but we can step back again
> @@ -115,32 +176,32 @@ with_test_prefix "nexti" {
>      gdb_test "info record" [multi_line \
>        "Active record target: record-btrace" \
>        ".*" \
> -      "Recorded 40 instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
> +      "Recorded $instructions_count instructions in 16 functions \\\(0 gaps\\\) for \[^\\\r\\\n\]*" \
>  			       ]
>  }
>  
>  # let's step from a goto position somewhere in the middle
>  with_test_prefix "goto" {
> -    gdb_test "record goto 22" ".*fun3\.2.*"
> -    with_test_prefix "goto 22" { check_replay_at 22 }
> +    gdb_test "record goto $instruction_position(4) " ".*fun3\.2.*"
> +    with_test_prefix "goto $instruction_position(4) " { check_replay_at $instruction_position(4)  }
>  
>      gdb_test "stepi" ".*fun1\.1.*" "stepi.3"
> -    with_test_prefix "stepi to 23" { check_replay_at 23 }
> +    with_test_prefix "stepi to $instruction_position(5)" { check_replay_at $instruction_position(5) }
>  
>      gdb_test "reverse-stepi" ".*fun3\.2.*" "reverse-stepi.3"
> -    with_test_prefix "reverse-stepi to 22" { check_replay_at 22 }
> +    with_test_prefix "reverse-stepi to $instruction_position(6)" { check_replay_at $instruction_position(6) }
>  
>      gdb_test "nexti" ".*fun3\.3.*"
> -    with_test_prefix "nexti to 27" { check_replay_at 27 }
> +    with_test_prefix "nexti to $instruction_position(7) " { check_replay_at $instruction_position(7)  }
>  
>      gdb_test "reverse-nexti" ".*fun3\.2.*" "reverse-nexti.3"
> -    with_test_prefix "reverse-nexti to 22" { check_replay_at 22 }
> +    with_test_prefix "reverse-nexti to $instruction_position(8)" { check_replay_at $instruction_position(8) }
>  }
>  
>  # let's try to step off the left end
>  with_test_prefix "goto begin" {
>      gdb_test "record goto begin" ".*main\.2.*"
> -    check_replay_at 1
> +    check_replay_at $instruction_position(9)
>  
>      with_test_prefix "reverse-stepi" {
>  	gdb_test "reverse-stepi" \
> @@ -149,7 +210,7 @@ with_test_prefix "goto begin" {
>  	gdb_test "reverse-stepi" \
>  	    "No more reverse-execution history\.\r\n.*main\.2.*" \
>  	    "reverse-stepi.2"
> -	check_replay_at 1
> +	check_replay_at $instruction_position(10)
>      }
>  
>      with_test_prefix "reverse-nexti" {
> @@ -159,13 +220,13 @@ with_test_prefix "goto begin" {
>  	gdb_test "reverse-nexti" \
>  	    "No more reverse-execution history\.\r\n.*main\.2.*" \
>  	    "reverse-nexti.2"
> -	check_replay_at 1
> +	check_replay_at $instruction_position(11)
>      }
>  
>      # we can step forward, though
>      with_test_prefix "stepi" {
>  	gdb_test "stepi" ".*fun4\.1.*"
> -	check_replay_at 2
> +	check_replay_at $instruction_position(12)
>      }
>  }
>  
> @@ -178,5 +239,5 @@ with_test_prefix "reverse-stepi" {
>      gdb_test "reverse-stepi" \
>  	"No more reverse-execution history\.\r\n.*main\.2.*" \
>  	"reverse-stepi.3"
> -    check_replay_at 1
> +    check_replay_at $instruction_position(13)
>  }
> diff --git a/gdb/testsuite/gdb.btrace/tailcall-only.exp b/gdb/testsuite/gdb.btrace/tailcall-only.exp
> index 510f90c9d5e..036da4a2020 100644
> --- a/gdb/testsuite/gdb.btrace/tailcall-only.exp
> +++ b/gdb/testsuite/gdb.btrace/tailcall-only.exp
> @@ -37,15 +37,23 @@ if [info exists COMPILE] {
>      # make check RUNTESTFLAGS="gdb.btrace/tailcall-only.exp COMPILE=1"
>      standard_testfile tailcall-only.c
>      lappend opts debug optimize=-O2
> -} elseif {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +} else {
> +  set test_prefix ""
> +  if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
>      if {[is_amd64_regs_target]} {
> -		standard_testfile x86_64-tailcall-only.S
> +      set test_prefix "x86_64"
>      } else {
> -		standard_testfile i686-tailcall-only.S
> +      set test_prefix "i686"
>      }
> -} else {
> +  } elseif {[istarget "arm*-*-*"]} {
> +      set test_prefix "arm"
> +  } elseif {[istarget "aarch64*-*-*"]} {
> +    set test_prefix "aarch64"
> +  } else {
>      unsupported "target architecture not supported"
>      return -1
> +  }
> +  standard_testfile $test_prefix-tailcall-only.S
>  }
>  
>  if [prepare_for_testing "failed to prepare" $testfile $srcfile $opts] {
> @@ -62,6 +70,10 @@ gdb_test_no_output "set record function-call-history-size 0"
>  
>  # trace foo
>  gdb_test "step" ".*" "prepare for recording"
> +# make sure we get out of function epilogue
> +if { [istarget "arm*-*-*"] } {
> +  gdb_test "stepi"
> +}
>  gdb_test_no_output "record btrace"
>  gdb_test "stepi 4" ".*" "record branch trace"
>  
> diff --git a/gdb/testsuite/gdb.btrace/tailcall.exp b/gdb/testsuite/gdb.btrace/tailcall.exp
> index 07a3ec103f4..70fda8a5670 100644
> --- a/gdb/testsuite/gdb.btrace/tailcall.exp
> +++ b/gdb/testsuite/gdb.btrace/tailcall.exp
> @@ -34,20 +34,42 @@ if [info exists COMPILE] {
>      # make check RUNTESTFLAGS="gdb.btrace/tailcall.exp COMPILE=1"
>      standard_testfile tailcall.c
>      lappend opts debug optimize=-O2
> -} elseif {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +} else {
> +  set test_prefix ""
> +  if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
>      if {[is_amd64_regs_target]} {
> -		standard_testfile x86_64-tailcall.S
> +      set test_prefix "x86_64"
>      } else {
> -		standard_testfile i686-tailcall.S
> +      set test_prefix "i686"
>      }
> -} else {
> +  } elseif {[istarget "arm*-*-*"]} {
> +      set test_prefix "arm"
> +  } elseif {[istarget "aarch64*-*-*"]} {
> +    set test_prefix "aarch64"
> +  } else {
>      unsupported "target architecture not supported"
>      return -1
> +  }
> +  standard_testfile $test_prefix-tailcall.S
>  }
>  
>  if [prepare_for_testing "failed to prepare" $testfile $srcfile $opts] {
>      return -1
>  }
> +
> +if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +    set bar_return_line 24
> +    set bar_return_position 4
> +    set main_return_line 38
> +} elseif {[istarget "arm*-*-*"]} {
> +    set bar_return_line 24
> +    set bar_return_position 5
> +    set main_return_line 41
> +    } elseif {[istarget "aarch64*-*-*"]} {
> +    set bar_return_line 23
> +    set bar_return_position 6
> +    set main_return_line 40
> +}
>  if ![runto_main] {
>      untested "failed to run to main"
>      return -1
> @@ -58,7 +80,17 @@ gdb_test_no_output "set record function-call-history-size 0"
>  
>  # trace the call to foo
>  gdb_test_no_output "record btrace"
> -gdb_test "next 2"
> +
> +# make sure we get out of function epilogue
> +if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
> +    gdb_test "next 2"
> +} elseif {[istarget "arm*-*-*"]} {
> +  gdb_test "next 2"
> +  gdb_test "stepi"
> +} elseif {[istarget "aarch64*-*-*"]} {
> +  gdb_test "next"
> +  gdb_test "stepi 2"
> +}
>  
>  # show the flat branch trace
>  gdb_test "record function-call-history 1" [multi_line \
> @@ -77,11 +109,11 @@ gdb_test "record function-call-history /c 1" [multi_line \
>    ] "indented"
>  
>  # go into bar
> -gdb_test "record goto 4" ".*bar \\(\\) at .*tailcall.c:24\r\n.*"
> +gdb_test "record goto $bar_return_position" ".*bar \\(\\) at .*tailcall.c:$bar_return_line\r\n.*"
>  
>  # check the backtrace
>  gdb_test "backtrace" [multi_line \
> -  "#0.*bar \\(\\) at tailcall.c:24" \
> +  "#0.*bar \\(\\) at tailcall.c:$bar_return_line" \
>    "#1.*foo \\(\\) at tailcall.c:29" \
>    "#2.*main \\(\\) at tailcall.c:37" \
>    "Backtrace stopped: not enough registers or memory available to unwind further" \
> @@ -93,23 +125,23 @@ gdb_test "up" "#2\[^\r\n\]*main \\(\\) at tailcall.c:37\r\n.*" "up to main"
>  gdb_test "down" "#1\[^\r\n\]*foo \\(\\) at tailcall.c:29\r\n.*" "down to foo"
>  
>  # test stepping into and out of tailcalls.
> -gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:38\r\n.*" \
> +gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:$main_return_line\r\n.*" \
>      "finish.1"
> -gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at tailcall.c:24\r\n.*" \
> +gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at tailcall.c:$bar_return_line\r\n.*" \
>      "reverse-step.1"
>  gdb_test "reverse-finish" "\[^\r\n\]*foo \\(\\) at tailcall.c:29\r\n.*" \
>      "reverse-finish.1"
>  gdb_test "reverse-step" "\[^\r\n\]*main \\(\\) at tailcall.c:37\r\n.*" \
>      "reverse-step.2"
> -gdb_test "next" "\[^\r\n\]*38.*" \
> +gdb_test "next" "\[^\r\n\]*$main_return_line.*" \
>      "next.1"
>  gdb_test "reverse-next" "\[^\r\n\]*main \\(\\) at tailcall.c:37\r\n.*" \
>      "reverse-next.1"
>  gdb_test "step" "\[^\r\n\]*foo \\(\\) at tailcall.c:29\r\n.*" \
>      "step.1"
> -gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:38\r\n.*" \
> +gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:$main_return_line\r\n.*" \
>      "finish.2"
> -gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at tailcall.c:24\r\n.*" \
> +gdb_test "reverse-step" "\[^\r\n\]*bar \\(\\) at tailcall.c:$bar_return_line\r\n.*" \
>      "reverse-step.3"
> -gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:38\r\n.*" \
> +gdb_test "finish" "\[^\r\n\]*main \\(\\) at tailcall.c:$main_return_line\r\n.*" \
>      "finish.3"
> diff --git a/gdb/testsuite/gdb.btrace/instruction_history.S b/gdb/testsuite/gdb.btrace/x86-instruction_history.S
> similarity index 100%
> rename from gdb/testsuite/gdb.btrace/instruction_history.S
> rename to gdb/testsuite/gdb.btrace/x86-instruction_history.S
> diff --git a/gdb/testsuite/lib/gdb.exp b/gdb/testsuite/lib/gdb.exp
> index 36a5fd4feb7..02cf8a3cec6 100644
> --- a/gdb/testsuite/lib/gdb.exp
> +++ b/gdb/testsuite/lib/gdb.exp
> @@ -3248,7 +3248,7 @@ gdb_caching_proc skip_btrace_tests {
>      global srcdir subdir gdb_prompt inferior_exited_re
>  
>      set me "skip_btrace_tests"
> -    if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] } {
> +    if { ![istarget "i?86-*-*"] && ![istarget "x86_64-*-*"] && ![istarget "arm*-*-*"] && ![istarget "aarch64*-*-*"]} {
>          verbose "$me:  target does not support btrace, returning 1" 2
>          return 1
>      }
> -- 
> 2.25.1
> 

^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os
  2021-05-31 21:33 ` [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os Zied Guermazi
@ 2021-06-23  8:00   ` Metzger, Markus T
  2022-05-12 22:52     ` Zied Guermazi
  2021-06-30 13:24   ` Luis Machado
  1 sibling, 1 reply; 35+ messages in thread
From: Metzger, Markus T @ 2021-06-23  8:00 UTC (permalink / raw)
  To: Zied Guermazi, gdb-patches

Hello Zied,

>This patch implement the lower layer for starting ad stopping
>ARM CoreSight tracing on linux targets for arm and aarch64

The patch looks good overall.  There are a few style nits and I'd ask you to
split the PAGE_SIZE changes into a separate patch as they are unrelated.

Then, there's the discussion about sharing perf_event buffer mapping.
I pointed out which parts I believe can be shared.


>+/* Teardown branch tracing.  */
>+
>+void
>+arm_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)
>+{
>+  /* Ignore errors.  */
>+  linux_disable_btrace (tinfo);
>+}
>+
>+enum btrace_error
>+arm_linux_nat_target::read_btrace (struct btrace_data *data,
>+				   struct btrace_target_info *btinfo,
>+				   enum btrace_read_type type)
>+{
>+  return linux_read_btrace (data, btinfo, type);
>+}
>+
>+/* See to_btrace_conf in target.h.  */
>+
>+const struct btrace_config *
>+arm_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)
>+{
>+  return linux_btrace_conf (btinfo);
>+}

There's some inconsistency in comments on functions ranging from no comment
over referring to the original target struct, to an own comment.



>@@ -483,10 +487,11 @@ linux_enable_bts (ptid_t ptid, const struct
>btrace_config_bts *conf)
>   scoped_fd fd (syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0));
>   if (fd.get () < 0)
>     diagnose_perf_event_open_fail ();
>+  long page_size = sysconf (_SC_PAGESIZE);

Please split those PAGE_SIZE changes into a separate patch.  This is unrelated
to what this patch is doing.

Note that PAGE_SIZE was unsigned whereas sysconf () returns a signed integer.
I'd expect compilers to require proper casting.



>+/* Enable ARM CoreSight ETM tracing.  */
>+
>+static struct btrace_target_info *
>+linux_enable_etm (ptid_t ptid, const struct btrace_config_etm *conf)
>+{
[...]
>+  etm->attr.sample_type = PERF_SAMPLE_CPU;
>+  etm->attr.read_format = PERF_FORMAT_ID;
>+  etm->attr.sample_id_all = 1;

You enable sampling.  Wouldn't you need to mmap the data buffer, as well?


This ...

>+  errno = 0;
>+  scoped_fd fd (syscall (SYS_perf_event_open, &etm->attr, pid, -1, -1, 0));
>+  if (fd.get () < 0)
>+    diagnose_perf_event_open_fail ();
>+
>+  /* Allocate the configuration page.  */
>+  long page_size = sysconf (_SC_PAGESIZE);
>+  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE,
>MAP_SHARED,
>+		    fd.get (), 0);
>+  if (data.get () == MAP_FAILED)
>+    error (_("Failed to map trace user page: %s."), safe_strerror (errno));
>+
>+  struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)
>+    data.get ();
>+
>+  header->aux_offset = header->data_offset + header->data_size;
>+  /* Convert the requested size in bytes to pages (rounding up).  */
>+  pages = ((size_t) conf->size / page_size
>+	   + ((conf->size % page_size) == 0 ? 0 : 1));
>+  /* We need at least one page.  */
>+  if (pages == 0)
>+    pages = 1;
>+
>+  /* The buffer size can be requested in powers of two pages.  Adjust PAGES
>+     to the next power of two.  */
>+  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
>+    if ((pages & ((size_t) 1 << pg)) != 0)
>+      pages += ((size_t) 1 << pg);
>+
>+  /* We try to allocate the requested size.
>+     If that fails, try to get as much as we can.  */
>+  scoped_mmap aux;
>+  for (; pages > 0; pages >>= 1)
>+    {
>+      size_t length;
>+      __u64 data_size;
>+      data_size = (__u64) pages * page_size;
>+
>+      /* Don't ask for more than we can represent in the configuration.  */
>+      if ((__u64) UINT_MAX < data_size)
>+	continue;
>+
>+      length = (size_t) data_size;
>+
>+      /* Check for overflows.  */
>+      if ((__u64) length != data_size)
>+	continue;
>+
>+      header->aux_size = data_size;
>+
>+      errno = 0;
>+      aux.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (),
>+		 header->aux_offset);
>+      if (aux.get () != MAP_FAILED)
>+	break;
>+    }
>+  if (pages == 0)
>+    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));
>+
>+  etm->etm.size = aux.size ();
>+  etm->etm.mem = (const uint8_t *) aux.release ();
>+  etm->etm.data_head = &header->aux_head;
>+  etm->etm.last_head = header->aux_tail;
>+  etm->header = (struct perf_event_mmap_page *) data.release ();
>+  gdb_assert (etm->header == header);

... can be shared with btrace_enable_pt () by introducing some

perf_event_open_aux (struct perf_event_buffer *, const struct perf_event_attr *)

helper.

And if you indeed need to mmap the data buffer, as well, we can share that with
btrace_enable_bts (), although we'd need some more restructuring to leave
perf_event_open to the caller and just allocate the data and aux buffers using
two helpers - they would again look very similar but need to touch a different
set of fields in the header, so I'd keep those separate.

btrace_enable_foo () would then become
{
  perf_event_open ()
  perf_event_mmap_data ()
  perf_event_mmap_aux ()  /* not for bts */
}


>+  length = fread (buffer, 1, length, file.get ());
>+  buffer[length]='\0';

Spaces around =.

>+  while ((--length) != 0)
>+    {
>+      if ((buffer[length] == ',') || (buffer[length] == '-'))
>+	{
>+	  length++;
>+	  break;
>+	}
>+    }
>+
>+  int cpu_count;
>+  int found = sscanf (&buffer[length], "%d", &cpu_count);
>+  if (found < 1)
>+    error (_("Failed to get cpu count in %s: %s."),
>+	     buffer, safe_strerror (errno));
>+
>+  cpu_count ++;
>+  return (cpu_count);

No need for ().


>+  char filename[PATH_MAX];
>+  snprintf (filename, PATH_MAX,

sizeof (filename)


>+  char filename[PATH_MAX];
>+
>+  /* Get coresight register from sysfs.  */
>+  snprintf (filename, PATH_MAX,

sizeof (filename)

>+	    "/sys/bus/event_source/devices/cs_etm/cpu%d/%s", cpu, path);
>+  errno = 0;
>+  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
>+  if (file.get () == nullptr)
>+    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
>+
>+  uint32_t val = 0;
>+
>+  int  found = fscanf (file.get (), "0x%x", &val);
>+  if (found != 1)
>+    error (_("Failed to read coresight register from %s."), filename);
>+  return val;
>+}

Empty line before return?  I'd also remove the empty line between the
declaration of val and the call to fscanf ().

There are several very similar functions in this patch and each is structured
differently:

+perf_event_etm_event_type ()
+{
[...]
+
+  int type, found = fscanf (file.get (), "%d", &type);
+  if (found != 1)
+    error (_("Failed to read the ETM event type from %s."), filename);
+
+  return type;

+get_cpu_count (void)
+{
[...]
+
+  int cpu_count;
+  int found = sscanf (&buffer[length], "%d", &cpu_count);
+  if (found < 1)
+    error (_("Failed to get cpu count in %s: %s."),
+	     buffer, safe_strerror (errno));
+
+  cpu_count ++;
+  return (cpu_count);

+perf_event_etm_event_sink (const struct btrace_config_etm *conf)
+{
[...]
+
+  unsigned int sink;
+  int  found = fscanf (file.get (), "0x%x", &sink);
+  if (found != 1)
+    error (_("Failed to read the ETM sink from %s."), filename);
+
+  return sink;

+cs_etm_get_register (int cpu, const char *path)
+{
[...]
+
+  uint32_t val = 0;
+
+  int  found = fscanf (file.get (), "0x%x", &val);
+  if (found != 1)
+    error (_("Failed to read coresight register from %s."), filename);
+  return val;


>+
>+#define CORESIGHT_ETM_PMU_SEED  0x10
>+
>+/* Calculate trace_id for this cpu
>+   to be kept aligned with coresight-pmu.h.  */
>+
>+static inline int
>+coresight_get_trace_id (int cpu)
>+{
>+  return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));

In patch 3, you wrote

+  /* Trace id for this thread.
+     On a linux system, trace_id is assigned per cpu. The kernel copies 
+     the traces of each thread in a dedicated ring buffer. By this,
+     traces belonging to different threads are de-multiplexed.
+     On an RTOS system, especially when routing the traces outside of the SoC,
+     the OS has no other mean for de-multiplexing the traces than
+     the trace_id. The hardware (ETM IP) reserves 7 bits for the trace_id.
+     On linux system trace id is not needed, set it to 0xFF to ignore it
+     during parsing.  */
+  uint8_t trace_id;

Should this function return uint8_t and check that the ID is 7 bit max?


>+static void
>+fill_etm_trace_params (struct cs_etm_trace_params *etm_trace_params, int
>cpu)
>+{
>+  if (cs_etm_is_etmv4 (cpu) == true)

No need for explicit checks on bool.


>+static void
>+linux_fill_btrace_etm_config (struct btrace_target_info *tinfo,
>+			       struct btrace_data_etm_config *conf)
>+{
>+
>+  cs_etm_trace_params etm_trace_params;

Please declare at initialization time.

>+  conf->cpu_count = get_cpu_count ();
>+  conf->etm_trace_params = new std::vector<cs_etm_trace_params>;
>+  for (int i = 0; i < conf->cpu_count; i++)
>+    {
>+      fill_etm_trace_params (&etm_trace_params,i);
>+      conf->etm_trace_params->push_back (etm_trace_params);
>+    }

We need to avoid leaking the vector when fill_etm_trace_params () throws.



>+static enum btrace_error
>+linux_read_etm (struct btrace_data_etm *btrace,
>+		struct btrace_target_info *tinfo,
>+		enum btrace_read_type type)
>+{
>+  struct perf_event_buffer *etm;
>+  etm = &tinfo->variant.etm.etm;

Please combine.  No forward declarations anymore.  The old code was written
when GDB was still C.


regards,
markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v6 5/7] fix issue: gdb hangs in the command following a commad returning with TARGET_WAITKIND_NO_HISTORY
  2021-05-31 21:33 ` [PATCH v6 5/7] fix issue: gdb hangs in the command following a commad returning with TARGET_WAITKIND_NO_HISTORY Zied Guermazi
@ 2021-06-23  8:08   ` Metzger, Markus T
  0 siblings, 0 replies; 35+ messages in thread
From: Metzger, Markus T @ 2021-06-23  8:08 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches

Hello Zied,

>This patch fixes an issue observed with btrace when replaying the execution.
>The issue was observed on ARMv7 processors.
>To reproduce the issue, the user needs to replay in forward direction
>until he reaches the end of history, then replay backwards
>(e.g a reverse-next) and then replay forwards. GDB hangs and the user
>can not issue new commands.
>This fix keeps the same behaviour of gdb as when
>TARGET_WAITKIND_NO_HISTORY
>is hit on other architectures.
>
>gdb/ChangeLog
>
>	* infrun.c (set_step_over_info): add debug print.
>	(handle_inferior_event): clear step over info
>	in case TARGET_WAITKIND_NO_HISTORY.
>---
> gdb/infrun.c | 3 ++-
> 1 file changed, 2 insertions(+), 1 deletion(-)

Please drop this patch.  My series is still waiting for reviews but the fix below
is not sufficient.

Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v6 6/7] add support for coresight btrace via remote protocol
  2021-05-31 21:33 ` [PATCH v6 6/7] add support for coresight btrace via remote protocol Zied Guermazi
  2021-06-01 12:08   ` Eli Zaretskii
@ 2021-06-23 10:59   ` Metzger, Markus T
  1 sibling, 0 replies; 35+ messages in thread
From: Metzger, Markus T @ 2021-06-23 10:59 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches

Hello Zied,

>+/* Parse a btrace etm "cpu-etm-config-etmv4_config" xml record.  */
>+
>+static void
>+parse_xml_btrace_etm_config_source_config_cpu_etmv4_config (

Isn't the element just called "etmv4-config"?


>+				struct gdb_xml_parser *parser,
>+				const struct gdb_xml_element *element,
>+				void *user_data,
>+				std::vector<gdb_xml_value> &attributes)
>+{
>+  struct btrace_data *btrace;
>+  cs_etm_trace_params *etm_trace_params;

No forward declarations; etm_trace_params can become a reference.

>+
>+  DEBUG ("parse_xml_btrace_etm_config_source_config_cpu_etmv4_config");
>+
>+  btrace = (struct btrace_data *) user_data;
>+  etm_trace_params = & (btrace->variant.etm.config.etm_trace_params->back
>());
>+
>+  struct gdb_xml_value *reg_idr0;
>+  reg_idr0
>+    = xml_find_attribute (attributes, "reg_idr0");

Please combine declaration and initialization.

>+
>+  struct gdb_xml_value *reg_idr1;
>+  reg_idr1
>+    = xml_find_attribute (attributes, "reg_idr1");
>+
>+  struct gdb_xml_value *reg_idr2;
>+  reg_idr2
>+    = xml_find_attribute (attributes, "reg_idr2");
>+
>+  struct gdb_xml_value *reg_idr8;
>+  reg_idr8
>+    = xml_find_attribute (attributes, "reg_idr8");
>+
>+  struct gdb_xml_value *reg_configr;
>+  reg_configr
>+    = xml_find_attribute (attributes, "reg_configr");
>+
>+  struct gdb_xml_value *reg_traceidr;
>+  reg_traceidr
>+    = xml_find_attribute (attributes, "reg_traceidr");
>+
>+  etm_trace_params->etmv4.reg_idr0
>+    = (unsigned int) *(ULONGEST *) reg_idr0->value.get ();

We should check those pointers before dereferencing.  If the XML
doesn't contain that attribute, the pointer would be nullptr.


>+/* Parse a btrace etm "cpu-etm-config" xml record.  */
>+
>+static void
>+parse_xml_btrace_etm_config_source_config_cpu_etm_config (

How would we arrive at that function name?  Wouldn't it simply be

parse_xml_btrace_cpu_etm_config?

More below.

>+  trace_id = xml_find_attribute (attributes, "trace_id");
>+  if (trace_id != NULL)
>+    btrace->variant.etm.trace_id
>+      = (uint8_t) *(ULONGEST *) trace_id->value.get ();

Shouldn't this be an error?


>+static const struct gdb_xml_attribute
>+btrace_etm_config_source_config_cpu_config_etmv3_config_attributes[] = {
>+  { "reg_ctrl", GDB_XML_AF_NONE, gdb_xml_parse_attr_ulongest, NULL },

NULL is spelled nullptr in new code.


>+  struct gdb_xml_value *sink;
>+  sink = xml_find_attribute (attributes, "sink");
>+  if (sink != nullptr)
>+    conf->etm.sink = (char*) sink->value.get ();

I don't think this memory is alive after parsing the XML.


>+static const struct gdb_xml_attribute btrace_conf_etm_attributes[] = {
>+  { "size", GDB_XML_AF_OPTIONAL, gdb_xml_parse_attr_ulongest, NULL },
>+  { "sink", GDB_XML_AF_OPTIONAL, NULL, NULL },
>+  { NULL, GDB_XML_AF_NONE, NULL, NULL }
>+};

Shouldn't there be entries for filter and config, too?


> These are the currently defined stub features, in more detail:
>@@ -42248,9 +42262,11 @@ The remote stub understands the
>@samp{Qbtrace:off} packet.
>
> @item Qbtrace:bts
> The remote stub understands the @samp{Qbtrace:bts} packet.
>+(@pxref{bts}).
>
> @item Qbtrace:pt
> The remote stub understands the @samp{Qbtrace:pt} packet.
>+(@pxref{pt}).
>
> @item Qbtrace-conf:bts:size
> The remote stub understands the @samp{Qbtrace-conf:bts:size} packet.
>@@ -42286,7 +42302,6 @@ The remote stub understands the
>@samp{QThreadEvents} packet.
> @item no-resumed
> The remote stub reports the @samp{N} stop reply.
>
>-
> @item memory-tagging
> The remote stub supports and implements the required memory tagging
> functionality and understands the @samp{qMemTags} (@pxref{qMemTags}) and
>@@ -42296,6 +42311,15 @@ For AArch64 GNU/Linux systems, this feature also
>requires access to the
> @file{/proc/@var{pid}/smaps} file so memory mapping page flags can be
>inspected.
> This is done via the @samp{vFile} requests.
>
>+@item Qbtrace:etm
>+The remote stub understands the @samp{Qbtrace:etm} packet.
>+(@pxref{etm}).

Shouldn't this command be documented together with Qbtrace:bts and
Qbtrace:pt above?


>+
>+@item Qbtrace-conf:etm:size
>+The remote stub understands the @samp{Qbtrace-conf:etm:size} packet.
>+
>+@item Qbtrace-conf:etm:sink
>+The remote stub understands the @samp{Qbtrace-conf:etm:sink} packet.
> @end table

Same here.  They would go to the Qbtrace-conf:... block.

This comment is for all new packets that are really extensions of existing
btrace packets for a new format.


>diff --git a/gdb/features/btrace-conf.dtd b/gdb/features/btrace-conf.dtd
>index 4b060bb408c..7334d035f34 100644
>--- a/gdb/features/btrace-conf.dtd
>+++ b/gdb/features/btrace-conf.dtd
>@@ -4,11 +4,17 @@
>      are permitted in any medium without royalty provided the copyright
>      notice and this notice are preserved.  -->
>
>-<!ELEMENT btrace-conf	(bts?, pt?)>
>-<!ATTLIST btrace-conf	version	CDATA	#FIXED "1.0">
>+<!ELEMENT btrace-conf	(bts?, pt?, etm?)>
>+<!ATTLIST btrace-conf	version	(1.0|1.1) #REQUIRED>

I don't know how this would be handled.  We'd need someone else to
review this part.


>diff --git a/gdb/remote.c b/gdb/remote.c
>index 9b465d77343..2defaa4503e 100644
>--- a/gdb/remote.c
>+++ b/gdb/remote.c
>@@ -2184,6 +2184,15 @@ enum {
>      packets and the tag violation stop replies.  */
>   PACKET_memory_tagging_feature,
>
>+  /* Support for the Qbtrace-etm packet.  */
>+  PACKET_Qbtrace_etm,
>+
>+  /* Support for the Qbtrace-conf:etm:size packet.  */
>+  PACKET_Qbtrace_conf_etm_size,
>+
>+  /* Support for the Qbtrace-conf:etm:sink packet.  */
>+  PACKET_Qbtrace_conf_etm_sink,

Packets for filter and config are missing.


>+static void
>+linux_low_encode_etm_config (struct buffer *buffer,
>+			    const struct btrace_data_etm_config *config)
>+{
>+  int architecture;
>+  buffer_grow_str (buffer, "<etm-config>\n");
>+  buffer_grow_str (buffer, "<source-config>\n");
>+  for (int i=0; i< config->cpu_count;i++)
>+  {
>+    if ((config->etm_trace_params->at (i).protocol == OCSD_PROTOCOL_ETMV3)
>+	||(config->etm_trace_params->at (i).protocol == OCSD_PROTOCOL_PTM))

space before (

>+      {
>+	architecture = ARCH_V7;
>+      }

No {} for a single statement.

>+    else if (config->etm_trace_params->at (i).protocol ==
>OCSD_PROTOCOL_ETMV4I)
>+      {
>+	architecture = ARCH_V8;
>+      }
>+    else
>+      {
>+	architecture = ARCH_UNKNOWN;
>+      }
>+
>+    buffer_xml_printf (buffer,"<cpu-etm-config arch_ver=\"0x%x\" "
>+	"core_prof=\"0x%x\" cpu_id=\"0x%x\" protocol=\"0x%x\">\n",

Indentation of the split string is off.  More below.

>+			  architecture, profile_CortexA,
>+			  i, config->etm_trace_params->at (i).protocol);
>+    if (architecture == ARCH_V7)
>+    {
>+      buffer_xml_printf (buffer,
>+	"<etmv3-config reg_idr=\"0x%x\" reg_ctrl=\"0x%x\" "
>+	"reg_ccer=\"0x%x\" reg_trc_id=\"0x%x\"/>\n",
>+			  config->etm_trace_params->at (i).etmv3.reg_idr,
>+			  config->etm_trace_params->at (i).etmv3.reg_ctrl,
>+			  config->etm_trace_params->at (i).etmv3.reg_ccer,
>+			  config->etm_trace_params->at (i).etmv3.reg_trc_id);

It might help to stick to the order used in the corresponding schema.


>@@ -7077,7 +7161,7 @@ linux_process_target::read_btrace_conf (const
>btrace_target_info *tinfo,
>   const struct btrace_config *conf;
>
>   buffer_grow_str (buffer, "<!DOCTYPE btrace-conf SYSTEM \"btrace-
>conf.dtd\">\n");
>-  buffer_grow_str (buffer, "<btrace-conf version=\"1.0\">\n");
>+  buffer_grow_str (buffer, "<btrace-conf version=\"1.1\">\n");

We only need 1.1 for the new features.

If we built a new gdbserver from this it wouldn't be able to use PT or BTS
with an old GDB, although we didn't change anything, there.


>   conf = linux_btrace_conf (tinfo);
>   if (conf != NULL)
>@@ -7098,6 +7182,20 @@ linux_process_target::read_btrace_conf (const
>btrace_target_info *tinfo,
> 	  buffer_xml_printf (buffer, " size=\"0x%x\"", conf->pt.size);
> 	  buffer_xml_printf (buffer, "/>\n");
> 	  break;
>+
>+	case BTRACE_FORMAT_ETM:
>+	  buffer_xml_printf (buffer, "<etm");
>+	  buffer_xml_printf (buffer, " size=\"0x%x\"", conf->etm.size);
>+	  if (conf->etm.sink !=NULL)

spaces around != and nullptr instead of NULL

>+	    {
>+	      buffer_xml_printf (buffer, " sink=\"%s\"", conf->etm.sink);
>+	    }
>+	  else
>+	    {
>+	      buffer_xml_printf (buffer, " sink=\"default\"");

We treat nullptr as default above, yet we pass "default", here.  The parser
just installs that string.

Where do we handle the "default" sink?


Regards,
Markus.

Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v6 7/7] adapt btrace testcases for arm target
  2021-05-31 21:33 ` [PATCH v6 7/7] adapt btrace testcases for arm target Zied Guermazi
  2021-06-22 21:28   ` Lancelot SIX
@ 2021-06-23 14:16   ` Metzger, Markus T
  2022-05-13 11:08   ` Richard Earnshaw
  2 siblings, 0 replies; 35+ messages in thread
From: Metzger, Markus T @ 2021-06-23 14:16 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches

Hello Zied,

>+   This file has been generated on an armv8 machine using:
>+   gcc -m32 -S -O2 -dA -g tailcall-only.c -o aarch64-tailcall-only.S

Does the aarch64- prefix still hold when compiled with -m32?


>diff --git a/gdb/testsuite/gdb.btrace/buffer-size.exp
>b/gdb/testsuite/gdb.btrace/buffer-size.exp
>index ea4e36c1593..23f859dce9c 100644
>--- a/gdb/testsuite/gdb.btrace/buffer-size.exp
>+++ b/gdb/testsuite/gdb.btrace/buffer-size.exp
>@@ -32,10 +32,17 @@ if ![runto_main] {
>     return -1
> }
>
>-gdb_test_no_output "set record btrace bts buffer-size 1"
>-gdb_test_no_output "set record btrace pt buffer-size 1"
>-gdb_test "show record btrace bts buffer-size" "The record/replay bts buffer size
>is 1\.\r"
>-gdb_test "show record btrace pt buffer-size" "The record/replay pt buffer size is
>1\.\r"
>+set btrace_type ""
>+if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
>+    set btrace_type { "bts" "pt" }
>+} elseif {[istarget "arm*-*-*"]|| [istarget "aarch64*-*-*"]} {
>+    set btrace_type { "etm" }
>+}

There shouldn't be a need for this.  The config should work for all formats, even
if the target does not support it.  We should be able to run the below loop over
all existing formats.


>diff --git a/gdb/testsuite/gdb.btrace/instruction_history.exp
>b/gdb/testsuite/gdb.btrace/instruction_history.exp
>index 403085c083f..3a6e7362f74 100644
>--- a/gdb/testsuite/gdb.btrace/instruction_history.exp
>+++ b/gdb/testsuite/gdb.btrace/instruction_history.exp
>@@ -22,7 +22,17 @@ if { [skip_btrace_tests] } {
>     return -1
> }
>
>-standard_testfile .c .S
>+set test_prefix ""
>+if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
>+    set test_prefix "x86"
>+} elseif {[istarget "arm*-*-*"]} {
>+    set test_prefix "arm"
>+} elseif {[istarget "aarch64*-*-*"]} {
>+    set test_prefix "aarch64"
>+}

We should error out to give a more meaningful error message in case
we cannot determine the prefix.


>diff --git a/gdb/testsuite/gdb.btrace/non-stop.exp
>b/gdb/testsuite/gdb.btrace/non-stop.exp
>index e509d65d660..fbc4cda7dd6 100644
>--- a/gdb/testsuite/gdb.btrace/non-stop.exp
>+++ b/gdb/testsuite/gdb.btrace/non-stop.exp
>@@ -31,6 +31,12 @@ save_vars { GDBFLAGS } {
>     clean_restart $testfile
> }
>
>+if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
>+    set loop_position 2
>+} elseif {[istarget "arm*-*-*"] || [istarget "aarch64*-*-*"]} {
>+    set loop_position 3
>+}
>+
> if ![runto_main] {
>     untested "failed to run to main"
>     return -1
>@@ -111,87 +117,99 @@ gdb_test "thread apply all info rec" ".*"
> gdb_test "info threads" ".*"
>
> with_test_prefix "navigate" {
>-    gdb_test "thread apply 1 record goto 3" "$loop_line"
>-    gdb_test "thread apply 2 record goto 4" "$loop_line"
>+    gdb_test "thread apply 1 record goto [expr {$loop_position + 1}]"
>"$loop_line"
>+    gdb_test "thread apply 2 record goto [expr {$loop_position + 2}]"
>"$loop_line"

This test is already making assumptions about the compiled code.

I'd rather we started at 4 for all architectures instead of having different
starting points.  We may also adjust the tests so we don't go beyond 12,
which seems to be working for all IA targets we're running this on.  We
can also try to stay below 10 to be on the safe side.

Could you propose something that works for ARM in v7 of the series?
I can validate that on IA, then, before this gets merged.  If we stay between
3 and 12, however, I don't see why it shouldn't continue working.


>+if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
>+    set function_positions(0) 19
>+    set function_positions(1) 27
>+    set function_positions(2) 2
>+    set function_positions(end) 40
>+    set function_positions(3) 39
>+
>+    set sequence_begin(1) 1
>+    set sequence_end(1) 1
>+    set sequence_begin(2) 2
>+    set sequence_end(2) 4

That's too cryptic.  If we cannot tweak the assembly files to work
with the same .exp, I'd rather split the latter into arch-specific .exp's.

Also for other tests below.


> if [prepare_for_testing "failed to prepare" $testfile $srcfile $opts] {
>@@ -62,6 +70,10 @@ gdb_test_no_output "set record function-call-history-size
>0"
>
> # trace foo
> gdb_test "step" ".*" "prepare for recording"
>+# make sure we get out of function epilogue
>+if { [istarget "arm*-*-*"] } {
>+  gdb_test "stepi"
>+}

We can also work with breakpoints if stepping isn't producing reliable
results.


>     lappend opts debug optimize=-O2
>-} elseif {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
>+} else {
>+  set test_prefix ""
>+  if { [istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {

Extra space between { and [.  Let's be consistent in formatting.


> if [prepare_for_testing "failed to prepare" $testfile $srcfile $opts] {
>     return -1
> }
>+
>+if {[istarget "i?86-*-*"] || [istarget "x86_64-*-*"]} {
>+    set bar_return_line 24
>+    set bar_return_position 4
>+    set main_return_line 38
>+} elseif {[istarget "arm*-*-*"]} {
>+    set bar_return_line 24
>+    set bar_return_position 5
>+    set main_return_line 41
>+    } elseif {[istarget "aarch64*-*-*"]} {
>+    set bar_return_line 23
>+    set bar_return_position 6
>+    set main_return_line 40
>+}

Why would compilers not agree on the source line of the return statement?

I can see that the "record goto 4" is causing trouble.  Can we just tweak that
number?  I.e. find another place inside the trace that produces the same bt?

We can also try to work with breakpoints again, instead of hard-coding an
instruction number.

Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 1/7] configure gdb build system for supporting btrace on arm processors
  2021-05-31 21:33 ` [PATCH v6 1/7] configure gdb build system for supporting btrace on arm processors Zied Guermazi
@ 2021-06-30 12:17   ` Luis Machado
  0 siblings, 0 replies; 35+ messages in thread
From: Luis Machado @ 2021-06-30 12:17 UTC (permalink / raw)
  To: Zied Guermazi, gdb-patches, markus.t.metzger

On 5/31/21 6:33 PM, Zied Guermazi wrote:
> This patch adds the possibility to build GDB with support for branch tracing
> using ARM CoreSight traces.
> New flag is --with-arm-cs
> 
> gdb/ChangeLog
> 
> 	* Makefile.in LIBOPENCSD_C_API: Regenerated.
> 	* config.in LIBOPENCSD_C_API: Regenerated.
> 	* configure: Regenerated.
> 	* configure.nat: add nat/linux-btrace.o to the build
> 	for aarch64 and arm
> 	* top.c (print_gdb_configuration): add --with-arm-cs
> 	or --without-arm-cs according to the configuration.
> 
> gdbserver/ChangeLog
> 	* gdbserver/configure: Regenerated.
> 	* gdbserver/config.in: Regenerated.
> 
> gdbsupport/ChangeLog
> 
> 	* gdbsupport/common.m4: check --with-arm-cs configuration
> 	flag, perf_event and opencsd_c_api to set compilation flags.
> 	* gdbsupport/config.in: Regenerated.
> 	* gdbsupport/Makefile.in: Regenerated.
> 	* gdbsupport/configure: Regenerated.
> ---
>   gdb/Makefile.in        |   5 +-
>   gdb/config.in          |   3 +
>   gdb/configure          | 549 +++++++++++++++++++++++++++++++++++++++++
>   gdb/configure.nat      |   4 +-
>   gdb/top.c              |  10 +-
>   gdbserver/config.in    |   3 +
>   gdbserver/configure    | 545 ++++++++++++++++++++++++++++++++++++++++
>   gdbsupport/Makefile.in |   3 +
>   gdbsupport/common.m4   |  39 +++
>   gdbsupport/config.in   |   3 +
>   gdbsupport/configure   | 545 ++++++++++++++++++++++++++++++++++++++++
>   11 files changed, 1705 insertions(+), 4 deletions(-)
> 
> diff --git a/gdb/Makefile.in b/gdb/Makefile.in
> index bb6c5dfa784..04680a22e5c 100644
> --- a/gdb/Makefile.in
> +++ b/gdb/Makefile.in
> @@ -203,6 +203,9 @@ LIBXXHASH = @LIBXXHASH@
>   # Where is libipt?  This will be empty if libipt was not available.
>   LIBIPT = @LIBIPT@
>   
> +#where is libopencsd? this will be empty if libopencsd was not available
> +LIBOPENCSD_C_API = @LIBOPENCSD_C_API@
> +
>   # Where is libgmp?
>   LIBGMP = @LIBGMP@
>   
> @@ -636,7 +639,7 @@ CLIBS = $(SIM) $(READLINE) $(OPCODES) $(LIBCTF) $(BFD) $(ZLIB) \
>           $(LIBSUPPORT) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \
>   	$(XM_CLIBS) $(GDBTKLIBS) \
>   	@LIBS@ @GUILE_LIBS@ @PYTHON_LIBS@ \
> -	$(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) $(LIBIPT) \
> +	$(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) $(LIBIPT) $(LIBOPENCSD_C_API) \
>   	$(WIN32LIBS) $(LIBGNU) $(LIBGNU_EXTRA_LIBS) $(LIBICONV) \
>   	$(LIBMPFR) $(LIBGMP) $(SRCHIGH_LIBS) $(LIBXXHASH) $(PTHREAD_LIBS) \
>   	$(DEBUGINFOD_LIBS)
> diff --git a/gdb/config.in b/gdb/config.in
> index 99c924f9ba0..0373689a3ce 100644
> --- a/gdb/config.in
> +++ b/gdb/config.in
> @@ -246,6 +246,9 @@
>   /* Define if you have the mpfr library. */
>   #undef HAVE_LIBMPFR
>   
> +/* Define if you have the opencsd_c_api library. */
> +#undef HAVE_LIBOPENCSD_C_API
> +
>   /* Define to 1 if you have the <libunwind-ia64.h> header file. */
>   #undef HAVE_LIBUNWIND_IA64_H
>   
> diff --git a/gdb/configure b/gdb/configure
> index cdc112e10dc..26b075a7c61 100755
> --- a/gdb/configure
> +++ b/gdb/configure
> @@ -705,6 +705,9 @@ SYSTEM_GDBINIT
>   TARGET_SYSTEM_ROOT
>   CONFIG_LDFLAGS
>   RDYNAMIC
> +LTLIBOPENCSD_C_API
> +LIBOPENCSD_C_API
> +HAVE_LIBOPENCSD_C_API
>   LTLIBIPT
>   LIBIPT
>   HAVE_LIBIPT
> @@ -911,6 +914,9 @@ enable_source_highlight
>   with_intel_pt
>   with_libipt_prefix
>   with_libipt_type
> +with_arm_cs
> +with_libopencsd_c_api_prefix
> +with_libopencsd_c_api_type
>   with_sysroot
>   with_system_gdbinit
>   with_system_gdbinit_dir
> @@ -1660,6 +1666,15 @@ Optional Packages:
>     --with-libipt-prefix[=DIR]  search for libipt in DIR/include and DIR/lib
>     --without-libipt-prefix     don't search for libipt in includedir and libdir
>     --with-libipt-type=TYPE     type of library to search for (auto/static/shared)
> +  --with-arm-cs           include ARM CoreSight Processor Trace support
> +                          (auto/yes/no)
> +  --with-libopencsd_c_api-prefix[=DIR]  search for libopencsd_c_api in DIR/include and DIR/lib
> +  --without-libopencsd_c_api-prefix     don't search for libopencsd_c_api in includedir and libdir
> +  --with-libopencsd_c_api-type=TYPE     type of library to search for (auto/static/shared)
> +  --without-included-regex
> +                          don't use included regex; this is the default on
> +                          systems with version 2 of the GNU C library (use
> +                          with caution on other system)
>     --with-sysroot[=DIR]    search for usr/lib et al within DIR
>     --with-system-gdbinit=PATH
>                             automatically load a system-wide gdbinit file
> @@ -15238,6 +15253,540 @@ fi
>       fi
>     fi
>   
> +  # ARM CoreSight trace       #
> +
> +
> +# Check whether --with-arm_cs was given.
> +if test "${with_arm_cs+set}" = set; then :
> +  withval=$with_arm_cs;
> +else
> +  with_arm_cs=auto
> +fi
> +
> +  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use ARM CoreSight Processor Trace " >&5
> +$as_echo_n "checking whether to use ARM CoreSight Processor Trace ... " >&6; }
> +  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_arm_cs" >&5
> +$as_echo "$with_arm_cs" >&6; }
> +
> +  if test "${with_arm_cs}" = no; then
> +    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&5
> +$as_echo "$as_me: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&2;}
> +    HAVE_LIBOPENCSD=no
> +  else
> +    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h.  */
> +
> +  #include <linux/perf_event.h>
> +  #ifndef PERF_ATTR_SIZE_VER5
> +  # error
> +  #endif
> +
> +_ACEOF
> +if ac_fn_c_try_cpp "$LINENO"; then :
> +  perf_event=yes
> +else
> +  perf_event=no
> +fi
> +rm -f conftest.err conftest.i conftest.$ac_ext
> +    if test "$perf_event" != yes; then
> +      if test "$with_arm_cs" = yes; then
> +        as_fn_error $? "linux/perf_event.h missing or too old" "$LINENO" 5
> +      else
> +        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&5
> +$as_echo "$as_me: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&2;}
> +      fi
> +    fi
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +    use_additional=yes
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +
> +    eval additional_includedir=\"$includedir\"
> +    eval additional_libdir=\"$libdir\"
> +
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +
> +# Check whether --with-libopencsd_c_api-prefix was given.
> +if test "${with_libopencsd_c_api_prefix+set}" = set; then :
> +  withval=$with_libopencsd_c_api_prefix;
> +    if test "X$withval" = "Xno"; then
> +      use_additional=no
> +    else
> +      if test "X$withval" = "X"; then
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +
> +          eval additional_includedir=\"$includedir\"
> +          eval additional_libdir=\"$libdir\"
> +
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +      else
> +        additional_includedir="$withval/include"
> +        additional_libdir="$withval/lib"
> +      fi
> +    fi
> +
> +fi
> +
> +
> +# Check whether --with-libopencsd_c_api-type was given.
> +if test "${with_libopencsd_c_api_type+set}" = set; then :
> +  withval=$with_libopencsd_c_api_type;  with_libopencsd_c_api_type=$withval
> +else
> +   with_libopencsd_c_api_type=auto
> +fi
> +
> +  lib_type=`eval echo \$with_libopencsd_c_api_type`
> +
> +      LIBOPENCSD_C_API=
> +  LTLIBOPENCSD_C_API=
> +  INCOPENCSD_C_API=
> +  rpathdirs=
> +  ltrpathdirs=
> +  names_already_handled=
> +  names_next_round='opencsd_c_api '
> +  while test -n "$names_next_round"; do
> +    names_this_round="$names_next_round"
> +    names_next_round=
> +    for name in $names_this_round; do
> +      already_handled=
> +      for n in $names_already_handled; do
> +        if test "$n" = "$name"; then
> +          already_handled=yes
> +          break
> +        fi
> +      done
> +      if test -z "$already_handled"; then
> +        names_already_handled="$names_already_handled $name"
> +                        uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
> +        eval value=\"\$HAVE_LIB$uppername\"
> +        if test -n "$value"; then
> +          if test "$value" = yes; then
> +            eval value=\"\$LIB$uppername\"
> +            test -z "$value" || LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$value"
> +            eval value=\"\$LTLIB$uppername\"
> +            test -z "$value" || LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$value"
> +          else
> +                                    :
> +          fi
> +        else
> +                              found_dir=
> +          found_la=
> +          found_so=
> +          found_a=
> +          if test $use_additional = yes; then
> +            if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext" && test x$lib_type != xstatic; then
> +              found_dir="$additional_libdir"
> +              found_so="$additional_libdir/lib$name.$shlibext"
> +              if test -f "$additional_libdir/lib$name.la"; then
> +                found_la="$additional_libdir/lib$name.la"
> +              fi
> +            elif test x$lib_type != xshared; then
> +              if test -f "$additional_libdir/lib$name.$libext"; then
> +                found_dir="$additional_libdir"
> +                found_a="$additional_libdir/lib$name.$libext"
> +                if test -f "$additional_libdir/lib$name.la"; then
> +                  found_la="$additional_libdir/lib$name.la"
> +                fi
> +              fi
> +            fi
> +          fi
> +          if test "X$found_dir" = "X"; then
> +            for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +              case "$x" in
> +                -L*)
> +                  dir=`echo "X$x" | sed -e 's/^X-L//'`
> +                  if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext" && test x$lib_type != xstatic; then
> +                    found_dir="$dir"
> +                    found_so="$dir/lib$name.$shlibext"
> +                    if test -f "$dir/lib$name.la"; then
> +                      found_la="$dir/lib$name.la"
> +                    fi
> +                  elif test x$lib_type != xshared; then
> +                    if test -f "$dir/lib$name.$libext"; then
> +                      found_dir="$dir"
> +                      found_a="$dir/lib$name.$libext"
> +                      if test -f "$dir/lib$name.la"; then
> +                        found_la="$dir/lib$name.la"
> +                      fi
> +                    fi
> +                  fi
> +                  ;;
> +              esac
> +              if test "X$found_dir" != "X"; then
> +                break
> +              fi
> +            done
> +          fi
> +          if test "X$found_dir" != "X"; then
> +                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$found_dir -l$name"
> +            if test "X$found_so" != "X"; then
> +                                                        if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
> +                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +              else
> +                                                                                haveit=
> +                for x in $ltrpathdirs; do
> +                  if test "X$x" = "X$found_dir"; then
> +                    haveit=yes
> +                    break
> +                  fi
> +                done
> +                if test -z "$haveit"; then
> +                  ltrpathdirs="$ltrpathdirs $found_dir"
> +                fi
> +                                if test "$hardcode_direct" = yes; then
> +                                                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +                else
> +                  if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
> +                                                            LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +                                                            haveit=
> +                    for x in $rpathdirs; do
> +                      if test "X$x" = "X$found_dir"; then
> +                        haveit=yes
> +                        break
> +                      fi
> +                    done
> +                    if test -z "$haveit"; then
> +                      rpathdirs="$rpathdirs $found_dir"
> +                    fi
> +                  else
> +                                                                                haveit=
> +                    for x in $LDFLAGS $LIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                      if test "X$x" = "X-L$found_dir"; then
> +                        haveit=yes
> +                        break
> +                      fi
> +                    done
> +                    if test -z "$haveit"; then
> +                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir"
> +                    fi
> +                    if test "$hardcode_minus_L" != no; then
> +                                                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +                    else
> +                                                                                                                                                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
> +                    fi
> +                  fi
> +                fi
> +              fi
> +            else
> +              if test "X$found_a" != "X"; then
> +                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_a"
> +              else
> +                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir -l$name"
> +              fi
> +            fi
> +                        additional_includedir=
> +            case "$found_dir" in
> +              */lib | */lib/)
> +                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
> +                additional_includedir="$basedir/include"
> +                ;;
> +            esac
> +            if test "X$additional_includedir" != "X"; then
> +                                                                                                                if test "X$additional_includedir" != "X/usr/include"; then
> +                haveit=
> +                if test "X$additional_includedir" = "X/usr/local/include"; then
> +                  if test -n "$GCC"; then
> +                    case $host_os in
> +                      linux*) haveit=yes;;
> +                    esac
> +                  fi
> +                fi
> +                if test -z "$haveit"; then
> +                  for x in $CPPFLAGS $INCOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                    if test "X$x" = "X-I$additional_includedir"; then
> +                      haveit=yes
> +                      break
> +                    fi
> +                  done
> +                  if test -z "$haveit"; then
> +                    if test -d "$additional_includedir"; then
> +                                            INCOPENCSD_C_API="${INCOPENCSD_C_API}${INCOPENCSD_C_API:+ }-I$additional_includedir"
> +                    fi
> +                  fi
> +                fi
> +              fi
> +            fi
> +                        if test -n "$found_la"; then
> +                                                        save_libdir="$libdir"
> +              case "$found_la" in
> +                */* | *\\*) . "$found_la" ;;
> +                *) . "./$found_la" ;;
> +              esac
> +              libdir="$save_libdir"
> +                            for dep in $dependency_libs; do
> +                case "$dep" in
> +                  -L*)
> +                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
> +                                                                                                                                                                if test "X$additional_libdir" != "X/usr/lib"; then
> +                      haveit=
> +                      if test "X$additional_libdir" = "X/usr/local/lib"; then
> +                        if test -n "$GCC"; then
> +                          case $host_os in
> +                            linux*) haveit=yes;;
> +                          esac
> +                        fi
> +                      fi
> +                      if test -z "$haveit"; then
> +                        haveit=
> +                        for x in $LDFLAGS $LIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                          if test "X$x" = "X-L$additional_libdir"; then
> +                            haveit=yes
> +                            break
> +                          fi
> +                        done
> +                        if test -z "$haveit"; then
> +                          if test -d "$additional_libdir"; then
> +                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$additional_libdir"
> +                          fi
> +                        fi
> +                        haveit=
> +                        for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                          if test "X$x" = "X-L$additional_libdir"; then
> +                            haveit=yes
> +                            break
> +                          fi
> +                        done
> +                        if test -z "$haveit"; then
> +                          if test -d "$additional_libdir"; then
> +                                                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$additional_libdir"
> +                          fi
> +                        fi
> +                      fi
> +                    fi
> +                    ;;
> +                  -R*)
> +                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
> +                    if test "$enable_rpath" != no; then
> +                                                                  haveit=
> +                      for x in $rpathdirs; do
> +                        if test "X$x" = "X$dir"; then
> +                          haveit=yes
> +                          break
> +                        fi
> +                      done
> +                      if test -z "$haveit"; then
> +                        rpathdirs="$rpathdirs $dir"
> +                      fi
> +                                                                  haveit=
> +                      for x in $ltrpathdirs; do
> +                        if test "X$x" = "X$dir"; then
> +                          haveit=yes
> +                          break
> +                        fi
> +                      done
> +                      if test -z "$haveit"; then
> +                        ltrpathdirs="$ltrpathdirs $dir"
> +                      fi
> +                    fi
> +                    ;;
> +                  -l*)
> +                                        names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
> +                    ;;
> +                  *.la)
> +                                                                                names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
> +                    ;;
> +                  *)
> +                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$dep"
> +                    LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$dep"
> +                    ;;
> +                esac
> +              done
> +            fi
> +          else
> +                                                            if test "x$lib_type" = "xauto" || test "x$lib_type" = "xshared"; then
> +              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
> +              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l$name"
> +            else
> +              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l:lib$name.$libext"
> +              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l:lib$name.$libext"
> +            fi
> +          fi
> +        fi
> +      fi
> +    done
> +  done
> +  if test "X$rpathdirs" != "X"; then
> +    if test -n "$hardcode_libdir_separator"; then
> +                        alldirs=
> +      for found_dir in $rpathdirs; do
> +        alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
> +      done
> +            acl_save_libdir="$libdir"
> +      libdir="$alldirs"
> +      eval flag=\"$hardcode_libdir_flag_spec\"
> +      libdir="$acl_save_libdir"
> +      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
> +    else
> +            for found_dir in $rpathdirs; do
> +        acl_save_libdir="$libdir"
> +        libdir="$found_dir"
> +        eval flag=\"$hardcode_libdir_flag_spec\"
> +        libdir="$acl_save_libdir"
> +        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
> +      done
> +    fi
> +  fi
> +  if test "X$ltrpathdirs" != "X"; then
> +            for found_dir in $ltrpathdirs; do
> +      LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-R$found_dir"
> +    done
> +  fi
> +
> +
> +        ac_save_CPPFLAGS="$CPPFLAGS"
> +
> +  for element in $INCOPENCSD_C_API; do
> +    haveit=
> +    for x in $CPPFLAGS; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +      if test "X$x" = "X$element"; then
> +        haveit=yes
> +        break
> +      fi
> +    done
> +    if test -z "$haveit"; then
> +      CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
> +    fi
> +  done
> +
> +
> +  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libopencsd_c_api" >&5
> +$as_echo_n "checking for libopencsd_c_api... " >&6; }
> +if ${ac_cv_libopencsd_c_api+:} false; then :
> +  $as_echo_n "(cached) " >&6
> +else
> +
> +    ac_save_LIBS="$LIBS"
> +    LIBS="$LIBS $LIBOPENCSD_C_API"
> +    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h.  */
> +#include "opencsd/c_api/opencsd_c_api.h"
> +int
> +main ()
> +{
> +ocsd_get_version();
> +  ;
> +  return 0;
> +}
> +_ACEOF
> +if ac_fn_c_try_link "$LINENO"; then :
> +  ac_cv_libopencsd_c_api=yes
> +else
> +  ac_cv_libopencsd_c_api=no
> +fi
> +rm -f core conftest.err conftest.$ac_objext \
> +    conftest$ac_exeext conftest.$ac_ext
> +    LIBS="$ac_save_LIBS"
> +
> +fi
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libopencsd_c_api" >&5
> +$as_echo "$ac_cv_libopencsd_c_api" >&6; }
> +  if test "$ac_cv_libopencsd_c_api" = yes; then
> +    HAVE_LIBOPENCSD_C_API=yes
> +
> +$as_echo "#define HAVE_LIBOPENCSD_C_API 1" >>confdefs.h
> +
> +    { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libopencsd_c_api" >&5
> +$as_echo_n "checking how to link with libopencsd_c_api... " >&6; }
> +    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBOPENCSD_C_API" >&5
> +$as_echo "$LIBOPENCSD_C_API" >&6; }
> +  else
> +    HAVE_LIBOPENCSD_C_API=no
> +            CPPFLAGS="$ac_save_CPPFLAGS"
> +    LIBOPENCSD_C_API=
> +    LTLIBOPENCSD_C_API=
> +  fi
> +
> +
> +
> +
> +
> +
> +    if test "$HAVE_LIBOPENCSD_C_API" != yes; then
> +      if test "$with_arm_cs" = yes; then
> +        as_fn_error $? "libopencsd_c_api is missing or unusable" "$LINENO" 5
> +      else
> +        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&5
> +$as_echo "$as_me: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&2;}
> +      fi
> +    else
> +      LIBS="$LIBS $LIBOPENCSD_C_API"
> +    fi
> +  fi
> +
> +
>   
>   $as_echo "#define _STRUCTURED_PROC 1" >>confdefs.h
>   
> diff --git a/gdb/configure.nat b/gdb/configure.nat
> index e34cccffd98..135041bd031 100644
> --- a/gdb/configure.nat
> +++ b/gdb/configure.nat
> @@ -235,7 +235,7 @@ case ${gdb_host} in
>   		#  Host: AArch64 based machine running GNU/Linux
>   		NATDEPFILES="${NATDEPFILES} aarch64-linux-nat.o \
>   		aarch32-linux-nat.o nat/aarch64-linux-hw-point.o \
> -		nat/aarch64-linux.o \
> +		nat/aarch64-linux.o nat/linux-btrace.o \
>   		nat/aarch64-sve-linux-ptrace.o \
>   		nat/aarch64-mte-linux-ptrace.o"
>   		;;
> @@ -246,7 +246,7 @@ case ${gdb_host} in
>   	    arm)
>   		# Host: ARM based machine running GNU/Linux
>   		NATDEPFILES="${NATDEPFILES} arm-linux-nat.o \
> -		aarch32-linux-nat.o"
> +		aarch32-linux-nat.o nat/linux-btrace.o"
>   		;;
>   	    i386)
>   		# Host: Intel 386 running GNU/Linux.
> diff --git a/gdb/top.c b/gdb/top.c
> index 6e0f43d2fd9..53f04ee29af 100644
> --- a/gdb/top.c
> +++ b/gdb/top.c
> @@ -1538,7 +1538,15 @@ This GDB was configured as follows:\n\
>   	     --without-intel-pt\n\
>   "));
>   #endif
> -
> +#if HAVE_LIBOPENCSD_C_API
> +    fprintf_filtered (stream, _("\
> +             --with-arm-cs\n\
> +"));
> +#else
> +    fprintf_filtered (stream, _("\
> +             --without-arm-cs\n\
> +"));
> +#endif
>   #if HAVE_LIBMPFR
>     fprintf_filtered (stream, _("\
>   	     --with-mpfr\n\
> diff --git a/gdbserver/config.in b/gdbserver/config.in
> index 611bfd7aa76..fbcdf896ef7 100644
> --- a/gdbserver/config.in
> +++ b/gdbserver/config.in
> @@ -139,6 +139,9 @@
>   /* Define if you have the ipt library. */
>   #undef HAVE_LIBIPT
>   
> +/* Define if you have the opencsd_c_api library. */
> +#undef HAVE_LIBOPENCSD_C_API
> +
>   /* Define if the target supports branch tracing. */
>   #undef HAVE_LINUX_BTRACE
>   
> diff --git a/gdbserver/configure b/gdbserver/configure
> index aab72c0b8c5..e8265116c8c 100755
> --- a/gdbserver/configure
> +++ b/gdbserver/configure
> @@ -656,6 +656,9 @@ am__leading_dot
>   host_noncanonical
>   target_noncanonical
>   WIN32APILIBS
> +LTLIBOPENCSD_C_API
> +LIBOPENCSD_C_API
> +HAVE_LIBOPENCSD_C_API
>   LTLIBIPT
>   LIBIPT
>   HAVE_LIBIPT
> @@ -751,6 +754,9 @@ with_gnu_ld
>   enable_rpath
>   with_libipt_prefix
>   with_libipt_type
> +with_arm_cs
> +with_libopencsd_c_api_prefix
> +with_libopencsd_c_api_type
>   enable_unit_tests
>   with_ust
>   with_ust_include
> @@ -1415,6 +1421,11 @@ Optional Packages:
>     --with-libipt-prefix[=DIR]  search for libipt in DIR/include and DIR/lib
>     --without-libipt-prefix     don't search for libipt in includedir and libdir
>     --with-libipt-type=TYPE     type of library to search for (auto/static/shared)
> +  --with-arm-cs           include ARM CoreSight Processor Trace support
> +                          (auto/yes/no)
> +  --with-libopencsd_c_api-prefix[=DIR]  search for libopencsd_c_api in DIR/include and DIR/lib
> +  --without-libopencsd_c_api-prefix     don't search for libopencsd_c_api in includedir and libdir
> +  --with-libopencsd_c_api-type=TYPE     type of library to search for (auto/static/shared)
>     --with-ust=PATH       Specify prefix directory for the installed UST package
>                             Equivalent to --with-ust-include=PATH/include
>                             plus --with-ust-lib=PATH/lib
> @@ -8549,6 +8560,540 @@ fi
>       fi
>     fi
>   
> +  # ARM CoreSight trace       #
> +
> +
> +# Check whether --with-arm_cs was given.
> +if test "${with_arm_cs+set}" = set; then :
> +  withval=$with_arm_cs;
> +else
> +  with_arm_cs=auto
> +fi
> +
> +  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use ARM CoreSight Processor Trace " >&5
> +$as_echo_n "checking whether to use ARM CoreSight Processor Trace ... " >&6; }
> +  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_arm_cs" >&5
> +$as_echo "$with_arm_cs" >&6; }
> +
> +  if test "${with_arm_cs}" = no; then
> +    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&5
> +$as_echo "$as_me: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&2;}
> +    HAVE_LIBOPENCSD=no
> +  else
> +    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h.  */
> +
> +  #include <linux/perf_event.h>
> +  #ifndef PERF_ATTR_SIZE_VER5
> +  # error
> +  #endif
> +
> +_ACEOF
> +if ac_fn_c_try_cpp "$LINENO"; then :
> +  perf_event=yes
> +else
> +  perf_event=no
> +fi
> +rm -f conftest.err conftest.i conftest.$ac_ext
> +    if test "$perf_event" != yes; then
> +      if test "$with_arm_cs" = yes; then
> +        as_fn_error $? "linux/perf_event.h missing or too old" "$LINENO" 5
> +      else
> +        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&5
> +$as_echo "$as_me: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&2;}
> +      fi
> +    fi
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +    use_additional=yes
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +
> +    eval additional_includedir=\"$includedir\"
> +    eval additional_libdir=\"$libdir\"
> +
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +
> +# Check whether --with-libopencsd_c_api-prefix was given.
> +if test "${with_libopencsd_c_api_prefix+set}" = set; then :
> +  withval=$with_libopencsd_c_api_prefix;
> +    if test "X$withval" = "Xno"; then
> +      use_additional=no
> +    else
> +      if test "X$withval" = "X"; then
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +
> +          eval additional_includedir=\"$includedir\"
> +          eval additional_libdir=\"$libdir\"
> +
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +      else
> +        additional_includedir="$withval/include"
> +        additional_libdir="$withval/lib"
> +      fi
> +    fi
> +
> +fi
> +
> +
> +# Check whether --with-libopencsd_c_api-type was given.
> +if test "${with_libopencsd_c_api_type+set}" = set; then :
> +  withval=$with_libopencsd_c_api_type;  with_libopencsd_c_api_type=$withval
> +else
> +   with_libopencsd_c_api_type=auto
> +fi
> +
> +  lib_type=`eval echo \$with_libopencsd_c_api_type`
> +
> +      LIBOPENCSD_C_API=
> +  LTLIBOPENCSD_C_API=
> +  INCOPENCSD_C_API=
> +  rpathdirs=
> +  ltrpathdirs=
> +  names_already_handled=
> +  names_next_round='opencsd_c_api '
> +  while test -n "$names_next_round"; do
> +    names_this_round="$names_next_round"
> +    names_next_round=
> +    for name in $names_this_round; do
> +      already_handled=
> +      for n in $names_already_handled; do
> +        if test "$n" = "$name"; then
> +          already_handled=yes
> +          break
> +        fi
> +      done
> +      if test -z "$already_handled"; then
> +        names_already_handled="$names_already_handled $name"
> +                        uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
> +        eval value=\"\$HAVE_LIB$uppername\"
> +        if test -n "$value"; then
> +          if test "$value" = yes; then
> +            eval value=\"\$LIB$uppername\"
> +            test -z "$value" || LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$value"
> +            eval value=\"\$LTLIB$uppername\"
> +            test -z "$value" || LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$value"
> +          else
> +                                    :
> +          fi
> +        else
> +                              found_dir=
> +          found_la=
> +          found_so=
> +          found_a=
> +          if test $use_additional = yes; then
> +            if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext" && test x$lib_type != xstatic; then
> +              found_dir="$additional_libdir"
> +              found_so="$additional_libdir/lib$name.$shlibext"
> +              if test -f "$additional_libdir/lib$name.la"; then
> +                found_la="$additional_libdir/lib$name.la"
> +              fi
> +            elif test x$lib_type != xshared; then
> +              if test -f "$additional_libdir/lib$name.$libext"; then
> +                found_dir="$additional_libdir"
> +                found_a="$additional_libdir/lib$name.$libext"
> +                if test -f "$additional_libdir/lib$name.la"; then
> +                  found_la="$additional_libdir/lib$name.la"
> +                fi
> +              fi
> +            fi
> +          fi
> +          if test "X$found_dir" = "X"; then
> +            for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +              case "$x" in
> +                -L*)
> +                  dir=`echo "X$x" | sed -e 's/^X-L//'`
> +                  if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext" && test x$lib_type != xstatic; then
> +                    found_dir="$dir"
> +                    found_so="$dir/lib$name.$shlibext"
> +                    if test -f "$dir/lib$name.la"; then
> +                      found_la="$dir/lib$name.la"
> +                    fi
> +                  elif test x$lib_type != xshared; then
> +                    if test -f "$dir/lib$name.$libext"; then
> +                      found_dir="$dir"
> +                      found_a="$dir/lib$name.$libext"
> +                      if test -f "$dir/lib$name.la"; then
> +                        found_la="$dir/lib$name.la"
> +                      fi
> +                    fi
> +                  fi
> +                  ;;
> +              esac
> +              if test "X$found_dir" != "X"; then
> +                break
> +              fi
> +            done
> +          fi
> +          if test "X$found_dir" != "X"; then
> +                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$found_dir -l$name"
> +            if test "X$found_so" != "X"; then
> +                                                        if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
> +                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +              else
> +                                                                                haveit=
> +                for x in $ltrpathdirs; do
> +                  if test "X$x" = "X$found_dir"; then
> +                    haveit=yes
> +                    break
> +                  fi
> +                done
> +                if test -z "$haveit"; then
> +                  ltrpathdirs="$ltrpathdirs $found_dir"
> +                fi
> +                                if test "$hardcode_direct" = yes; then
> +                                                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +                else
> +                  if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
> +                                                            LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +                                                            haveit=
> +                    for x in $rpathdirs; do
> +                      if test "X$x" = "X$found_dir"; then
> +                        haveit=yes
> +                        break
> +                      fi
> +                    done
> +                    if test -z "$haveit"; then
> +                      rpathdirs="$rpathdirs $found_dir"
> +                    fi
> +                  else
> +                                                                                haveit=
> +                    for x in $LDFLAGS $LIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                      if test "X$x" = "X-L$found_dir"; then
> +                        haveit=yes
> +                        break
> +                      fi
> +                    done
> +                    if test -z "$haveit"; then
> +                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir"
> +                    fi
> +                    if test "$hardcode_minus_L" != no; then
> +                                                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +                    else
> +                                                                                                                                                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
> +                    fi
> +                  fi
> +                fi
> +              fi
> +            else
> +              if test "X$found_a" != "X"; then
> +                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_a"
> +              else
> +                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir -l$name"
> +              fi
> +            fi
> +                        additional_includedir=
> +            case "$found_dir" in
> +              */lib | */lib/)
> +                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
> +                additional_includedir="$basedir/include"
> +                ;;
> +            esac
> +            if test "X$additional_includedir" != "X"; then
> +                                                                                                                if test "X$additional_includedir" != "X/usr/include"; then
> +                haveit=
> +                if test "X$additional_includedir" = "X/usr/local/include"; then
> +                  if test -n "$GCC"; then
> +                    case $host_os in
> +                      linux*) haveit=yes;;
> +                    esac
> +                  fi
> +                fi
> +                if test -z "$haveit"; then
> +                  for x in $CPPFLAGS $INCOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                    if test "X$x" = "X-I$additional_includedir"; then
> +                      haveit=yes
> +                      break
> +                    fi
> +                  done
> +                  if test -z "$haveit"; then
> +                    if test -d "$additional_includedir"; then
> +                                            INCOPENCSD_C_API="${INCOPENCSD_C_API}${INCOPENCSD_C_API:+ }-I$additional_includedir"
> +                    fi
> +                  fi
> +                fi
> +              fi
> +            fi
> +                        if test -n "$found_la"; then
> +                                                        save_libdir="$libdir"
> +              case "$found_la" in
> +                */* | *\\*) . "$found_la" ;;
> +                *) . "./$found_la" ;;
> +              esac
> +              libdir="$save_libdir"
> +                            for dep in $dependency_libs; do
> +                case "$dep" in
> +                  -L*)
> +                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
> +                                                                                                                                                                if test "X$additional_libdir" != "X/usr/lib"; then
> +                      haveit=
> +                      if test "X$additional_libdir" = "X/usr/local/lib"; then
> +                        if test -n "$GCC"; then
> +                          case $host_os in
> +                            linux*) haveit=yes;;
> +                          esac
> +                        fi
> +                      fi
> +                      if test -z "$haveit"; then
> +                        haveit=
> +                        for x in $LDFLAGS $LIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                          if test "X$x" = "X-L$additional_libdir"; then
> +                            haveit=yes
> +                            break
> +                          fi
> +                        done
> +                        if test -z "$haveit"; then
> +                          if test -d "$additional_libdir"; then
> +                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$additional_libdir"
> +                          fi
> +                        fi
> +                        haveit=
> +                        for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                          if test "X$x" = "X-L$additional_libdir"; then
> +                            haveit=yes
> +                            break
> +                          fi
> +                        done
> +                        if test -z "$haveit"; then
> +                          if test -d "$additional_libdir"; then
> +                                                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$additional_libdir"
> +                          fi
> +                        fi
> +                      fi
> +                    fi
> +                    ;;
> +                  -R*)
> +                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
> +                    if test "$enable_rpath" != no; then
> +                                                                  haveit=
> +                      for x in $rpathdirs; do
> +                        if test "X$x" = "X$dir"; then
> +                          haveit=yes
> +                          break
> +                        fi
> +                      done
> +                      if test -z "$haveit"; then
> +                        rpathdirs="$rpathdirs $dir"
> +                      fi
> +                                                                  haveit=
> +                      for x in $ltrpathdirs; do
> +                        if test "X$x" = "X$dir"; then
> +                          haveit=yes
> +                          break
> +                        fi
> +                      done
> +                      if test -z "$haveit"; then
> +                        ltrpathdirs="$ltrpathdirs $dir"
> +                      fi
> +                    fi
> +                    ;;
> +                  -l*)
> +                                        names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
> +                    ;;
> +                  *.la)
> +                                                                                names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
> +                    ;;
> +                  *)
> +                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$dep"
> +                    LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$dep"
> +                    ;;
> +                esac
> +              done
> +            fi
> +          else
> +                                                            if test "x$lib_type" = "xauto" || test "x$lib_type" = "xshared"; then
> +              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
> +              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l$name"
> +            else
> +              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l:lib$name.$libext"
> +              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l:lib$name.$libext"
> +            fi
> +          fi
> +        fi
> +      fi
> +    done
> +  done
> +  if test "X$rpathdirs" != "X"; then
> +    if test -n "$hardcode_libdir_separator"; then
> +                        alldirs=
> +      for found_dir in $rpathdirs; do
> +        alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
> +      done
> +            acl_save_libdir="$libdir"
> +      libdir="$alldirs"
> +      eval flag=\"$hardcode_libdir_flag_spec\"
> +      libdir="$acl_save_libdir"
> +      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
> +    else
> +            for found_dir in $rpathdirs; do
> +        acl_save_libdir="$libdir"
> +        libdir="$found_dir"
> +        eval flag=\"$hardcode_libdir_flag_spec\"
> +        libdir="$acl_save_libdir"
> +        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
> +      done
> +    fi
> +  fi
> +  if test "X$ltrpathdirs" != "X"; then
> +            for found_dir in $ltrpathdirs; do
> +      LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-R$found_dir"
> +    done
> +  fi
> +
> +
> +        ac_save_CPPFLAGS="$CPPFLAGS"
> +
> +  for element in $INCOPENCSD_C_API; do
> +    haveit=
> +    for x in $CPPFLAGS; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +      if test "X$x" = "X$element"; then
> +        haveit=yes
> +        break
> +      fi
> +    done
> +    if test -z "$haveit"; then
> +      CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
> +    fi
> +  done
> +
> +
> +  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libopencsd_c_api" >&5
> +$as_echo_n "checking for libopencsd_c_api... " >&6; }
> +if ${ac_cv_libopencsd_c_api+:} false; then :
> +  $as_echo_n "(cached) " >&6
> +else
> +
> +    ac_save_LIBS="$LIBS"
> +    LIBS="$LIBS $LIBOPENCSD_C_API"
> +    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h.  */
> +#include "opencsd/c_api/opencsd_c_api.h"
> +int
> +main ()
> +{
> +ocsd_get_version();
> +  ;
> +  return 0;
> +}
> +_ACEOF
> +if ac_fn_c_try_link "$LINENO"; then :
> +  ac_cv_libopencsd_c_api=yes
> +else
> +  ac_cv_libopencsd_c_api=no
> +fi
> +rm -f core conftest.err conftest.$ac_objext \
> +    conftest$ac_exeext conftest.$ac_ext
> +    LIBS="$ac_save_LIBS"
> +
> +fi
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libopencsd_c_api" >&5
> +$as_echo "$ac_cv_libopencsd_c_api" >&6; }
> +  if test "$ac_cv_libopencsd_c_api" = yes; then
> +    HAVE_LIBOPENCSD_C_API=yes
> +
> +$as_echo "#define HAVE_LIBOPENCSD_C_API 1" >>confdefs.h
> +
> +    { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libopencsd_c_api" >&5
> +$as_echo_n "checking how to link with libopencsd_c_api... " >&6; }
> +    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBOPENCSD_C_API" >&5
> +$as_echo "$LIBOPENCSD_C_API" >&6; }
> +  else
> +    HAVE_LIBOPENCSD_C_API=no
> +            CPPFLAGS="$ac_save_CPPFLAGS"
> +    LIBOPENCSD_C_API=
> +    LTLIBOPENCSD_C_API=
> +  fi
> +
> +
> +
> +
> +
> +
> +    if test "$HAVE_LIBOPENCSD_C_API" != yes; then
> +      if test "$with_arm_cs" = yes; then
> +        as_fn_error $? "libopencsd_c_api is missing or unusable" "$LINENO" 5
> +      else
> +        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&5
> +$as_echo "$as_me: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&2;}
> +      fi
> +    else
> +      LIBS="$LIBS $LIBOPENCSD_C_API"
> +    fi
> +  fi
> +
> +
>   
>   $as_echo "#define _STRUCTURED_PROC 1" >>confdefs.h
>   
> diff --git a/gdbsupport/Makefile.in b/gdbsupport/Makefile.in
> index d7f2d4914b0..cdb7ba2f0ac 100644
> --- a/gdbsupport/Makefile.in
> +++ b/gdbsupport/Makefile.in
> @@ -250,6 +250,7 @@ GMSGFMT = @GMSGFMT@
>   GREP = @GREP@
>   HAVE_CXX11 = @HAVE_CXX11@
>   HAVE_LIBIPT = @HAVE_LIBIPT@
> +HAVE_LIBOPENCSD_C_API = @HAVE_LIBOPENCSD_C_API@
>   INCINTL = @INCINTL@
>   INSTALL = @INSTALL@
>   INSTALL_DATA = @INSTALL_DATA@
> @@ -263,9 +264,11 @@ LIBINTL = @LIBINTL@
>   LIBINTL_DEP = @LIBINTL_DEP@
>   LIBIPT = @LIBIPT@
>   LIBOBJS = @LIBOBJS@
> +LIBOPENCSD_C_API = @LIBOPENCSD_C_API@
>   LIBS = @LIBS@
>   LTLIBIPT = @LTLIBIPT@
>   LTLIBOBJS = @LTLIBOBJS@
> +LTLIBOPENCSD_C_API = @LTLIBOPENCSD_C_API@
>   MAINT = @MAINT@
>   MAKEINFO = @MAKEINFO@
>   MKDIR_P = @MKDIR_P@
> diff --git a/gdbsupport/common.m4 b/gdbsupport/common.m4
> index 2e709dbbdbb..856fb9bc31b 100644
> --- a/gdbsupport/common.m4
> +++ b/gdbsupport/common.m4
> @@ -163,6 +163,45 @@ AC_DEFUN([GDB_AC_COMMON], [
>       fi
>     fi
>   
> +  # ARM CoreSight trace       #
> +
> +  AC_ARG_WITH(arm_cs,
> +    AS_HELP_STRING([--with-arm-cs], [include ARM CoreSight Processor Trace support (auto/yes/no)]),
> +    [], [with_arm_cs=auto])
> +  AC_MSG_CHECKING([whether to use ARM CoreSight Processor Trace ])
> +  AC_MSG_RESULT([$with_arm_cs])
> +
> +  if test "${with_arm_cs}" = no; then
> +    AC_MSG_WARN([ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable.])
> +    HAVE_LIBOPENCSD=no
> +  else
> +    AC_PREPROC_IFELSE([AC_LANG_SOURCE([[
> +  #include <linux/perf_event.h>
> +  #ifndef PERF_ATTR_SIZE_VER5
> +  # error
> +  #endif
> +    ]])], [perf_event=yes], [perf_event=no])
> +    if test "$perf_event" != yes; then
> +      if test "$with_arm_cs" = yes; then
> +        AC_MSG_ERROR([linux/perf_event.h missing or too old])
> +      else
> +        AC_MSG_WARN([linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable.])
> +      fi
> +    fi
> +
> +    AC_LIB_HAVE_LINKFLAGS([opencsd_c_api], [], [#include "opencsd/c_api/opencsd_c_api.h"], [ocsd_get_version();])
> +    if test "$HAVE_LIBOPENCSD_C_API" != yes; then
> +      if test "$with_arm_cs" = yes; then
> +        AC_MSG_ERROR([libopencsd_c_api is missing or unusable])
> +      else
> +        AC_MSG_WARN([libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable.])
> +      fi
> +    else
> +      LIBS="$LIBS $LIBOPENCSD_C_API"
> +    fi
> +  fi
> +
> +
>     BFD_SYS_PROCFS_H
>     if test "$ac_cv_header_sys_procfs_h" = yes; then
>       BFD_HAVE_SYS_PROCFS_TYPE(gregset_t)
> diff --git a/gdbsupport/config.in b/gdbsupport/config.in
> index c44a2a1e5de..2259ada7fdb 100644
> --- a/gdbsupport/config.in
> +++ b/gdbsupport/config.in
> @@ -117,6 +117,9 @@
>   /* Define if you have the ipt library. */
>   #undef HAVE_LIBIPT
>   
> +/* Define if you have the opencsd_c_api library. */
> +#undef HAVE_LIBOPENCSD_C_API
> +
>   /* Define to 1 if you have the <linux/elf.h> header file. */
>   #undef HAVE_LINUX_ELF_H
>   
> diff --git a/gdbsupport/configure b/gdbsupport/configure
> index 60643c80b5a..08bc0e6a86d 100755
> --- a/gdbsupport/configure
> +++ b/gdbsupport/configure
> @@ -628,6 +628,9 @@ WERROR_CFLAGS
>   WARN_CFLAGS
>   SELFTEST_FALSE
>   SELFTEST_TRUE
> +LTLIBOPENCSD_C_API
> +LIBOPENCSD_C_API
> +HAVE_LIBOPENCSD_C_API
>   LTLIBIPT
>   LIBIPT
>   HAVE_LIBIPT
> @@ -772,6 +775,9 @@ with_gnu_ld
>   enable_rpath
>   with_libipt_prefix
>   with_libipt_type
> +with_arm_cs
> +with_libopencsd_c_api_prefix
> +with_libopencsd_c_api_type
>   enable_unit_tests
>   enable_werror
>   enable_build_warnings
> @@ -1436,6 +1442,11 @@ Optional Packages:
>     --with-libipt-prefix[=DIR]  search for libipt in DIR/include and DIR/lib
>     --without-libipt-prefix     don't search for libipt in includedir and libdir
>     --with-libipt-type=TYPE     type of library to search for (auto/static/shared)
> +  --with-arm-cs           include ARM CoreSight Processor Trace support
> +                          (auto/yes/no)
> +  --with-libopencsd_c_api-prefix[=DIR]  search for libopencsd_c_api in DIR/include and DIR/lib
> +  --without-libopencsd_c_api-prefix     don't search for libopencsd_c_api in includedir and libdir
> +  --with-libopencsd_c_api-type=TYPE     type of library to search for (auto/static/shared)
>   
>   Some influential environment variables:
>     CC          C compiler command
> @@ -9563,6 +9574,540 @@ fi
>       fi
>     fi
>   
> +  # ARM CoreSight trace       #
> +
> +
> +# Check whether --with-arm_cs was given.
> +if test "${with_arm_cs+set}" = set; then :
> +  withval=$with_arm_cs;
> +else
> +  with_arm_cs=auto
> +fi
> +
> +  { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use ARM CoreSight Processor Trace " >&5
> +$as_echo_n "checking whether to use ARM CoreSight Processor Trace ... " >&6; }
> +  { $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_arm_cs" >&5
> +$as_echo "$with_arm_cs" >&6; }
> +
> +  if test "${with_arm_cs}" = no; then
> +    { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&5
> +$as_echo "$as_me: WARNING: ARM CoreSight Processor Trace support disabled; CoreSight Tracing will be unavailable." >&2;}
> +    HAVE_LIBOPENCSD=no
> +  else
> +    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h.  */
> +
> +  #include <linux/perf_event.h>
> +  #ifndef PERF_ATTR_SIZE_VER5
> +  # error
> +  #endif
> +
> +_ACEOF
> +if ac_fn_c_try_cpp "$LINENO"; then :
> +  perf_event=yes
> +else
> +  perf_event=no
> +fi
> +rm -f conftest.err conftest.i conftest.$ac_ext
> +    if test "$perf_event" != yes; then
> +      if test "$with_arm_cs" = yes; then
> +        as_fn_error $? "linux/perf_event.h missing or too old" "$LINENO" 5
> +      else
> +        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&5
> +$as_echo "$as_me: WARNING: linux/perf_event.h missing or too old; CoreSight Tracing will be unavailable." >&2;}
> +      fi
> +    fi
> +
> +
> +
> +
> +
> +
> +
> +
> +
> +    use_additional=yes
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +
> +    eval additional_includedir=\"$includedir\"
> +    eval additional_libdir=\"$libdir\"
> +
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +
> +# Check whether --with-libopencsd_c_api-prefix was given.
> +if test "${with_libopencsd_c_api_prefix+set}" = set; then :
> +  withval=$with_libopencsd_c_api_prefix;
> +    if test "X$withval" = "Xno"; then
> +      use_additional=no
> +    else
> +      if test "X$withval" = "X"; then
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +
> +          eval additional_includedir=\"$includedir\"
> +          eval additional_libdir=\"$libdir\"
> +
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +      else
> +        additional_includedir="$withval/include"
> +        additional_libdir="$withval/lib"
> +      fi
> +    fi
> +
> +fi
> +
> +
> +# Check whether --with-libopencsd_c_api-type was given.
> +if test "${with_libopencsd_c_api_type+set}" = set; then :
> +  withval=$with_libopencsd_c_api_type;  with_libopencsd_c_api_type=$withval
> +else
> +   with_libopencsd_c_api_type=auto
> +fi
> +
> +  lib_type=`eval echo \$with_libopencsd_c_api_type`
> +
> +      LIBOPENCSD_C_API=
> +  LTLIBOPENCSD_C_API=
> +  INCOPENCSD_C_API=
> +  rpathdirs=
> +  ltrpathdirs=
> +  names_already_handled=
> +  names_next_round='opencsd_c_api '
> +  while test -n "$names_next_round"; do
> +    names_this_round="$names_next_round"
> +    names_next_round=
> +    for name in $names_this_round; do
> +      already_handled=
> +      for n in $names_already_handled; do
> +        if test "$n" = "$name"; then
> +          already_handled=yes
> +          break
> +        fi
> +      done
> +      if test -z "$already_handled"; then
> +        names_already_handled="$names_already_handled $name"
> +                        uppername=`echo "$name" | sed -e 'y|abcdefghijklmnopqrstuvwxyz./-|ABCDEFGHIJKLMNOPQRSTUVWXYZ___|'`
> +        eval value=\"\$HAVE_LIB$uppername\"
> +        if test -n "$value"; then
> +          if test "$value" = yes; then
> +            eval value=\"\$LIB$uppername\"
> +            test -z "$value" || LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$value"
> +            eval value=\"\$LTLIB$uppername\"
> +            test -z "$value" || LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$value"
> +          else
> +                                    :
> +          fi
> +        else
> +                              found_dir=
> +          found_la=
> +          found_so=
> +          found_a=
> +          if test $use_additional = yes; then
> +            if test -n "$shlibext" && test -f "$additional_libdir/lib$name.$shlibext" && test x$lib_type != xstatic; then
> +              found_dir="$additional_libdir"
> +              found_so="$additional_libdir/lib$name.$shlibext"
> +              if test -f "$additional_libdir/lib$name.la"; then
> +                found_la="$additional_libdir/lib$name.la"
> +              fi
> +            elif test x$lib_type != xshared; then
> +              if test -f "$additional_libdir/lib$name.$libext"; then
> +                found_dir="$additional_libdir"
> +                found_a="$additional_libdir/lib$name.$libext"
> +                if test -f "$additional_libdir/lib$name.la"; then
> +                  found_la="$additional_libdir/lib$name.la"
> +                fi
> +              fi
> +            fi
> +          fi
> +          if test "X$found_dir" = "X"; then
> +            for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +              case "$x" in
> +                -L*)
> +                  dir=`echo "X$x" | sed -e 's/^X-L//'`
> +                  if test -n "$shlibext" && test -f "$dir/lib$name.$shlibext" && test x$lib_type != xstatic; then
> +                    found_dir="$dir"
> +                    found_so="$dir/lib$name.$shlibext"
> +                    if test -f "$dir/lib$name.la"; then
> +                      found_la="$dir/lib$name.la"
> +                    fi
> +                  elif test x$lib_type != xshared; then
> +                    if test -f "$dir/lib$name.$libext"; then
> +                      found_dir="$dir"
> +                      found_a="$dir/lib$name.$libext"
> +                      if test -f "$dir/lib$name.la"; then
> +                        found_la="$dir/lib$name.la"
> +                      fi
> +                    fi
> +                  fi
> +                  ;;
> +              esac
> +              if test "X$found_dir" != "X"; then
> +                break
> +              fi
> +            done
> +          fi
> +          if test "X$found_dir" != "X"; then
> +                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$found_dir -l$name"
> +            if test "X$found_so" != "X"; then
> +                                                        if test "$enable_rpath" = no || test "X$found_dir" = "X/usr/lib"; then
> +                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +              else
> +                                                                                haveit=
> +                for x in $ltrpathdirs; do
> +                  if test "X$x" = "X$found_dir"; then
> +                    haveit=yes
> +                    break
> +                  fi
> +                done
> +                if test -z "$haveit"; then
> +                  ltrpathdirs="$ltrpathdirs $found_dir"
> +                fi
> +                                if test "$hardcode_direct" = yes; then
> +                                                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +                else
> +                  if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then
> +                                                            LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +                                                            haveit=
> +                    for x in $rpathdirs; do
> +                      if test "X$x" = "X$found_dir"; then
> +                        haveit=yes
> +                        break
> +                      fi
> +                    done
> +                    if test -z "$haveit"; then
> +                      rpathdirs="$rpathdirs $found_dir"
> +                    fi
> +                  else
> +                                                                                haveit=
> +                    for x in $LDFLAGS $LIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                      if test "X$x" = "X-L$found_dir"; then
> +                        haveit=yes
> +                        break
> +                      fi
> +                    done
> +                    if test -z "$haveit"; then
> +                      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir"
> +                    fi
> +                    if test "$hardcode_minus_L" != no; then
> +                                                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_so"
> +                    else
> +                                                                                                                                                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
> +                    fi
> +                  fi
> +                fi
> +              fi
> +            else
> +              if test "X$found_a" != "X"; then
> +                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$found_a"
> +              else
> +                                                LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$found_dir -l$name"
> +              fi
> +            fi
> +                        additional_includedir=
> +            case "$found_dir" in
> +              */lib | */lib/)
> +                basedir=`echo "X$found_dir" | sed -e 's,^X,,' -e 's,/lib/*$,,'`
> +                additional_includedir="$basedir/include"
> +                ;;
> +            esac
> +            if test "X$additional_includedir" != "X"; then
> +                                                                                                                if test "X$additional_includedir" != "X/usr/include"; then
> +                haveit=
> +                if test "X$additional_includedir" = "X/usr/local/include"; then
> +                  if test -n "$GCC"; then
> +                    case $host_os in
> +                      linux*) haveit=yes;;
> +                    esac
> +                  fi
> +                fi
> +                if test -z "$haveit"; then
> +                  for x in $CPPFLAGS $INCOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                    if test "X$x" = "X-I$additional_includedir"; then
> +                      haveit=yes
> +                      break
> +                    fi
> +                  done
> +                  if test -z "$haveit"; then
> +                    if test -d "$additional_includedir"; then
> +                                            INCOPENCSD_C_API="${INCOPENCSD_C_API}${INCOPENCSD_C_API:+ }-I$additional_includedir"
> +                    fi
> +                  fi
> +                fi
> +              fi
> +            fi
> +                        if test -n "$found_la"; then
> +                                                        save_libdir="$libdir"
> +              case "$found_la" in
> +                */* | *\\*) . "$found_la" ;;
> +                *) . "./$found_la" ;;
> +              esac
> +              libdir="$save_libdir"
> +                            for dep in $dependency_libs; do
> +                case "$dep" in
> +                  -L*)
> +                    additional_libdir=`echo "X$dep" | sed -e 's/^X-L//'`
> +                                                                                                                                                                if test "X$additional_libdir" != "X/usr/lib"; then
> +                      haveit=
> +                      if test "X$additional_libdir" = "X/usr/local/lib"; then
> +                        if test -n "$GCC"; then
> +                          case $host_os in
> +                            linux*) haveit=yes;;
> +                          esac
> +                        fi
> +                      fi
> +                      if test -z "$haveit"; then
> +                        haveit=
> +                        for x in $LDFLAGS $LIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                          if test "X$x" = "X-L$additional_libdir"; then
> +                            haveit=yes
> +                            break
> +                          fi
> +                        done
> +                        if test -z "$haveit"; then
> +                          if test -d "$additional_libdir"; then
> +                                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-L$additional_libdir"
> +                          fi
> +                        fi
> +                        haveit=
> +                        for x in $LDFLAGS $LTLIBOPENCSD_C_API; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +                          if test "X$x" = "X-L$additional_libdir"; then
> +                            haveit=yes
> +                            break
> +                          fi
> +                        done
> +                        if test -z "$haveit"; then
> +                          if test -d "$additional_libdir"; then
> +                                                        LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-L$additional_libdir"
> +                          fi
> +                        fi
> +                      fi
> +                    fi
> +                    ;;
> +                  -R*)
> +                    dir=`echo "X$dep" | sed -e 's/^X-R//'`
> +                    if test "$enable_rpath" != no; then
> +                                                                  haveit=
> +                      for x in $rpathdirs; do
> +                        if test "X$x" = "X$dir"; then
> +                          haveit=yes
> +                          break
> +                        fi
> +                      done
> +                      if test -z "$haveit"; then
> +                        rpathdirs="$rpathdirs $dir"
> +                      fi
> +                                                                  haveit=
> +                      for x in $ltrpathdirs; do
> +                        if test "X$x" = "X$dir"; then
> +                          haveit=yes
> +                          break
> +                        fi
> +                      done
> +                      if test -z "$haveit"; then
> +                        ltrpathdirs="$ltrpathdirs $dir"
> +                      fi
> +                    fi
> +                    ;;
> +                  -l*)
> +                                        names_next_round="$names_next_round "`echo "X$dep" | sed -e 's/^X-l//'`
> +                    ;;
> +                  *.la)
> +                                                                                names_next_round="$names_next_round "`echo "X$dep" | sed -e 's,^X.*/,,' -e 's,^lib,,' -e 's,\.la$,,'`
> +                    ;;
> +                  *)
> +                                        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$dep"
> +                    LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }$dep"
> +                    ;;
> +                esac
> +              done
> +            fi
> +          else
> +                                                            if test "x$lib_type" = "xauto" || test "x$lib_type" = "xshared"; then
> +              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l$name"
> +              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l$name"
> +            else
> +              LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }-l:lib$name.$libext"
> +              LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-l:lib$name.$libext"
> +            fi
> +          fi
> +        fi
> +      fi
> +    done
> +  done
> +  if test "X$rpathdirs" != "X"; then
> +    if test -n "$hardcode_libdir_separator"; then
> +                        alldirs=
> +      for found_dir in $rpathdirs; do
> +        alldirs="${alldirs}${alldirs:+$hardcode_libdir_separator}$found_dir"
> +      done
> +            acl_save_libdir="$libdir"
> +      libdir="$alldirs"
> +      eval flag=\"$hardcode_libdir_flag_spec\"
> +      libdir="$acl_save_libdir"
> +      LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
> +    else
> +            for found_dir in $rpathdirs; do
> +        acl_save_libdir="$libdir"
> +        libdir="$found_dir"
> +        eval flag=\"$hardcode_libdir_flag_spec\"
> +        libdir="$acl_save_libdir"
> +        LIBOPENCSD_C_API="${LIBOPENCSD_C_API}${LIBOPENCSD_C_API:+ }$flag"
> +      done
> +    fi
> +  fi
> +  if test "X$ltrpathdirs" != "X"; then
> +            for found_dir in $ltrpathdirs; do
> +      LTLIBOPENCSD_C_API="${LTLIBOPENCSD_C_API}${LTLIBOPENCSD_C_API:+ }-R$found_dir"
> +    done
> +  fi
> +
> +
> +        ac_save_CPPFLAGS="$CPPFLAGS"
> +
> +  for element in $INCOPENCSD_C_API; do
> +    haveit=
> +    for x in $CPPFLAGS; do
> +
> +  acl_save_prefix="$prefix"
> +  prefix="$acl_final_prefix"
> +  acl_save_exec_prefix="$exec_prefix"
> +  exec_prefix="$acl_final_exec_prefix"
> +  eval x=\"$x\"
> +  exec_prefix="$acl_save_exec_prefix"
> +  prefix="$acl_save_prefix"
> +
> +      if test "X$x" = "X$element"; then
> +        haveit=yes
> +        break
> +      fi
> +    done
> +    if test -z "$haveit"; then
> +      CPPFLAGS="${CPPFLAGS}${CPPFLAGS:+ }$element"
> +    fi
> +  done
> +
> +
> +  { $as_echo "$as_me:${as_lineno-$LINENO}: checking for libopencsd_c_api" >&5
> +$as_echo_n "checking for libopencsd_c_api... " >&6; }
> +if ${ac_cv_libopencsd_c_api+:} false; then :
> +  $as_echo_n "(cached) " >&6
> +else
> +
> +    ac_save_LIBS="$LIBS"
> +    LIBS="$LIBS $LIBOPENCSD_C_API"
> +    cat confdefs.h - <<_ACEOF >conftest.$ac_ext
> +/* end confdefs.h.  */
> +#include "opencsd/c_api/opencsd_c_api.h"
> +int
> +main ()
> +{
> +ocsd_get_version();
> +  ;
> +  return 0;
> +}
> +_ACEOF
> +if ac_fn_c_try_link "$LINENO"; then :
> +  ac_cv_libopencsd_c_api=yes
> +else
> +  ac_cv_libopencsd_c_api=no
> +fi
> +rm -f core conftest.err conftest.$ac_objext \
> +    conftest$ac_exeext conftest.$ac_ext
> +    LIBS="$ac_save_LIBS"
> +
> +fi
> +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_libopencsd_c_api" >&5
> +$as_echo "$ac_cv_libopencsd_c_api" >&6; }
> +  if test "$ac_cv_libopencsd_c_api" = yes; then
> +    HAVE_LIBOPENCSD_C_API=yes
> +
> +$as_echo "#define HAVE_LIBOPENCSD_C_API 1" >>confdefs.h
> +
> +    { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libopencsd_c_api" >&5
> +$as_echo_n "checking how to link with libopencsd_c_api... " >&6; }
> +    { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBOPENCSD_C_API" >&5
> +$as_echo "$LIBOPENCSD_C_API" >&6; }
> +  else
> +    HAVE_LIBOPENCSD_C_API=no
> +            CPPFLAGS="$ac_save_CPPFLAGS"
> +    LIBOPENCSD_C_API=
> +    LTLIBOPENCSD_C_API=
> +  fi
> +
> +
> +
> +
> +
> +
> +    if test "$HAVE_LIBOPENCSD_C_API" != yes; then
> +      if test "$with_arm_cs" = yes; then
> +        as_fn_error $? "libopencsd_c_api is missing or unusable" "$LINENO" 5
> +      else
> +        { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&5
> +$as_echo "$as_me: WARNING: libopencsd_c_api is missing or unusable; CoreSight Tracing will be unavailable." >&2;}
> +      fi
> +    else
> +      LIBS="$LIBS $LIBOPENCSD_C_API"
> +    fi
> +  fi
> +
> +
>   
>   $as_echo "#define _STRUCTURED_PROC 1" >>confdefs.h
>   
> 

Odd formatting aside (some deeply-indented lines), I have no further 
comments on this one.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 2/7] add btrace coresight related commands
  2021-05-31 21:33 ` [PATCH v6 2/7] add btrace coresight related commands Zied Guermazi
  2021-06-01 12:07   ` Eli Zaretskii
@ 2021-06-30 12:26   ` Luis Machado
  1 sibling, 0 replies; 35+ messages in thread
From: Luis Machado @ 2021-06-30 12:26 UTC (permalink / raw)
  To: Zied Guermazi, gdb-patches, markus.t.metzger

On 5/31/21 6:33 PM, Zied Guermazi wrote:
> This patch extends the commands needed for using branch tracing
> with ARM CoreSight traces.
> Those commands are:
> set record btrace etm sink
> set record btrace etm buffer-size
> record btrace etm
> 
> gdb/ChangeLog
> 
> 	* NEWS: list new commands for extending btrace
> 	to support using ARM CoreSight Traces.
> 	* record-btrace.c (record_btrace_print_etm_conf): New.
> 	(record_btrace_print_conf): handle BTRACE_FORMAT_ETM.
> 	(cmd_record_btrace_etm_start): New.
> 	(cmd_record_btrace_start): handle starting ETM tracing.
> 	(cmd_show_record_btrace_cpu): extend for ARM cpus.
> 	(show_record_etm_buffer_size_value): New.
> 	(_initialize_record_btrace): add commands for ETM traces.
> 	(record_start): add starting ETM traces.
> 
> gdb/doc/ChangeLog
> 
> 	* gdb.texinfo (Process Record and Replay): Document extending
> 	GDB btrace commands to support using ARM CoreSight traces.
> 
> gdbsupport/ChangeLog
> 
> 	* btrace-common.h (btrace_format): add BTRACE_FORMAT_ETM
> 	to the enum.
> 	(btrace_config_etm): new struct.
> 	(btrace_config): add btrace_config_etm etm.
> 	* btrace-common.cc (btrace_format_string): add BTRACE_FORMAT_ETM.
> 	(btrace_format_short_string): add BTRACE_FORMAT_ETM.
> ---
>   gdb/NEWS                    |  16 +++++
>   gdb/doc/gdb.texinfo         |  51 +++++++++++++++-
>   gdb/record-btrace.c         | 117 +++++++++++++++++++++++++++++++++++-
>   gdb/record.c                |   2 +
>   gdbsupport/btrace-common.cc |   6 ++
>   gdbsupport/btrace-common.h  |  22 ++++++-
>   6 files changed, 210 insertions(+), 4 deletions(-)
> 
> diff --git a/gdb/NEWS b/gdb/NEWS
> index ab678acec8b..02035901f6e 100644
> --- a/gdb/NEWS
> +++ b/gdb/NEWS
> @@ -2,6 +2,7 @@
>   	     (Organized release by release)
>   
>   *** Changes since GDB 10
> +* Record btrace now  supports using ARM CoreSight ETM traces.

Spurious white-space above.

>   
>   * GDB now supports general memory tagging functionality if the underlying
>     architecture supports the proper primitives and hooks.  Currently this is
> @@ -77,6 +78,21 @@
>   
>   * New commands
>   
> +record btrace etm
> +record etm
> +  Start branch trace recording using ARM CoreSight trace format (ETM).
> +
> +set|show record btrace etm buffer-size
> +  Set and show the size of the ring buffer used for branch tracing in
> +  ETM format.
> +  The obtained size may differ from the requested size.  Use "info
> +  record" to see the obtained buffer size.
> +
> +set|show record btrace etm sink
> +  Set and show the trace sink used for branch tracing in
> +  ETM format.
> +  Use "default" to reset it to default sink.
> +
>   set debug event-loop
>   show debug event-loop
>     Control the display of debug output about GDB's event loop.
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index 90d827a50e7..b9b09ff97f2 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -7479,15 +7479,19 @@ For architecture environments that support process record and replay,
>   @kindex record btrace
>   @kindex record btrace bts
>   @kindex record btrace pt
> +@kindex record btrace etm
>   @kindex record bts
>   @kindex record pt
> +@kindex record etm
>   @kindex rec
>   @kindex rec full
>   @kindex rec btrace
>   @kindex rec btrace bts
>   @kindex rec btrace pt
> +@kindex rec btrace etm
>   @kindex rec bts
>   @kindex rec pt
> +@kindex rec etm
>   @item record @var{method}
>   This command starts the process record and replay target.  The
>   recording method can be specified as parameter.  Without a parameter
> @@ -7501,7 +7505,7 @@ replay implementation.  This method allows replaying and reverse
>   execution.
>   
>   @item btrace @var{format}
> -Hardware-supported instruction recording, supported on Intel
> +Hardware-supported instruction recording, supported on Intel and ARM
>   processors.  This method does not record data.  Further, the data is
>   collected in a ring buffer so old data will be overwritten when the
>   buffer is full.  It allows limited reverse execution.  Variables and
> @@ -7535,6 +7539,13 @@ Decoding the recorded execution trace, on the other hand, is more
>   expensive than decoding @acronym{BTS} trace.  This is mostly due to the
>   increased number of instructions to process.  You should increase the
>   buffer-size with care.
> +
> +@item etm
> +@cindex ARM CoreSight Trace
> +Use the @dfn{ARM CoreSight Trace} recording format.  In this
> +format, @acronym{ETM, Extended Trace Macrocell} stores the processor
> +execution trace in a compressed form that is afterwards
> +decoded by @value{GDBN}.
>   @end table
>   
>   Not all recording formats may be available on all processors.
> @@ -7682,6 +7693,12 @@ and to read-write memory.  Beware that the accessed memory corresponds
>   to the live target and not necessarily to the current replay
>   position.
>   
> +@item set record btrace etm sink @var{sink}
> +Set ARM CoreSight ETM sink to collect traces.
> +On @sc{gnu}/Linux systems, possible values for @var{sink} are the name of the
> +files in the directory @file{/sys/bus/event_source/devices/cs_etm/sinks/}.
> +Use the value @kbd{default} to reset it to default sink.
> +
>   @item set record btrace cpu @var{identifier}
>   Set the processor to be used for enabling workarounds for processor
>   errata when decoding the trace.
> @@ -7745,6 +7762,9 @@ Recorded 84872 instructions in 3189 functions (0 gaps) for thread 1 (...).
>   @item show record btrace replay-memory-access
>   Show the current setting of @code{replay-memory-access}.
>   
> +@item show record btrace etm sink
> +Show ARM CoreSight ETM sink.
> +
>   @item show record btrace cpu
>   Show the processor to be used for enabling trace decode errata
>   workarounds.
> @@ -7796,6 +7816,29 @@ also need longer to process the branch trace data before it can be used.
>   Show the current setting of the requested ring buffer size for branch
>   tracing in Intel Processor Trace format.
>   
> +@kindex set record btrace etm
> +@item set record btrace etm buffer-size @var{size}
> +@itemx set record btrace etm buffer-size unlimited
> +Set the requested ring buffer size for branch tracing in ARM
> +CoreSight ETM Trace format.  Default is 16KB.
> +
> +If @var{size} is a positive number, then @value{GDBN} will try to
> +allocate a buffer of at least @var{size} bytes for each new thread
> +that uses the btrace recording method and the ARM CoreSight ETM
> +format.  The actually obtained buffer size may differ from the
> +requested @var{size}.  Use the @code{info record} command to see the
> +actual buffer size for each thread.
> +
> +If @var{limit} is @code{unlimited} or zero, @value{GDBN} will try to
> +allocate a buffer of 4MB.
> +
> +Bigger buffers mean longer traces.  On the other hand, @value{GDBN} will
> +also need longer to process the branch trace data before it can be used.
> +
> +@item show record btrace etm buffer-size @var{size}
> +Show the current setting of the requested ring buffer size for branch
> +tracing in ARM CoreSight ETM Trace format.
> +
>   @kindex info record
>   @item info record
>   Show various statistics about the recording depending on the recording
> @@ -7847,6 +7890,12 @@ For the @code{pt} recording format, it also shows:
>   @item
>   Size of the perf ring buffer.
>   @end itemize
> +
> +For the @code{etm} recording format, it also shows:
> +@itemize @bullet
> +@item
> +Size of the perf ring buffer.
> +@end itemize
>   @end table
>   
>   @kindex record delete
> diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
> index 00affb85d22..16ffb76272b 100644
> --- a/gdb/record-btrace.c
> +++ b/gdb/record-btrace.c
> @@ -201,6 +201,10 @@ static struct cmd_list_element *show_record_btrace_bts_cmdlist;
>   static struct cmd_list_element *set_record_btrace_pt_cmdlist;
>   static struct cmd_list_element *show_record_btrace_pt_cmdlist;
>   
> +/* Command lists for "set/show record btrace etm".  */
> +static struct cmd_list_element *set_record_btrace_etm_cmdlist;
> +static struct cmd_list_element *show_record_btrace_etm_cmdlist;
> +
>   /* Command list for "set record btrace cpu".  */
>   static struct cmd_list_element *set_record_btrace_cpu_cmdlist;
>   
> @@ -526,6 +530,22 @@ record_btrace_print_pt_conf (const struct btrace_config_pt *conf)
>       }
>   }
>   
> +/* Print an ARM Processor Trace configuration.  */
> +
> +static void
> +record_btrace_print_etm_conf (const struct btrace_config_etm *conf)
> +{
> +  unsigned int size;
> +
> +  size = conf->size;
> +  if (size > 0)
> +    {
> +      const char *suffix;
> +      suffix = record_btrace_adjust_size (&size);
> +      printf_unfiltered (_("Buffer size: %u%s.\n"), size, suffix);
> +    }
> +}
> +
>   /* Print a branch tracing configuration.  */
>   
>   static void
> @@ -546,6 +566,10 @@ record_btrace_print_conf (const struct btrace_config *conf)
>       case BTRACE_FORMAT_PT:
>         record_btrace_print_pt_conf (&conf->pt);
>         return;
> +
> +    case BTRACE_FORMAT_ETM:
> +      record_btrace_print_etm_conf (&conf->etm);
> +      return;
>       }
>   
>     internal_error (__FILE__, __LINE__, _("Unknown branch trace format."));
> @@ -2676,7 +2700,7 @@ record_btrace_target::stop (ptid_t ptid)
>   	  tp->btrace.flags |= BTHR_STOP;
>   	}
>       }
> - }
> +}
>   

I keep seeing the above change. It looks spurious.

>   /* The can_execute_reverse method of target record-btrace.  */
>   
> @@ -2937,6 +2961,27 @@ cmd_record_btrace_pt_start (const char *args, int from_tty)
>       }
>   }
>   
> +/* Start recording in ARM CoreSight ETM Trace format.  */
> +
> +static void
> +cmd_record_btrace_etm_start (const char *args, int from_tty)
> +{
> +  if (args != nullptr && *args != 0)
> +    error (_("Invalid argument."));
> +
> +  record_btrace_conf.format = BTRACE_FORMAT_ETM;
> +
> +  try
> +    {
> +      execute_command ("target record-btrace", from_tty);
> +    }
> +  catch (const gdb_exception &exception)
> +    {
> +      record_btrace_conf.format = BTRACE_FORMAT_NONE;
> +      throw;
> +    }
> +}
> +
>   /* Alias for "target record".  */
>   
>   static void
> @@ -2951,10 +2996,18 @@ cmd_record_btrace_start (const char *args, int from_tty)
>       {
>         execute_command ("target record-btrace", from_tty);
>       }
> -  catch (const gdb_exception &exception)
> +  catch (const gdb_exception &exception_pt)
>       {
>         record_btrace_conf.format = BTRACE_FORMAT_BTS;
>   
> +      try
> +	{
> +	  execute_command ("target record-btrace", from_tty);
> +	}
> +      catch (const gdb_exception &exception_bts)
> +	{
> +	  record_btrace_conf.format = BTRACE_FORMAT_ETM;
> +
>   	  try
>   	    {
>   	      execute_command ("target record-btrace", from_tty);
> @@ -2965,6 +3018,7 @@ cmd_record_btrace_start (const char *args, int from_tty)
>   	      throw;
>   	    }
>   	}
> +    }
>   }
>   
>   /* The "show record btrace replay-memory-access" command.  */
> @@ -3103,6 +3157,17 @@ show_record_pt_buffer_size_value (struct ui_file *file, int from_tty,
>   		    value);
>   }
>   
> +/* The "record etm buffer-size" show value function.  */
> +
> +static void
> +show_record_etm_buffer_size_value (struct ui_file *file, int from_tty,
> +				   struct cmd_list_element *c,
> +				   const char *value)
> +{
> +  fprintf_filtered (file, _("The record/replay etm buffer size is %s.\n"),
> +		    value);
> +}
> +
>   /* Initialize btrace commands.  */
>   
>   void _initialize_record_btrace ();
> @@ -3170,6 +3235,15 @@ When set to \"none\", errata workarounds are disabled."),
>   		  1,
>   		  &set_record_btrace_cmdlist);
>   
> +
> +  cmd_list_element *record_btrace_etm_cmd
> +    = add_cmd ("etm", class_obscure, cmd_record_btrace_etm_start,
> +	       _("\
> +Start branch trace recording in ARM CoreSight ETM Trace format.\n\n\
> +This format may not be available on all processors."),
> +	     &record_btrace_cmdlist);
> +  add_alias_cmd ("etm", record_btrace_etm_cmd, class_obscure, 1, &record_cmdlist);
> +
>     add_cmd ("auto", class_support, cmd_set_record_btrace_cpu_auto, _("\
>   Automatically determine the cpu to be used for trace decode."),
>   	   &set_record_btrace_cpu_cmdlist);
> @@ -3231,6 +3305,43 @@ to see the actual buffer size."), NULL, show_record_pt_buffer_size_value,
>   			    &set_record_btrace_pt_cmdlist,
>   			    &show_record_btrace_pt_cmdlist);
>   
> +  add_basic_prefix_cmd ("etm", class_support,
> +			_("Set record btrace etm options."),
> +			&set_record_btrace_etm_cmdlist,
> +			0,
> +			&set_record_btrace_cmdlist);
> +
> +  add_show_prefix_cmd ("etm", class_support,
> +		       _("Show record btrace etm options."),
> +		       &show_record_btrace_etm_cmdlist,
> +		       0,
> +		       &show_record_btrace_cmdlist);
> +
> +  add_setshow_uinteger_cmd ("buffer-size", no_class,
> +			    &record_btrace_conf.etm.size,
> +			    _("Set the record/replay etm buffer size."),
> +			    _("Show the record/replay etm buffer size."), _("\
> +Bigger buffers allow longer recording but also take more time to process \
> +the recorded execution.\n\
> +The actual buffer size may differ from the requested size.  Use \"info record\"\
> + to see the actual buffer size."), NULL, show_record_etm_buffer_size_value,

This is inconsistent with what the documentation says:

"Bigger buffers mean longer traces.  On the other hand, @value{GDBN} 
will also need longer to process the branch trace data before it can be 
used."

How about using the same text so things are consistent across the codebase?


> +		  &set_record_btrace_etm_cmdlist,
> +		  &show_record_btrace_etm_cmdlist);
> +
> +  add_setshow_string_cmd ("sink", no_class,
> +			  &record_btrace_conf.etm.sink,
> +			  _("Set the record/replay ETM sink device."),
> +			  _("Show the record/replay ETM sink device."),
> +			  _("\
> +Sink device is the device that intercepts ETM traces and collects or routes \
> +them out of the System on Chip.\n\
> +The list of available sinks on linux targets corresponds to the files in \
> +the directory \"/sys/bus/event_source/devices/cs_etm/sinks/\".\n\
> +value \"default\" reset it to default sink"),
> +			  NULL, NULL,
> +			  &set_record_btrace_etm_cmdlist,
> +			  &show_record_btrace_etm_cmdlist);
> +
>     add_target (record_btrace_target_info, record_btrace_target_open);
>   
>     bfcache = htab_create_alloc (50, bfcache_hash, bfcache_eq, NULL,
> @@ -3238,4 +3349,6 @@ to see the actual buffer size."), NULL, show_record_pt_buffer_size_value,
>   
>     record_btrace_conf.bts.size = 64 * 1024;
>     record_btrace_conf.pt.size = 16 * 1024;
> +#define DEFAULT_ETM_BUFFER_SIZE (8 * 1024)
> +  record_btrace_conf.etm.size = DEFAULT_ETM_BUFFER_SIZE;
>   }
> diff --git a/gdb/record.c b/gdb/record.c
> index 6968c30d930..32492fb53c4 100644
> --- a/gdb/record.c
> +++ b/gdb/record.c
> @@ -118,6 +118,8 @@ record_start (const char *method, const char *format, int from_tty)
>   	execute_command_to_string ("record btrace bts", from_tty, false);
>         else if (strcmp (format, "pt") == 0)
>   	execute_command_to_string ("record btrace pt", from_tty, false);
> +      else if (strcmp (format, "etm") == 0)
> +	execute_command_to_string ("record btrace etm", from_tty, false);
>         else
>   	error (_("Invalid format."));
>       }
> diff --git a/gdbsupport/btrace-common.cc b/gdbsupport/btrace-common.cc
> index 4f9ef855e74..79ff21de44b 100644
> --- a/gdbsupport/btrace-common.cc
> +++ b/gdbsupport/btrace-common.cc
> @@ -36,6 +36,9 @@ btrace_format_string (enum btrace_format format)
>   
>       case BTRACE_FORMAT_PT:
>         return _("Intel Processor Trace");
> +
> +    case BTRACE_FORMAT_ETM:
> +      return _("ARM Processor CoreSight ETM Trace");
>       }
>   
>     internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
> @@ -56,6 +59,9 @@ btrace_format_short_string (enum btrace_format format)
>   
>       case BTRACE_FORMAT_PT:
>         return "pt";
> +
> +    case BTRACE_FORMAT_ETM:
> +      return "etm";
>       }
>   
>     internal_error (__FILE__, __LINE__, _("Unknown branch trace format"));
> diff --git a/gdbsupport/btrace-common.h b/gdbsupport/btrace-common.h
> index 26d26ec957f..153b977723a 100644
> --- a/gdbsupport/btrace-common.h
> +++ b/gdbsupport/btrace-common.h
> @@ -63,7 +63,10 @@ enum btrace_format
>     BTRACE_FORMAT_BTS,
>   
>     /* Branch trace is in Intel Processor Trace format.  */
> -  BTRACE_FORMAT_PT
> +  BTRACE_FORMAT_PT,
> +
> +  /* Branch trace is ARM CoreSight ETM format.  */
> +  BTRACE_FORMAT_ETM
>   };
>   
>   /* An enumeration of cpu vendors.  */
> @@ -119,6 +122,20 @@ struct btrace_config_pt
>     unsigned int size;
>   };
>   
> +/* An ARM CoreSight ETM Trace configuration.  */
> +
> +struct btrace_config_etm
> +{
> +  /* The size of the branch trace buffer in bytes.
> +
> +     This is unsigned int and not size_t since it is registered as
> +     control variable for "set record btrace etm buffer-size".  */
> +  unsigned int size;
> +
> +  /* The sink used to collect the traces.  */
> +  char *sink;
> +};
> +
>   /* A branch tracing configuration.
>   
>      This describes the requested configuration as well as the actually
> @@ -136,6 +153,9 @@ struct btrace_config
>   
>     /* The Intel Processor Trace format configuration.  */
>     struct btrace_config_pt pt;
> +
> +  /* The ARM CoreSight ETM Trace configuration.  */
> +  struct btrace_config_etm etm;
>   };
>   
>   /* Branch trace in BTS format.  */
> 

Other than those nits, I have no further comments on this.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant
  2021-05-31 21:33 ` [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant Zied Guermazi
  2021-06-22 14:59   ` Metzger, Markus T
@ 2021-06-30 12:54   ` Luis Machado
  1 sibling, 0 replies; 35+ messages in thread
From: Luis Machado @ 2021-06-30 12:54 UTC (permalink / raw)
  To: Zied Guermazi, gdb-patches, markus.t.metzger

On 5/31/21 6:33 PM, Zied Guermazi wrote:
> This patch extend branch tracing by adding the functions needed
> to collect parameters for decoding ETM traces and decoding them.
> 
> gdb/ChangeLog
> 
> 	* arch/arm.h: Add defines for exception numbers as encoded
> 	in the ETM traces.
> 	* btrace.c (ftrace_new_function): log new function.
> 	(ftrace_remove_last_insn): New.
> 	(cs_etm_get_etmv3_config): New.
> 	(cs_etm_get_etmv4_config): New.
> 	(cs_etm_get_isa_flag): New.
> 	(cs_etm_get_instruction_class): New.
> 	(cs_etm_update_btrace_with_inst_range): New.
> 	(cs_etm_update_btrace_with_exception): New.
> 	(cs_etm_update_btrace_with_trace_on): New.
> 	(cs_etm_trace_element_callback): New.
> 	(cs_etm_free_decoder): New.
> 	(cs_etm_create_decoder): New.
> 	(btrace_etm_readmem_callback): New.
> 	(cs_etm_add_mem_access_callback): New.
> 	(cs_etm_process_data_block): New.
> 	(btrace_print_all): New.
> 	(btrace_compute_ftrace_etm): New.
> 	(btrace_compute_ftrace_1): add handling of CoreSight traces.
> 	(btrace_enable): add error message if ETM unavailable.
> 	(btrace_stitch_trace): add handling of CoreSight traces.
> 	(maint_info_btrace_cmd): add handling of CoreSight trace format.
> 	* btrace.h (btrace_insn_flag): add ARM ISA flags.
> 	* record-btrace.c (cs_etm_reconstruct_cpsr_iset_state): New.
> 	(record_btrace_target::fetch_registers): fetch cpsr register
> 	from insn->flags when applicable.
> 
> gdbsupport/ChangeLog
> 
> 	* btrace-common.h (cs_etmv3_trace_params): New
> 	(cs_etmv4_trace_params): New.
> 	(cs_etm_trace_params): New.
> 	(cs_etm_decoder_params): New
> 	(btrace_data_etm_config): New.
> 	(btrace_data_etm): New.
> 	(btrace_data): add a btrace_data_etm.
> 	* btrace-common.cc (btrace_data::fini): handle CoreSight traces.
> 	(btrace_data::empty): handle CoreSight traces.
> 	(btrace_data_append): handle CoreSight traces.
> ---
>   gdb/arch/arm.h              |  33 ++
>   gdb/btrace.c                | 618 ++++++++++++++++++++++++++++++++++++
>   gdb/btrace.h                |  16 +-
>   gdb/record-btrace.c         |  57 +++-
>   gdbsupport/btrace-common.cc |  39 +++
>   gdbsupport/btrace-common.h  | 106 +++++++
>   6 files changed, 864 insertions(+), 5 deletions(-)
> 
> diff --git a/gdb/arch/arm.h b/gdb/arch/arm.h
> index fa589fd0582..4d520fe906a 100644
> --- a/gdb/arch/arm.h
> +++ b/gdb/arch/arm.h
> @@ -150,6 +150,39 @@ enum arm_m_profile_type {
>   #define BranchDest(addr,instr) \
>     ((CORE_ADDR) (((unsigned long) (addr)) + 8 + (sbits (instr, 0, 23) << 2)))
>   
> +/* Exception numbers as encoded in ETMv3.4 for ARMv7-M processors.
> +   See IHI0014Q_etm_architecture_spec table 7.11.  */
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_USAGE_FAULT    		0x009
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_NMI     			0x00a
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_SVC     			0x00b
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_DEBUG_MONITOR  		0x00c
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_MEM_USAGE      		0x00d
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_PEND_SV 			0x00e
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_SYS_TICK			0x00f
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_PROCESSOR_RESET		0x011
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_HARD_FAULT     		0x013
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_BUS_FAULT      		0x015
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_IRQ(n) \
> +  (n == 0 ? 0x008 : n < 8 ? n : n + 0x010)
> +#define CS_ETMV3_4_CORTEX_M_EXCEPTION_IS_IRQ(n) \
> +  ((n > 0x000 && n < 0x009) || (n > 0x017 && n < 0x200))
> +
> +/* Exception numbers as encoded in ETMv3.4 for non ARMv7-M processors.
> +   See IHI0014Q_etm_architecture_spec table 7.12.  */
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_HALTING_DEBUG		0x01
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_SECURE_MONITOR_CALL  	0x02
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_ENTRY_TO_HYP_MODE    	0x03
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_ASYNCHRONOUS_DATA_ABORT      0x04
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_JAZELLE_THUMBEE_CHECK	0x05
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_PROCESSOR_RESET      	0x08
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_UNDEFINED_INSTRUCTION	0x09
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_SUPERVISOR_CALL      	0x0a
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_PREFETCH_ABORT_SW_BKPT	0x0b
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_SYNCHRONOUS_DATA_ABORT_SW_WP 0x0c
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_GENERIC      		0x0d
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_IRQ   			0x0e
> +#define CS_ETMV3_4_CORTEX_A_R_EXCEPTION_FIQ   			0x0f
> +
>   /* Forward declaration.  */
>   struct regcache;
>   
> diff --git a/gdb/btrace.c b/gdb/btrace.c
> index 5e689c11d4b..2676389b63e 100644
> --- a/gdb/btrace.c
> +++ b/gdb/btrace.c
> @@ -35,6 +35,7 @@
>   #include "gdbcmd.h"
>   #include "cli/cli-utils.h"
>   #include "gdbarch.h"
> +#include "arch/arm.h"
>   
>   /* For maintenance commands.  */
>   #include "record-btrace.h"
> @@ -671,6 +672,38 @@ ftrace_update_insns (struct btrace_function *bfun, const btrace_insn &insn)
>       ftrace_debug (bfun, "update insn");
>   }
>   
> +#if defined (HAVE_LIBOPENCSD_C_API)
> +/* Remove last instruction from BFUN's list.
> +   This function is not generic and does not undo functions chaining.
> +   When adding an instruction after using it, the caller must ensure
> +   that the instruction produces the same chaining.
> +   An example of good case, is when the same removed instruction
> +   is added later.  */
> +
> +static void
> +ftrace_remove_last_insn (struct btrace_thread_info *btinfo)
> +{
> +  /* If we didn't have a function, we return.  */
> +  if (btinfo->functions.empty ())
> +    return;
> +
> +  struct btrace_function *bfun = &btinfo->functions.back ();
> +  /* If we had a gap before, we return.  */
> +  if (bfun->errcode != 0)
> +    return;
> +
> +  if (!bfun->insn.empty ())
> +    bfun->insn.pop_back ();
> +  else
> +    {
> +      /* A valid function must have at least one instruction.  */
> +      internal_error (__FILE__, __LINE__,
> +		       _("Attempt to remove last instruction"
> +			 "from an empty function"));
> +    }
> +}
> +#endif /* defined (HAVE_LIBOPENCSD_C_API) */
> +
>   /* Classify the instruction at PC.  */
>   
>   static enum btrace_insn_class
> @@ -1505,6 +1538,574 @@ btrace_compute_ftrace_pt (struct thread_info *tp,
>   
>   #endif /* defined (HAVE_LIBIPT)  */
>   
> +#if defined (HAVE_LIBOPENCSD_C_API)
> +
> +/* This structure holds the status and the context for decoding ETM traces.
> +   It is also used in the ETM trace element callback to get the context.  */
> +struct cs_etm_decoder
> +{
> +  /* The tree representing CoreSight architecture in the SoC.  */
> +  dcd_tree_handle_t dcd_tree;
> +
> +  /* Callback function to allow the decoder to access program memory.  */
> +  Fn_MemAcc_CB mem_access;
> +
> +  /* thread_info of traced thread.  */
> +  struct thread_info *t_info;
> +
> +  /* Returned value of previously processed block.  */
> +  ocsd_datapath_resp_t prev_return;
> +
> +  /* ARM architecture version of associated core.  */
> +  ocsd_arch_version_t arch_version;
> +
> +  /* List of gaps in the execution record.  */
> +  std::vector<unsigned int> &gaps;
> +};
> +
> +/* Fills a ocsd_etmv3_cfg from a cs_etm_trace_params.  */
> +
> +static void
> +cs_etm_get_etmv3_config (const struct cs_etm_trace_params *params,
> +			  ocsd_etmv3_cfg *config)
> +{
> +  config->reg_idr = params->etmv3.reg_idr;
> +  config->reg_ctrl = params->etmv3.reg_ctrl;
> +  config->reg_ccer = params->etmv3.reg_ccer;
> +  config->reg_trc_id = params->etmv3.reg_trc_id;
> +  config->arch_ver = (ocsd_arch_version_t)params->arch_ver;
> +  config->core_prof = (ocsd_core_profile_t)params->core_profile;
> +}
> +
> +/* Fills a ocsd_etmv4_cfg from a cs_etm_trace_params.  */
> +
> +static void
> +cs_etm_get_etmv4_config (const struct cs_etm_trace_params *params,
> +			  ocsd_etmv4_cfg *config)
> +{
> +  config->reg_configr = params->etmv4.reg_configr;
> +  config->reg_traceidr = params->etmv4.reg_traceidr;
> +  config->reg_idr0 = params->etmv4.reg_idr0;
> +  config->reg_idr1 = params->etmv4.reg_idr1;
> +  config->reg_idr2 = params->etmv4.reg_idr2;
> +  config->reg_idr8 = params->etmv4.reg_idr8;
> +  config->reg_idr9 = 0;
> +  config->reg_idr10 = 0;
> +  config->reg_idr11 = 0;
> +  config->reg_idr12 = 0;
> +  config->reg_idr13 = 0;
> +  config->arch_ver = (ocsd_arch_version_t)params->arch_ver;
> +  config->core_prof = (ocsd_core_profile_t)params->core_profile;
> +}
> +
> +/* Set the instruction flag to track ISA mode of ARM processor.  */
> +
> +static btrace_insn_flags
> +cs_etm_get_isa_flag (const ocsd_generic_trace_elem *elem)
> +{
> +  switch (elem->isa)
> +    {
> +    case ocsd_isa_arm:
> +      return BTRACE_INSN_FLAG_ISA_ARM;
> +
> +    case ocsd_isa_thumb2:
> +      return BTRACE_INSN_FLAG_ISA_THUMB2;
> +
> +    case ocsd_isa_aarch64:
> +      return BTRACE_INSN_FLAG_ISA_AARCH64;
> +
> +    case ocsd_isa_tee:
> +      return BTRACE_INSN_FLAG_ISA_TEE;
> +
> +    case ocsd_isa_jazelle:
> +      return BTRACE_INSN_FLAG_ISA_JAZELLE;
> +
> +    case ocsd_isa_custom:
> +      return BTRACE_INSN_FLAG_ISA_CUSTOM;
> +
> +    case ocsd_isa_unknown:
> +      return BTRACE_INSN_FLAG_ISA_UNKNOWN;
> +
> +    default:
> +      internal_error (__FILE__, __LINE__,
> +		       _("Undefined elem->isa value returned by OpenCsd."));
> +    }
> +}
> +
> +/* Returns the instruction class.  */
> +
> +static enum btrace_insn_class
> +cs_etm_get_instruction_class (const ocsd_generic_trace_elem *elem)
> +{
> +  switch (elem->last_i_type)
> +    {
> +    case OCSD_INSTR_BR:
> +    case OCSD_INSTR_BR_INDIRECT:
> +      switch (elem->last_i_subtype)
> +	{
> +	case OCSD_S_INSTR_V8_RET:
> +	case OCSD_S_INSTR_V8_ERET:
> +	case OCSD_S_INSTR_V7_IMPLIED_RET:
> +	  return BTRACE_INSN_RETURN;
> +
> +	case OCSD_S_INSTR_BR_LINK:
> +	  return BTRACE_INSN_CALL;
> +
> +	case OCSD_S_INSTR_NONE:
> +	  return BTRACE_INSN_JUMP;
> +	}
> +      return BTRACE_INSN_OTHER;
> +
> +    case OCSD_INSTR_ISB:
> +    case OCSD_INSTR_DSB_DMB:
> +    case OCSD_INSTR_WFI_WFE:
> +    case OCSD_INSTR_OTHER:
> +    default:
> +      return BTRACE_INSN_OTHER;
> +    }
> +}
> +
> +/* Update btrace in the case of an instruction range.  */
> +
> +static void
> +cs_etm_update_btrace_with_inst_range (const struct cs_etm_decoder *etm_decoder,
> +				       const ocsd_generic_trace_elem *elem)
> +{
> +  gdb_assert (elem->elem_type == OCSD_GEN_TRC_ELEM_INSTR_RANGE);
> +
> +  struct thread_info *tp = etm_decoder->t_info;
> +  struct btrace_thread_info *btinfo = &tp->btrace;
> +  struct gdbarch *gdbarch = target_gdbarch ();
> +  struct btrace_insn insn;
> +
> +  insn.iclass = BTRACE_INSN_OTHER;
> +  insn.pc = elem->st_addr;
> +  for (int i = 0; i< elem->num_instr_range; i++)
> +    {
> +      try
> +	{
> +	  insn.size = gdb_insn_length (gdbarch, insn.pc);
> +	}
> +      catch (const gdb_exception_error &err)
> +	{
> +	  error (_("Failed to get the size of the instruction."));
> +	}
> +
> +      struct btrace_function *bfun = ftrace_update_function (btinfo, insn.pc);
> +      if (etm_decoder->arch_version == ARCH_V7)
> +	insn.flags = cs_etm_get_isa_flag (elem);
> +
> +      if (i == elem->num_instr_range -1)
> +	insn.iclass = cs_etm_get_instruction_class (elem);
> +
> +      ftrace_update_insns (bfun, insn);
> +      insn.pc = insn.pc + insn.size;
> +    }
> +}
> +
> +/* Update btrace in the case of an exception.  */
> +
> +static void
> +cs_etm_update_btrace_with_exception (const struct cs_etm_decoder *etm_decoder,
> +				      const ocsd_generic_trace_elem *elem)
> +{
> +  gdb_assert (elem->elem_type == OCSD_GEN_TRC_ELEM_EXCEPTION);
> +
> +  struct thread_info *tp = etm_decoder->t_info;
> +  struct btrace_thread_info *btinfo = &tp->btrace;
> +
> +  /* Handle the implementation of breakpoints in gdb for arm (v7) architecture
> +     using undefined instructions.  */
> +  if (etm_decoder->arch_version == ARCH_V7)
> +    {
> +      if (elem->exception_number
> +	   == CS_ETMV3_4_CORTEX_A_R_EXCEPTION_UNDEFINED_INSTRUCTION)
> +	{
> +	  DEBUG ("handle breakpoints implementation in gdb for ARMv7");
> +	  ftrace_remove_last_insn (btinfo);
> +	}
> +    }
> +}
> +
> +/* Update btrace in the case of a trace on.  */
> +
> +static void
> +cs_etm_update_btrace_with_trace_on (const struct cs_etm_decoder *etm_decoder,
> +				     const ocsd_generic_trace_elem *elem)
> +{
> +  gdb_assert (elem->elem_type == OCSD_GEN_TRC_ELEM_TRACE_ON);
> +
> +  if (elem->trace_on_reason != TRACE_ON_NORMAL)
> +    {
> +      struct thread_info *tp = etm_decoder->t_info;
> +      struct btrace_thread_info *btinfo = &tp->btrace;
> +      ftrace_new_gap (btinfo, elem->trace_on_reason, etm_decoder->gaps);
> +    }
> +}
> +
> +/* Callback function when a ocsd_generic_trace_elem is emitted.  */
> +
> +static ocsd_datapath_resp_t
> +cs_etm_trace_element_callback (const void *context,
> +				const ocsd_trc_index_t index,
> +				const uint8_t trace_chan_id,
> +				const ocsd_generic_trace_elem *elem)
> +{
> +  if (record_debug != 0)
> +    {
> +      char str_buffer[128];
> +      if (ocsd_gen_elem_str (elem, str_buffer, 128) == OCSD_OK)
> +	DEBUG ("ETM trace_element: index= %s, channel= 0x%x, %s",
> +	       pulongest (index), trace_chan_id, str_buffer);
> +    }
> +
> +  const struct cs_etm_decoder *etm_decoder = (struct cs_etm_decoder *)context;
> +  gdb_assert (etm_decoder->t_info != nullptr);
> +
> +  switch (elem->elem_type)
> +    {
> +    case OCSD_GEN_TRC_ELEM_TRACE_ON:
> +      cs_etm_update_btrace_with_trace_on (etm_decoder, elem);
> +      break;
> +
> +    case OCSD_GEN_TRC_ELEM_INSTR_RANGE:
> +      cs_etm_update_btrace_with_inst_range (etm_decoder, elem);
> +      break;
> +
> +    case OCSD_GEN_TRC_ELEM_EXCEPTION:
> +      cs_etm_update_btrace_with_exception (etm_decoder, elem);
> +      break;
> +
> +    /* Not yet handled types, but may be supported in the future.  */
> +    case OCSD_GEN_TRC_ELEM_UNKNOWN:
> +    case OCSD_GEN_TRC_ELEM_EO_TRACE:
> +    case OCSD_GEN_TRC_ELEM_NO_SYNC:
> +    case OCSD_GEN_TRC_ELEM_EXCEPTION_RET:
> +    case OCSD_GEN_TRC_ELEM_TIMESTAMP:
> +    case OCSD_GEN_TRC_ELEM_PE_CONTEXT:
> +    case OCSD_GEN_TRC_ELEM_ADDR_NACC:
> +    case OCSD_GEN_TRC_ELEM_CYCLE_COUNT:
> +    case OCSD_GEN_TRC_ELEM_ADDR_UNKNOWN:
> +    case OCSD_GEN_TRC_ELEM_EVENT:
> +    case OCSD_GEN_TRC_ELEM_SWTRACE:
> +    case OCSD_GEN_TRC_ELEM_CUSTOM:
> +    default:
> +      break;
> +    }
> +  return OCSD_RESP_CONT;
> +}
> +
> +/* Callback to print error log.  */
> +
> +static void cs_etm_print_error_log (const void *p_context, const char *psz_msg_str, const int str_len)
> +{
> +    char string_buffer[128];
> +    memcpy (string_buffer, psz_msg_str, std::min(str_len, 127));
> +    if (str_len >127)

Space before operator and literal.

> +      string_buffer[127] = 0;
> +    else
> +      string_buffer[str_len] = 0;
> +    warning (_("Processing ETM traces failed with error: %s"), psz_msg_str);
> +}
> +
> +/* Create a cs_etm_decoder and initialize it.  */
> +
> +static ocsd_err_t
> +cs_etm_create_decoder (struct cs_etm_trace_params *t_params,
> +			struct cs_etm_decoder *decoder)
> +{
> +  ocsd_err_t errcode;
> +  const char *decoder_name;
> +  ocsd_etmv3_cfg config_etmv3;
> +  ocsd_etmv4_cfg config_etmv4;
> +  void *trace_config;
> +  switch (t_params->protocol)
> +    {
> +    case OCSD_PROTOCOL_ETMV3:
> +    case OCSD_PROTOCOL_PTM:
> +      cs_etm_get_etmv3_config (t_params, &config_etmv3);
> +      decoder_name = (t_params->protocol == OCSD_PROTOCOL_ETMV3)
> +		      ? OCSD_BUILTIN_DCD_ETMV3 : OCSD_BUILTIN_DCD_PTM;
> +      trace_config = &config_etmv3;
> +      decoder->arch_version = ARCH_V7;
> +      break;
> +
> +    case OCSD_PROTOCOL_ETMV4I:
> +      cs_etm_get_etmv4_config (t_params, &config_etmv4);
> +      decoder_name = OCSD_BUILTIN_DCD_ETMV4I;
> +      trace_config = &config_etmv4;
> +      decoder->arch_version = ARCH_V8;
> +      break;
> +
> +    default:
> +      decoder->arch_version = ARCH_UNKNOWN;
> +      warning (_("cs_etm_create_decoder: Unknown architecture version"));

It might be useful to print the hex value of the architecture version, 
in case we run into this and want to figure out what the value is.

> +      return OCSD_ERR_NOT_INIT;
> +
> +  }
> +  uint8_t csid;
> +  errcode = ocsd_dt_create_decoder (decoder->dcd_tree, decoder_name,
> +			      OCSD_CREATE_FLG_FULL_DECODER,
> +			      trace_config, &csid);
> +  if (errcode != OCSD_OK)
> +    {
> +      warning (_("ocsd_dt_create_decoder failed with error: %d"), errcode);
> +      return errcode;
> +    }
> +
> +  errcode = ocsd_dt_set_gen_elem_outfn (decoder->dcd_tree,
> +					 cs_etm_trace_element_callback,
> +					 decoder);
> +  if (errcode != OCSD_OK)
> +    {
> +      warning (_("ocsd_dt_set_gen_elem_outfn failed failed with error: %d"),
> +		   errcode);
> +      return errcode;
> +    }
> +
> +  errcode = ocsd_def_errlog_init (OCSD_ERR_SEV_ERROR, 1);
> +  if (errcode != OCSD_OK)
> +    {
> +      warning (_("ocsd_def_errlog_init failed failed with error: %d"),
> +		   errcode);
> +      return errcode;
> +    }
> +
> +  /* Initialize error printer.  */
> +  errcode = ocsd_def_errlog_config_output (C_API_MSGLOGOUT_FLG_NONE, nullptr);
> +  if (errcode != OCSD_OK)
> +    {
> +      warning (_("ocsd_def_errlog_init failed failed with error: %d"),
> +		   errcode);

In the error message, this should be "ocsd_def_errlog_config" instead of 
"ocsd_def_errlog_init".

> +      return errcode;
> +    }
> +
> +  errcode = ocsd_def_errlog_set_strprint_cb (decoder->dcd_tree, nullptr,
> +					      cs_etm_print_error_log);
> +  if (errcode != OCSD_OK)
> +    {
> +      warning (_("ocsd_def_errlog_set_strprint_cb failed failed with error: %d"),
> +		   errcode);
> +      return errcode;
> +    }
> +
> +  decoder->prev_return = OCSD_RESP_CONT;
> +  return OCSD_OK;
> +}
> +
> +/* Free a cs_etm_decoder.  */
> +
> +static void
> +cs_etm_free_decoder (struct cs_etm_decoder *decoder)
> +{
> +  if (decoder == nullptr)
> +    return;
> +
> +  ocsd_destroy_dcd_tree (decoder->dcd_tree);
> +  decoder->dcd_tree = nullptr;
> +  decoder->t_info = nullptr;
> +  xfree (decoder);
> +}
> +
> +/* Allocate a cs_etm_decoder and initialize it.  */
> +
> +static struct cs_etm_decoder *
> +cs_etm_alloc_decoder (struct thread_info *tp, int cpu_count,
> +		      const struct cs_etm_decoder_params * d_params,
> +		      std::vector<cs_etm_trace_params> * t_params)
> +{
> +  ocsd_dcd_tree_src_t src_type = OCSD_TRC_SRC_SINGLE;
> +  uint32_t deformatterCfgFlags = 0;
> +
> +  gdb_assert (d_params != nullptr);
> +
> +  if (d_params->formatted)
> +    src_type = OCSD_TRC_SRC_FRAME_FORMATTED;
> +  if (d_params->frame_aligned)
> +    deformatterCfgFlags |= OCSD_DFRMTR_FRAME_MEM_ALIGN;
> +  if (d_params->fsyncs)
> +    deformatterCfgFlags |= OCSD_DFRMTR_HAS_FSYNCS;
> +  if (d_params->hsyncs)
> +    deformatterCfgFlags |= OCSD_DFRMTR_HAS_HSYNCS;
> +  if (d_params->reset_on_4x_sync)
> +    deformatterCfgFlags |= OCSD_DFRMTR_RESET_ON_4X_FSYNC;
> +
> +  dcd_tree_handle_t dcdtree_handle;
> +  dcdtree_handle = ocsd_create_dcd_tree (src_type, deformatterCfgFlags);
> +
> +  if (dcdtree_handle == C_API_INVALID_TREE_HANDLE)
> +    {
> +      DEBUG ("ocsd_create_dcd_tree failed");
> +      return nullptr;
> +    }
> +  struct cs_etm_decoder *decoder;
> +
> +  decoder = (struct cs_etm_decoder*) xmalloc (sizeof (struct cs_etm_decoder));
> +  decoder->dcd_tree = dcdtree_handle;
> +
> +  for (int i = 0; i < cpu_count; i++)
> +    {
> +      ocsd_err_t errcode = cs_etm_create_decoder (&(t_params->at (i)), decoder);
> +      if (errcode != OCSD_OK)
> +	{
> +	  cs_etm_free_decoder (decoder);
> +	  return nullptr;
> +	}
> +    }
> +
> +  decoder->t_info = tp;
> +  return decoder;
> +}
> +
> +/* A callback function to allow the trace decoder to read the inferior's
> +   memory.
> +   Returns the number of bytes actually read, or 0 for access error  */

Nit. Period after the last sentence.

> +
> +static uint32_t
> +btrace_etm_readmem_callback (const void *p_context, const ocsd_vaddr_t address,
> +			      const ocsd_mem_space_acc_t mem_space,
> +			      const uint32_t reqBytes, uint8_t *byteBuffer)
> +{
> +  int result = (int) reqBytes;
> +  try
> +  {
> +      int errcode = target_read_code ((CORE_ADDR) address, byteBuffer, reqBytes);
> +      if (errcode != 0)
> +	result = 0;
> +  }
> +  catch (const gdb_exception_error &error)
> +  {
> +      result = 0;
> +  }
> +
> +  return result;
> +}
> +
> +/* Add memory access callback to the decoder.  */
> +
> +static ocsd_err_t
> +cs_etm_add_mem_access_callback (struct cs_etm_decoder *decoder,
> +				 CORE_ADDR start, CORE_ADDR end,
> +				 Fn_MemAcc_CB p_cb_func)
> +{
> +  ocsd_err_t error;
> +  error = ocsd_dt_add_callback_mem_acc (decoder->dcd_tree,
> +					 (ocsd_vaddr_t) start,
> +					 (ocsd_vaddr_t) end,
> +					 OCSD_MEM_SPACE_ANY,
> +					 p_cb_func, decoder);
> +  if (error == OCSD_OK)
> +    decoder->mem_access = p_cb_func;
> +
> +  return error;
> +}
> +
> +/* Process an etm traces data block.
> +   In case of an error it resets the decoder and tries to process further.  */
> +
> +static void
> +cs_etm_process_data_block (struct btrace_thread_info *btinfo,
> +			   struct cs_etm_decoder *decoder,
> +			   uint64_t index, const uint8_t *buf,
> +			   size_t len, size_t *consumed)
> +{
> +  ocsd_datapath_resp_t data_path_return = OCSD_RESP_CONT;
> +  size_t processed = 0;
> +  uint32_t count;
> +
> +  while (processed < len)
> +    {
> +      if (OCSD_DATA_RESP_IS_CONT (data_path_return))
> +	{
> +	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
> +					OCSD_OP_DATA,
> +					index + processed, len - processed,
> +					&buf[processed], &count);
> +	  processed += count;
> +	
> +	}
> +      else if (OCSD_DATA_RESP_IS_WAIT (data_path_return))
> +	{
> +	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
> +					OCSD_OP_FLUSH,
> +					0, 0, nullptr, nullptr);
> +	}
> +      else
> +	{
> +	  warning (_("error %d in ocsd_dt_process_data after processing %zu"),
> +		      data_path_return, processed);
> +	  ftrace_new_gap (btinfo, data_path_return, decoder->gaps);
> +	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
> +					OCSD_OP_RESET,
> +					0, 0, nullptr, nullptr);
> +	  //todo: shall we increase processed? by 1, or 4 do we need to manually align to 16 bytes?

I'm not sure about the above. If you want to leave a hint for future 
developers (or for yourself), maybe you could elaborate a bit more on 
this. And use TODO, uppercase.

> +	}
> +      DEBUG_FTRACE ("ocsd_dt_process_data returned with error code %d."
> +			 " Consumed = 0x%zx",
> +			 data_path_return,
> +			 processed);
> +    }
> +  *consumed = processed;
> +}
> +
> +/* Print all functions in a btrace.  */
> +
> +static void
> +btrace_print_all (struct btrace_thread_info *btinfo)
> +{
> +  for (const btrace_function &function : btinfo->functions)
> +    ftrace_debug (&function, "");
> +}
> +
> +static void
> +btrace_compute_ftrace_etm (struct thread_info *tp,
> +			   const struct btrace_data_etm *btrace,
> +			   std::vector<unsigned int> &gaps)
> +{
> +  DEBUG_FTRACE ("btrace->size is 0x%x for thread %s",
> +		(unsigned int)(btrace->size), print_thread_id (tp));
> +  if (btrace->size == 0)
> +    return;
> +
> +  struct btrace_thread_info *btinfo = &tp->btrace;
> +  if (btinfo->functions.empty ())
> +    btinfo->level = 0;
> +
> +  struct cs_etm_decoder *decoder
> +   = cs_etm_alloc_decoder (tp, btrace->config.cpu_count,
> +			    &(btrace->config.etm_decoder_params),
> +			    btrace->config.etm_trace_params);
> +  if (decoder == nullptr)
> +    error (_("Failed to allocate ARM CoreSight ETM Trace decoder."));
> +
> +  ocsd_err_t ocsd_error
> +   = cs_etm_add_mem_access_callback (decoder,
> +				      (CORE_ADDR) 0x0L, (CORE_ADDR) -1L,
> +				      btrace_etm_readmem_callback);
> +  if (ocsd_error != OCSD_OK)
> +    error (_("Failed to add CoreSight Trace decoder memory access callback."));
> +
> +  size_t consumed;
> +  cs_etm_process_data_block (btinfo, decoder, 0, btrace->data, btrace->size,
> +				 &consumed);
> +  DEBUG_FTRACE ("CoreSight Trace processed. Consumed 0x%zx", consumed);
> +
> +  ftrace_compute_global_level_offset (btinfo);
> +  btrace_add_pc (tp);
> +  btrace_print_all (btinfo);
> +  cs_etm_free_decoder (decoder);
> +}
> +#else /*    defined (HAVE_LIBOPENCSD_C_API)    */
> +
> +static void
> +btrace_compute_ftrace_etm (struct thread_info *tp,
> +			   const struct btrace_data_etm *btrace,
> +			   std::vector<unsigned int> &gaps)
> +{
> +  internal_error (__FILE__, __LINE__, _("Unexpected branch trace format."));
> +}
> +#endif /*    defined (HAVE_LIBOPENCSD_C_API)    */
> +
>   /* Compute the function branch trace from a block branch trace BTRACE for
>      a thread given by BTINFO.  If CPU is not NULL, overwrite the cpu in the
>      branch trace configuration.  This is currently only used for the PT
> @@ -1534,6 +2135,10 @@ btrace_compute_ftrace_1 (struct thread_info *tp,
>   
>         btrace_compute_ftrace_pt (tp, &btrace->variant.pt, gaps);
>         return;
> +
> +    case BTRACE_FORMAT_ETM:
> +      btrace_compute_ftrace_etm (tp, &btrace->variant.etm, gaps);
> +      return;
>       }
>   
>     internal_error (__FILE__, __LINE__, _("Unknown branch trace format."));
> @@ -1602,6 +2207,10 @@ btrace_enable (struct thread_info *tp, const struct btrace_config *conf)
>     if (conf->format == BTRACE_FORMAT_PT)
>       error (_("Intel Processor Trace support was disabled at compile time."));
>   #endif /* !defined (HAVE_LIBIPT) */
> +#if !defined (HAVE_LIBOPENCSD_C_API)
> +  if (conf->format == BTRACE_FORMAT_ETM)
> +    error (_("ARM CoreSight Trace support was disabled at compile time."));
> +#endif /* !defined (HAVE_LIBOPENCSD_C_API) */
>   
>     DEBUG ("enable thread %s (%s)", print_thread_id (tp),
>   	 target_pid_to_str (tp->ptid).c_str ());
> @@ -1794,6 +2403,10 @@ btrace_stitch_trace (struct btrace_data *btrace, struct thread_info *tp)
>       case BTRACE_FORMAT_PT:
>         /* Delta reads are not supported.  */
>         return -1;
> +
> +    case BTRACE_FORMAT_ETM:
> +      /* Delta reads are not supported.  */
> +      return -1;
>       }
>   
>     internal_error (__FILE__, __LINE__, _("Unknown branch trace format."));
> @@ -3416,6 +4029,11 @@ maint_info_btrace_cmd (const char *args, int from_tty)
>         }
>         break;
>   #endif /* defined (HAVE_LIBIPT)  */
> +#if defined (HAVE_LIBOPENCSD_C_API)
> +    case BTRACE_FORMAT_ETM:
> +      printf_unfiltered (_("Version: %s.\n"), ocsd_get_version_str ());
> +      break;
> +#endif /* defined (HAVE_LIBOPENCSD_C_API) */
>       }
>   }
>   
> diff --git a/gdb/btrace.h b/gdb/btrace.h
> index 8f6ce32103d..92e14b6aadf 100644
> --- a/gdb/btrace.h
> +++ b/gdb/btrace.h
> @@ -34,6 +34,12 @@
>   #  include <intel-pt.h>
>   #endif
>   
> +#if defined (HAVE_LIBOPENCSD_C_API)
> +#  include <opencsd/c_api/opencsd_c_api.h>
> +#  include <opencsd/etmv4/trc_pkt_types_etmv4.h>
> +#  include <opencsd/ocsd_if_types.h>
> +#endif
> +
>   #include <vector>
>   
>   struct thread_info;
> @@ -59,7 +65,15 @@ enum btrace_insn_class
>   enum btrace_insn_flag
>   {
>     /* The instruction has been executed speculatively.  */
> -  BTRACE_INSN_FLAG_SPECULATIVE = (1 << 0)
> +  BTRACE_INSN_FLAG_SPECULATIVE =	(1 << 0),
> +  BTRACE_INSN_FLAG_ISA_ARM =		(1 << 24),
> +  BTRACE_INSN_FLAG_ISA_THUMB2 =	(2 << 24),
> +  BTRACE_INSN_FLAG_ISA_AARCH64 =	(3 << 24),
> +  BTRACE_INSN_FLAG_ISA_TEE =		(4 << 24),
> +  BTRACE_INSN_FLAG_ISA_JAZELLE =	(5 << 24),
> +  BTRACE_INSN_FLAG_ISA_CUSTOM =	(6 << 24),
> +  BTRACE_INSN_FLAG_ISA_UNKNOWN =	(7 << 24),
> +  BTRACE_INSN_FLAG_ISA_MASK =		(15 << 24)

Indentation looks off for the last line above.

>   };
>   DEF_ENUM_FLAGS_TYPE (enum btrace_insn_flag, btrace_insn_flags);
>   
> diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
> index 16ffb76272b..57b9c8ec487 100644
> --- a/gdb/record-btrace.c
> +++ b/gdb/record-btrace.c
> @@ -44,6 +44,7 @@
>   #include "cli/cli-style.h"
>   #include "async-event.h"
>   #include <forward_list>
> +#include "arch/arm.h"
>   
>   static const target_info record_btrace_target_info = {
>     "record-btrace",
> @@ -1557,6 +1558,38 @@ record_btrace_target::remove_breakpoint (struct gdbarch *gdbarch,
>     return ret;
>   }
>   
> +/* Reconstruct the instruction set state bits of CPSR register
> +   according to instruction flags.
> +   See Table A2-1 in DDI0406B_arm_architecture_reference_manual
> +   for more details.  */
> +
> +static unsigned int
> +cs_etm_reconstruct_cpsr_iset_state (const struct btrace_insn *insn)
> +{
> +  switch (insn->flags & BTRACE_INSN_FLAG_ISA_MASK)
> +    {
> +    case BTRACE_INSN_FLAG_ISA_ARM:
> +      /* ARM state: J and T bits are not set.  */
> +      return 0;
> +
> +    case BTRACE_INSN_FLAG_ISA_THUMB2:
> +      /* THUMB state: J bit is not set, T bit is set.  */
> +      return 0x20;
> +
> +    case BTRACE_INSN_FLAG_ISA_TEE:
> +      /* THUMB EE state: J and T bits are set.  */
> +      return 0x1000020;
> +
> +    case BTRACE_INSN_FLAG_ISA_JAZELLE:
> +      /* JAZELLE state: J bit is set, T bit is not set.  */
> +      return 0x1000000;
> +
> +    default:
> +      /* Default is ARM mode.  */
> +      return 0;
> +    }
> +}
> +
>   /* The fetch_registers method of target record-btrace.  */
>   
>   void
> @@ -1581,16 +1614,32 @@ record_btrace_target::fetch_registers (struct regcache *regcache, int regno)
>         pcreg = gdbarch_pc_regnum (gdbarch);
>         if (pcreg < 0)
>   	return;
> -
> -      /* We can only provide the PC register.  */
> -      if (regno >= 0 && regno != pcreg)
> +      /* We can only provide the PC or CPSR registers here.  */
> +      if (regno >= 0 && !(regno == pcreg || regno == ARM_PS_REGNUM))
>   	return;
>   
>         insn = btrace_insn_get (replay);
> -      gdb_assert (insn != NULL);
> +      gdb_assert (insn != nullptr);
>   
> +      if ((regno < 0) || (regno == pcreg))
> +	{
>   	  regcache->raw_supply (regno, &insn->pc);
>   	}
> +      if ((regno < 0) || (regno == ARM_PS_REGNUM))
> +	{
> +	  /*  Provide CPSR register in the case of an armv7 target.  */
> +	  const struct target_desc *tdesc = gdbarch_target_desc (gdbarch);
> +
> +	  const char *tdesc_name = tdesc_architecture_name (tdesc);
> +	  if (strcmp (tdesc_name, "arm") == 0)
> +	    {
> +	      int cpsr;
> +	      cpsr = cs_etm_reconstruct_cpsr_iset_state (insn);

Put the declaration and initialization of cpsr in the same line.

> +	      regcache->raw_supply (regno, &cpsr);
> +	    }
> +	}
> +      return;
> +    }
>     else
>       this->beneath ()->fetch_registers (regcache, regno);
>   }
> diff --git a/gdbsupport/btrace-common.cc b/gdbsupport/btrace-common.cc
> index 79ff21de44b..6005b0d15e4 100644
> --- a/gdbsupport/btrace-common.cc
> +++ b/gdbsupport/btrace-common.cc
> @@ -86,6 +86,12 @@ btrace_data::fini ()
>       case BTRACE_FORMAT_PT:
>         xfree (variant.pt.data);
>         return;
> +
> +    case BTRACE_FORMAT_ETM:
> +      delete variant.etm.config.etm_trace_params;
> +      variant.etm.config.etm_trace_params = nullptr;
> +      xfree (variant.etm.data);
> +      return;
>       }
>   
>     internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
> @@ -106,6 +112,9 @@ btrace_data::empty () const
>   
>       case BTRACE_FORMAT_PT:
>         return (variant.pt.size == 0);
> +
> +    case BTRACE_FORMAT_ETM:
> +      return (variant.etm.size == 0);
>       }
>   
>     internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
> @@ -191,6 +200,36 @@ btrace_data_append (struct btrace_data *dst,
>   	  }
>   	}
>         return 0;
> +
> +    case BTRACE_FORMAT_ETM:
> +      switch (dst->format)
> +	{
> +	default:
> +	  return -1;
> +
> +	case BTRACE_FORMAT_NONE:
> +	  dst->format = BTRACE_FORMAT_ETM;
> +	  dst->variant.etm.data = nullptr;
> +	  dst->variant.etm.size = 0;
> +
> +	/* fall-through.  */
> +	case BTRACE_FORMAT_ETM:
> +	  {
> +	    size_t size = src->variant.etm.size + dst->variant.etm.size;
> +	    gdb_byte *data = (gdb_byte *) xmalloc (size);
> +
> +	    if (dst->variant.etm.size > 0)
> +	      memcpy (data, dst->variant.etm.data, dst->variant.etm.size);
> +	    memcpy (data + dst->variant.etm.size, src->variant.etm.data,
> +		    src->variant.etm.size);
> +
> +	    xfree (dst->variant.etm.data);
> +
> +	    dst->variant.etm.data = data;
> +	    dst->variant.etm.size = size;
> +	  }
> +	}
> +      return 0;
>       }
>   
>     internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
> diff --git a/gdbsupport/btrace-common.h b/gdbsupport/btrace-common.h
> index 153b977723a..ee05ecb8b10 100644
> --- a/gdbsupport/btrace-common.h
> +++ b/gdbsupport/btrace-common.h
> @@ -187,6 +187,109 @@ struct btrace_data_pt
>     size_t size;
>   };
>   
> +/* Parameters needed for decoding ETMv3 traces.
> +   See open source coresight trace decoder library (opencsd)
> +   for further details.  */
> +struct cs_etmv3_trace_params
> +{
> +  /* ETMv3 Main Control Register.  */
> +  uint32_t reg_ctrl;
> +
> +  /* ETMv3 Trace ID Register.  */
> +  uint32_t reg_trc_id;
> +
> +  /* ETMv3 Configuration Code Extension Register.  */
> +  uint32_t reg_ccer;
> +
> +  /* ETMv3 ID Register.  */
> +  uint32_t reg_idr;
> +};
> +
> +/* Parameters needed for decoding ETMv4 traces.
> +   See open source coresight trace decoder library (opencsd)
> +   for further details.  */
> +struct cs_etmv4_trace_params
> +{
> +  /* ETMv4 ID Register 0.  */
> +  uint32_t reg_idr0;
> +
> +  /* ETMv4 ID Register 1.  */
> +  uint32_t reg_idr1;
> +
> +  /* ETMv4 ID Register 2.  */
> +  uint32_t reg_idr2;
> +
> +  /* ETMv4 ID Register 8.  */
> +  uint32_t reg_idr8;
> +
> +  /* ETMv4 Config Register.  */
> +  uint32_t reg_configr;
> +
> +  /* ETMv4 Trace ID Register.  */
> +  uint32_t reg_traceidr;
> +};
> +
> +/* Parameters of trace source.  */
> +struct cs_etm_trace_params
> +{
> +  /* Architecture version of trace source.  */
> +  int arch_ver;
> +  /* Core profile of the trace source.  */
> +  int core_profile;
> +  /* Traces protocol.  */
> +  int protocol;
> +  union {
> +    struct cs_etmv3_trace_params etmv3;
> +    struct cs_etmv4_trace_params etmv4;
> +  };
> +};
> +
> +/* Parameters of trace sink/decoder.  */
> +struct cs_etm_decoder_params
> +{
> +  uint8_t formatted:1,     /* Formatted input, or single source input.  */
> +  fsyncs	   :1,     /* Formatted data has fsyncs.  */
> +  hsyncs	   :1,     /* Formatted data has hsyncs.  */
> +  frame_aligned    :1,     /* Formatted frames are memory aligned.  */
> +  reset_on_4x_sync :1,     /* Reset decoders at 4x consecutive fsyncs.  */
> +  __res	    :3;     /* Reserved, not used.  */
> +};

I'm not sure the struct format above matches GDB's style. We usually 
declare each field individually for structs. I'll leave this to other 
maintainers.

> +
> +/* Configuration information to go with the etm trace data.  */
> +struct btrace_data_etm_config
> +{
> +  /* Count of the CPUs (trace sources).  */
> +  int    cpu_count;
> +  /* List of traces sources parameters.  */
> +  std::vector<struct cs_etm_trace_params> *etm_trace_params;
> +  /* Trace sink parameters.  */
> +  struct cs_etm_decoder_params etm_decoder_params;
> +};
> +
> +/* Branch trace in ARM Processor Trace format.  */
> +struct btrace_data_etm
> +{
> +  /* Some configuration information to go with the data.  */
> +  struct btrace_data_etm_config config;
> +
> +  /* The trace data.  */
> +  gdb_byte *data;
> +
> +  /* The size of DATA in bytes.  */
> +  size_t size;
> +
> +  /* Trace id for this thread.
> +     On a linux system, trace_id is assigned per cpu. The kernel copies
> +     the traces of each thread in a dedicated ring buffer. By this,
> +     traces belonging to different threads are de-multiplexed.
> +     On an RTOS system, especially when routing the traces outside of the SoC,
> +     the OS has no other mean for de-multiplexing the traces than
> +     the trace_id. The hardware (ETM IP) reserves 7 bits for the trace_id.
> +     On linux system trace id is not needed, set it to 0xFF to ignore it
> +     during parsing.  */
> +  uint8_t trace_id;
> +};
> +
>   /* The branch trace data.  */
>   struct btrace_data
>   {
> @@ -224,6 +327,9 @@ struct btrace_data
>   
>       /* Format == BTRACE_FORMAT_PT.  */
>       struct btrace_data_pt pt;
> +
> +    /* Format == BTRACE_FORMAT_ETM.  */
> +    struct btrace_data_etm etm;
>     } variant;
>   
>   private:
> 

Looks OK to me otherwise.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os
  2021-05-31 21:33 ` [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os Zied Guermazi
  2021-06-23  8:00   ` Metzger, Markus T
@ 2021-06-30 13:24   ` Luis Machado
  1 sibling, 0 replies; 35+ messages in thread
From: Luis Machado @ 2021-06-30 13:24 UTC (permalink / raw)
  To: Zied Guermazi, gdb-patches, markus.t.metzger

On 5/31/21 6:33 PM, Zied Guermazi wrote:
> This patch implement the lower layer for starting ad stopping
> ARM CoreSight tracing on linux targets for arm and aarch64
> 
> gdb/ChangeLog
> 
> 	* nat/linux-btrace.h (btrace_tinfo_etm): New.
> 	(btrace_target_info): add etm.
> 	* nat/linux-btrace.c (linux_enable_bts): get page size from sysconf.
> 	(linux_enable_pt): get page size from sysconf.
> 	(perf_event_etm_event_type): New.
> 	(perf_event_etm_event_sink): New.
> 	(linux_enable_etm): New.
> 	(linux_enable_btrace): add enabling etm traces.
> 	(linux_disable_bts): get page size from sysconf.
> 	(linux_disable_pt): get page size from sysconf.
> 	(linux_disable_etm): New.
> 	(linux_disable_btrace): add disabling etm traces.
> 	(get_cpu_count): New.
> 	(cs_etm_is_etmv4): New.
> 	(cs_etm_get_register): New.
> 	(coresight_get_trace_id): New.
> 	(fill_etm_trace_params): New.
> 	(linux_fill_btrace_etm_config): New.
> 	(linux_read_etm): New.
> 	(linux_read_btrace): add reading etm traces.
> 	* arm-linux-nat.c (arm_linux_nat_target::enable_btrace): New.
> 	(arm_linux_nat_target::disable_btrace): New.
> 	(arm_linux_nat_target::teardown_btrace): New.
> 	(arm_linux_nat_target::read_btrace): New.
> 	(arm_linux_nat_target::btrace_conf): New.
> 	* aarch64-linux-nat.c (aarch64_linux_nat_target::enable_btrace): New.
> 	(aarch64_linux_nat_target::disable_btrace): New.
> 	(aarch64_linux_nat_target::teardown_btrace): New.
> 	(aarch64_linux_nat_target::read_btrace): New.
> 	(aarch64_linux_nat_target::btrace_conf): New.
> ---
>   gdb/aarch64-linux-nat.c |  68 +++++++
>   gdb/arm-linux-nat.c     |  68 +++++++
>   gdb/nat/linux-btrace.c  | 395 ++++++++++++++++++++++++++++++++++++++--
>   gdb/nat/linux-btrace.h  |  19 ++
>   4 files changed, 537 insertions(+), 13 deletions(-)
> 
> diff --git a/gdb/aarch64-linux-nat.c b/gdb/aarch64-linux-nat.c
> index 61224022f6a..4b629e1b3d2 100644
> --- a/gdb/aarch64-linux-nat.c
> +++ b/gdb/aarch64-linux-nat.c
> @@ -35,6 +35,7 @@
>   #include "nat/aarch64-linux.h"
>   #include "nat/aarch64-linux-hw-point.h"
>   #include "nat/aarch64-sve-linux-ptrace.h"
> +#include "nat/linux-btrace.h"
>   
>   #include "elf/external.h"
>   #include "elf/common.h"
> @@ -88,6 +89,17 @@ class aarch64_linux_nat_target final : public linux_nat_target
>     /* Override the GNU/Linux post attach hook.  */
>     void post_attach (int pid) override;
>   
> +  /* Override branch tracing.  */
> +  struct btrace_target_info *enable_btrace (ptid_t ptid,
> +				const struct btrace_config *conf) override;
> +  void disable_btrace (struct btrace_target_info *tinfo) override;
> +  void teardown_btrace (struct btrace_target_info *tinfo) override;
> +  enum btrace_error read_btrace (struct btrace_data *data,
> +				  struct btrace_target_info *btinfo,
> +				  enum btrace_read_type type) override;
> +  const struct btrace_config *btrace_conf (const struct btrace_target_info *)
> +				override;
> +
>     /* These three defer to common nat/ code.  */
>     void low_new_thread (struct lwp_info *lp) override
>     { aarch64_linux_new_thread (lp); }
> @@ -714,6 +726,62 @@ aarch64_linux_nat_target::post_attach (int pid)
>     linux_nat_target::post_attach (pid);
>   }
>   
> +/* Enable branch tracing.  */
> +
> +struct btrace_target_info *
> +aarch64_linux_nat_target::enable_btrace (ptid_t ptid,
> +					  const struct btrace_config *conf)
> +{
> +  struct btrace_target_info *tinfo = nullptr;
> +  try
> +    {
> +      tinfo = linux_enable_btrace (ptid, conf);
> +    }
> +  catch (const gdb_exception_error &exception)
> +    {
> +      error (_("Could not enable branch tracing for %s: %s"),
> +	     target_pid_to_str (ptid).c_str (), exception.what ());
> +    }
> +
> +  return tinfo;
> +}
> +
> +/* Disable branch tracing.  */
> +
> +void
> +aarch64_linux_nat_target::disable_btrace (struct btrace_target_info *tinfo)
> +{
> +  enum btrace_error errcode = linux_disable_btrace (tinfo);
> +
> +  if (errcode != BTRACE_ERR_NONE)
> +    error (_("Could not disable branch tracing."));

We should probably print the error code.

> +}
> +
> +/* Teardown branch tracing.  */
> +
> +void
> +aarch64_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)
> +{
> +  /* Ignore errors.  */
> +  linux_disable_btrace (tinfo);
> +}
> +
> +enum btrace_error
> +aarch64_linux_nat_target::read_btrace (struct btrace_data *data,
> +				       struct btrace_target_info *btinfo,
> +				       enum btrace_read_type type)
> +{
> +  return linux_read_btrace (data, btinfo, type);
> +}
> +
> +/* See to_btrace_conf in target.h.  */
> +
> +const struct btrace_config *
> +aarch64_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)
> +{
> +  return linux_btrace_conf (btinfo);
> +}
> +
>   /* Implement the "read_description" target_ops method.  */
>   
>   const struct target_desc *
> diff --git a/gdb/arm-linux-nat.c b/gdb/arm-linux-nat.c
> index 880ac0da044..32631b33328 100644
> --- a/gdb/arm-linux-nat.c
> +++ b/gdb/arm-linux-nat.c
> @@ -39,6 +39,7 @@
>   #include <sys/procfs.h>
>   
>   #include "nat/linux-ptrace.h"
> +#include "nat/linux-btrace.h"
>   #include "linux-tdep.h"
>   
>   /* Prototypes for supply_gregset etc.  */
> @@ -95,6 +96,17 @@ class arm_linux_nat_target final : public linux_nat_target
>   
>     const struct target_desc *read_description () override;
>   
> +  /* Override branch tracing.  */
> +  struct btrace_target_info *enable_btrace (ptid_t ptid,
> +				const struct btrace_config *conf) override;
> +  void disable_btrace (struct btrace_target_info *tinfo) override;
> +  void teardown_btrace (struct btrace_target_info *tinfo) override;
> +  enum btrace_error read_btrace (struct btrace_data *data,
> +				  struct btrace_target_info *btinfo,
> +				  enum btrace_read_type type) override;
> +  const struct btrace_config *btrace_conf (const struct btrace_target_info *)
> +				override;
> +
>     /* Override linux_nat_target low methods.  */
>   
>     /* Handle thread creation and exit.  */
> @@ -1190,6 +1202,62 @@ arm_linux_nat_target::watchpoint_addr_within_range (CORE_ADDR addr,
>     return start <= addr && start + length - 1 >= addr;
>   }
>   
> +/* Enable branch tracing.  */
> +
> +struct btrace_target_info *
> +arm_linux_nat_target::enable_btrace (ptid_t ptid,
> +				     const struct btrace_config *conf)
> +{
> +  struct btrace_target_info *tinfo = nullptr;
> +  try
> +    {
> +      tinfo = linux_enable_btrace (ptid, conf);
> +    }
> +  catch (const gdb_exception_error &exception)
> +    {
> +      error (_("Could not enable branch tracing for %s: %s"),
> +	     target_pid_to_str (ptid).c_str (), exception.what ());
> +    }
> +
> +  return tinfo;
> +}
> +
> +/* Disable branch tracing.  */
> +
> +void
> +arm_linux_nat_target::disable_btrace (struct btrace_target_info *tinfo)
> +{
> +  enum btrace_error errcode = linux_disable_btrace (tinfo);
> +
> +  if (errcode != BTRACE_ERR_NONE)
> +    error (_("Could not disable branch tracing."));

Same here. We should show the error code.

> +}
> +
> +/* Teardown branch tracing.  */
> +
> +void
> +arm_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)
> +{
> +  /* Ignore errors.  */
> +  linux_disable_btrace (tinfo);
> +}
> +
> +enum btrace_error
> +arm_linux_nat_target::read_btrace (struct btrace_data *data,
> +				   struct btrace_target_info *btinfo,
> +				   enum btrace_read_type type)
> +{
> +  return linux_read_btrace (data, btinfo, type);
> +}
> +
> +/* See to_btrace_conf in target.h.  */
> +
> +const struct btrace_config *
> +arm_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)
> +{
> +  return linux_btrace_conf (btinfo);
> +}
> +
>   /* Handle thread creation.  We need to copy the breakpoints and watchpoints
>      in the parent thread to the child thread.  */
>   void
> diff --git a/gdb/nat/linux-btrace.c b/gdb/nat/linux-btrace.c
> index 324f7ef0407..9ef182d774a 100644
> --- a/gdb/nat/linux-btrace.c
> +++ b/gdb/nat/linux-btrace.c
> @@ -32,6 +32,10 @@
>   
>   #include <sys/syscall.h>
>   
> +#if defined (HAVE_LIBOPENCSD_C_API)
> +#  include <opencsd/ocsd_if_types.h>
> +#endif
> +
>   #if HAVE_LINUX_PERF_EVENT_H && defined(SYS_perf_event_open)
>   #include <unistd.h>
>   #include <sys/mman.h>
> @@ -483,10 +487,11 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf)
>     scoped_fd fd (syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0));
>     if (fd.get () < 0)
>       diagnose_perf_event_open_fail ();
> +  long page_size = sysconf (_SC_PAGESIZE);
>   
>     /* Convert the requested size in bytes to pages (rounding up).  */
> -  pages = ((size_t) conf->size / PAGE_SIZE
> -	   + ((conf->size % PAGE_SIZE) == 0 ? 0 : 1));
> +  pages = ((size_t) conf->size / page_size
> +	   + ((conf->size % page_size) == 0 ? 0 : 1));
>     /* We need at least one page.  */
>     if (pages == 0)
>       pages = 1;
> @@ -505,17 +510,17 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf)
>         size_t length;
>         __u64 data_size;
>   
> -      data_size = (__u64) pages * PAGE_SIZE;
> +      data_size = (__u64) pages * page_size;
>   
>         /* Don't ask for more than we can represent in the configuration.  */
>         if ((__u64) UINT_MAX < data_size)
>   	continue;
>   
>         size = (size_t) data_size;
> -      length = size + PAGE_SIZE;
> +      length = size + page_size;
>   
>         /* Check for overflows.  */
> -      if ((__u64) length != data_size + PAGE_SIZE)
> +      if ((__u64) length != data_size + page_size)
>   	continue;
>   
>         errno = 0;
> @@ -530,7 +535,7 @@ linux_enable_bts (ptid_t ptid, const struct btrace_config_bts *conf)
>   
>     struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)
>       data.get ();
> -  data_offset = PAGE_SIZE;
> +  data_offset = page_size;
>   
>   #if defined (PERF_ATTR_SIZE_VER5)
>     if (offsetof (struct perf_event_mmap_page, data_size) <= header->size)
> @@ -613,7 +618,8 @@ linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
>       diagnose_perf_event_open_fail ();
>   
>     /* Allocate the configuration page. */
> -  scoped_mmap data (nullptr, PAGE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED,
> +  long page_size = sysconf (_SC_PAGESIZE);
> +  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
>   		    fd.get (), 0);
>     if (data.get () == MAP_FAILED)
>       error (_("Failed to map trace user page: %s."), safe_strerror (errno));
> @@ -624,8 +630,8 @@ linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
>     header->aux_offset = header->data_offset + header->data_size;
>   
>     /* Convert the requested size in bytes to pages (rounding up).  */
> -  pages = ((size_t) conf->size / PAGE_SIZE
> -	   + ((conf->size % PAGE_SIZE) == 0 ? 0 : 1));
> +  pages = ((size_t) conf->size / page_size
> +	   + ((conf->size % page_size) == 0 ? 0 : 1));
>     /* We need at least one page.  */
>     if (pages == 0)
>       pages = 1;
> @@ -644,7 +650,7 @@ linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
>         size_t length;
>         __u64 data_size;
>   
> -      data_size = (__u64) pages * PAGE_SIZE;
> +      data_size = (__u64) pages * page_size;
>   
>         /* Don't ask for more than we can represent in the configuration.  */
>         if ((__u64) UINT_MAX < data_size)
> @@ -689,6 +695,154 @@ linux_enable_pt (ptid_t ptid, const struct btrace_config_pt *conf)
>   
>   #endif /* !defined (PERF_ATTR_SIZE_VER5) */
>   
> +/* Determine the etm event type.  */
> +
> +static int
> +perf_event_etm_event_type ()
> +{
> +  static const char filename[] = "/sys/bus/event_source/devices/cs_etm/type";
> +
> +  errno = 0;
> +  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
> +  if (file.get () == nullptr)
> +    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
> +
> +  int type, found = fscanf (file.get (), "%d", &type);
> +  if (found != 1)
> +    error (_("Failed to read the ETM event type from %s."), filename);
> +
> +  return type;
> +}
> +
> +/* Get the sink hash to use in the event attributes.  */
> +
> +static unsigned int
> +perf_event_etm_event_sink (const struct btrace_config_etm *conf)
> +{
> +  char filename[PATH_MAX];
> +
> +  snprintf (filename, PATH_MAX,
> +	    "/sys/bus/event_source/devices/cs_etm/sinks/%s", conf->sink);
> +  errno = 0;
> +  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
> +  if (file.get () == nullptr)
> +    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
> +
> +  unsigned int sink;
> +  int  found = fscanf (file.get (), "0x%x", &sink);
> +  if (found != 1)
> +    error (_("Failed to read the ETM sink from %s."), filename);
> +
> +  return sink;
> +}
> +
> +/* Enable ARM CoreSight ETM tracing.  */
> +
> +static struct btrace_target_info *
> +linux_enable_etm (ptid_t ptid, const struct btrace_config_etm *conf)
> +{
> +  struct btrace_tinfo_etm *etm;
> +  size_t pages;
> +  int pid, pg;
> +
> +  pid = ptid.lwp ();
> +  if (pid == 0)
> +    pid = ptid.pid ();
> +
> +  gdb::unique_xmalloc_ptr<btrace_target_info> tinfo
> +    (XCNEW (btrace_target_info));
> +  tinfo->ptid = ptid;
> +
> +  tinfo->conf.format = BTRACE_FORMAT_ETM;
> +  etm = &tinfo->variant.etm;
> +
> +  etm->attr.type = perf_event_etm_event_type ();
> +  etm->attr.size = sizeof (etm->attr);
> +
> +  etm->attr.sample_type = PERF_SAMPLE_CPU;
> +  etm->attr.read_format = PERF_FORMAT_ID;
> +  etm->attr.sample_id_all = 1;
> +  etm->attr.enable_on_exec = 1;
> +  etm->attr.exclude_kernel = 1;
> +  etm->attr.exclude_hv = 1;
> +  etm->attr.exclude_idle = 1;
> +  if (conf->sink != nullptr)
> +    {
> +      if (strcmp (conf->sink, "default") != 0)
> +	etm->attr.config2 = perf_event_etm_event_sink (conf);
> +    }
> +
> +  errno = 0;
> +  scoped_fd fd (syscall (SYS_perf_event_open, &etm->attr, pid, -1, -1, 0));
> +  if (fd.get () < 0)
> +    diagnose_perf_event_open_fail ();
> +
> +  /* Allocate the configuration page.  */
> +  long page_size = sysconf (_SC_PAGESIZE);
> +  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
> +		    fd.get (), 0);
> +  if (data.get () == MAP_FAILED)
> +    error (_("Failed to map trace user page: %s."), safe_strerror (errno));
> +
> +  struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)
> +    data.get ();
> +
> +  header->aux_offset = header->data_offset + header->data_size;
> +  /* Convert the requested size in bytes to pages (rounding up).  */
> +  pages = ((size_t) conf->size / page_size
> +	   + ((conf->size % page_size) == 0 ? 0 : 1));
> +  /* We need at least one page.  */
> +  if (pages == 0)
> +    pages = 1;
> +
> +  /* The buffer size can be requested in powers of two pages.  Adjust PAGES
> +     to the next power of two.  */
> +  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
> +    if ((pages & ((size_t) 1 << pg)) != 0)
> +      pages += ((size_t) 1 << pg);
> +
> +  /* We try to allocate the requested size.
> +     If that fails, try to get as much as we can.  */
> +  scoped_mmap aux;
> +  for (; pages > 0; pages >>= 1)
> +    {
> +      size_t length;
> +      __u64 data_size;
> +      data_size = (__u64) pages * page_size;
> +
> +      /* Don't ask for more than we can represent in the configuration.  */
> +      if ((__u64) UINT_MAX < data_size)
> +	continue;
> +
> +      length = (size_t) data_size;
> +
> +      /* Check for overflows.  */
> +      if ((__u64) length != data_size)
> +	continue;
> +
> +      header->aux_size = data_size;
> +
> +      errno = 0;
> +      aux.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (),
> +		 header->aux_offset);
> +      if (aux.get () != MAP_FAILED)
> +	break;
> +    }
> +  if (pages == 0)
> +    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));
> +
> +  etm->etm.size = aux.size ();
> +  etm->etm.mem = (const uint8_t *) aux.release ();
> +  etm->etm.data_head = &header->aux_head;
> +  etm->etm.last_head = header->aux_tail;
> +  etm->header = (struct perf_event_mmap_page *) data.release ();
> +  gdb_assert (etm->header == header);
> +  etm->file = fd.release ();
> +
> +  tinfo->conf.etm.size = (unsigned int) etm->etm.size;
> +  return tinfo.release ();
> +}
> +
>   /* See linux-btrace.h.  */
>   
>   struct btrace_target_info *
> @@ -707,6 +861,10 @@ linux_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
>   
>       case BTRACE_FORMAT_PT:
>         return linux_enable_pt (ptid, &conf->pt);
> +
> +    case BTRACE_FORMAT_ETM:
> +      return linux_enable_etm (ptid, &conf->etm);
> +
>       }
>   }
>   
> @@ -715,7 +873,8 @@ linux_enable_btrace (ptid_t ptid, const struct btrace_config *conf)
>   static enum btrace_error
>   linux_disable_bts (struct btrace_tinfo_bts *tinfo)
>   {
> -  munmap((void *) tinfo->header, tinfo->bts.size + PAGE_SIZE);
> +  long page_size = sysconf (_SC_PAGESIZE);
> +  munmap ((void *) tinfo->header, tinfo->bts.size + page_size);
>     close (tinfo->file);
>   
>     return BTRACE_ERR_NONE;
> @@ -726,8 +885,22 @@ linux_disable_bts (struct btrace_tinfo_bts *tinfo)
>   static enum btrace_error
>   linux_disable_pt (struct btrace_tinfo_pt *tinfo)
>   {
> -  munmap((void *) tinfo->pt.mem, tinfo->pt.size);
> -  munmap((void *) tinfo->header, PAGE_SIZE);
> +  long page_size = sysconf (_SC_PAGESIZE);
> +  munmap ((void *) tinfo->pt.mem, tinfo->pt.size);
> +  munmap ((void *) tinfo->header, page_size);
> +  close (tinfo->file);
> +
> +  return BTRACE_ERR_NONE;
> +}
> +
> +/* Disable ARM CoreSight ETM tracing.  */
> +
> +static enum btrace_error
> +linux_disable_etm (struct btrace_tinfo_etm *tinfo)
> +{
> +  long page_size = sysconf (_SC_PAGESIZE);
> +  munmap ((void *) tinfo->etm.mem, tinfo->etm.size);
> +  munmap ((void *) tinfo->header, page_size);
>     close (tinfo->file);
>   
>     return BTRACE_ERR_NONE;
> @@ -753,6 +926,10 @@ linux_disable_btrace (struct btrace_target_info *tinfo)
>       case BTRACE_FORMAT_PT:
>         errcode = linux_disable_pt (&tinfo->variant.pt);
>         break;
> +
> +    case BTRACE_FORMAT_ETM:
> +      errcode = linux_disable_etm (&tinfo->variant.etm);
> +      break;
>       }
>   
>     if (errcode == BTRACE_ERR_NONE)
> @@ -898,6 +1075,190 @@ linux_read_pt (struct btrace_data_pt *btrace,
>     internal_error (__FILE__, __LINE__, _("Unknown btrace read type."));
>   }
>   
> +/* Return the number of CPUs that are present.  */
> +
> +static int
> +get_cpu_count (void)
> +{
> +  static const char filename[] = "/sys/devices/system/cpu/present";
> +
> +  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
> +  if (file.get () == nullptr)
> +    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
> +
> +  fseek (file.get (), 0, SEEK_END);
> +  int length = ftell (file.get ());
> +  fseek (file.get (), 0, SEEK_SET);
> +
> +  char *buffer = (char *) xmalloc (length+1);
> +
> +  length = fread (buffer, 1, length, file.get ());
> +  buffer[length]='\0';
> +  while ((--length) != 0)
> +    {
> +      if ((buffer[length] == ',') || (buffer[length] == '-'))
> +	{
> +	  length++;
> +	  break;
> +	}
> +    }
> +
> +  int cpu_count;
> +  int found = sscanf (&buffer[length], "%d", &cpu_count);
> +  if (found < 1)
> +    error (_("Failed to get cpu count in %s: %s."),
> +	     buffer, safe_strerror (errno));
> +
> +  cpu_count ++;
> +  return (cpu_count);
> +}
> +
> +/* Check if the ETM is an etmv4.  */
> +
> +static bool
> +cs_etm_is_etmv4 (int cpu)
> +{
> +  char filename[PATH_MAX];
> +  snprintf (filename, PATH_MAX,
> +	    "/sys/bus/event_source/devices/cs_etm/cpu%d/trcidr/trcidr0", cpu);
> +  errno = 0;
> +  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
> +  if (file.get () == nullptr)
> +    return false;
> +
> +  return true;
> +}
> +
> +/* Get etm configuration register from sys filesystem.  */
> +
> +static uint32_t
> +cs_etm_get_register (int cpu, const char *path)
> +{
> +  char filename[PATH_MAX];
> +
> +  /* Get coresight register from sysfs.  */
> +  snprintf (filename, PATH_MAX,
> +	    "/sys/bus/event_source/devices/cs_etm/cpu%d/%s", cpu, path);
> +  errno = 0;
> +  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
> +  if (file.get () == nullptr)
> +    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
> +
> +  uint32_t val = 0;
> +
> +  int  found = fscanf (file.get (), "0x%x", &val);
> +  if (found != 1)
> +    error (_("Failed to read coresight register from %s."), filename);
> +  return val;
> +}
> +
> +#define CORESIGHT_ETM_PMU_SEED  0x10
> +
> +/* Calculate trace_id for this cpu
> +   to be kept aligned with coresight-pmu.h.  */
> +
> +static inline int
> +coresight_get_trace_id (int cpu)
> +{
> +  return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));
> +}
> +
> +/* PTMs ETMIDR[11:8] set to b0011.  */
> +#define ETMIDR_PTM_VERSION 0x00000300
> +
> +/* Collect and fill etm trace parameter.  */
> +
> +static void
> +fill_etm_trace_params (struct cs_etm_trace_params *etm_trace_params, int cpu)
> +{
> +  if (cs_etm_is_etmv4 (cpu) == true)
> +    {
> +      etm_trace_params->arch_ver = ARCH_V8;
> +      etm_trace_params->core_profile = profile_CortexA;
> +      etm_trace_params->protocol = OCSD_PROTOCOL_ETMV4I;
> +      /* This is the parameter passed in etm->attr.config in the call to
> +	 perf_event_open remapped according to linux/coresight-pmu.h.  */
> +      etm_trace_params->etmv4.reg_configr = 0;
> +      etm_trace_params->etmv4.reg_idr0
> +	 = cs_etm_get_register (cpu, "trcidr/trcidr0");
> +      etm_trace_params->etmv4.reg_idr1
> +	 = cs_etm_get_register (cpu, "trcidr/trcidr1");
> +      etm_trace_params->etmv4.reg_idr2
> +	 = cs_etm_get_register (cpu, "trcidr/trcidr2");
> +      etm_trace_params->etmv4.reg_idr8
> +	 = cs_etm_get_register (cpu, "trcidr/trcidr8");
> +      etm_trace_params->etmv4.reg_traceidr = coresight_get_trace_id (cpu);
> +    }
> +  else
> +    {
> +      etm_trace_params->arch_ver = ARCH_V7;
> +      etm_trace_params->core_profile = profile_CortexA;
> +      etm_trace_params->protocol
> +	= (etm_trace_params->etmv3.reg_idr & ETMIDR_PTM_VERSION)
> +	  == ETMIDR_PTM_VERSION ? OCSD_PROTOCOL_PTM : OCSD_PROTOCOL_ETMV3;
> +      etm_trace_params->etmv3.reg_ccer
> +	 = cs_etm_get_register (cpu, "mgmt/etmccer");
> +      etm_trace_params->etmv3.reg_ctrl
> +	 = cs_etm_get_register (cpu, "mgmt/etmcr");
> +      etm_trace_params->etmv3.reg_idr
> +	 = cs_etm_get_register (cpu, "mgmt/etmidr");
> +      etm_trace_params->etmv3.reg_trc_id
> +	 = cs_etm_get_register (cpu, "traceid");
> +    }
> +}
> +
> +static void
> +linux_fill_btrace_etm_config (struct btrace_target_info *tinfo,
> +			       struct btrace_data_etm_config *conf)
> +{
> +
> +  cs_etm_trace_params etm_trace_params;
> +  conf->cpu_count = get_cpu_count ();
> +  conf->etm_trace_params = new std::vector<cs_etm_trace_params>;
> +  for (int i = 0; i < conf->cpu_count; i++)
> +    {
> +      fill_etm_trace_params (&etm_trace_params,i);
> +      conf->etm_trace_params->push_back (etm_trace_params);
> +    }
> +
> +  conf->etm_decoder_params.formatted = 1;
> +  conf->etm_decoder_params.fsyncs = 0;
> +  conf->etm_decoder_params.hsyncs = 0;
> +  conf->etm_decoder_params.frame_aligned = 0;
> +  conf->etm_decoder_params.reset_on_4x_sync = 1;
> +}
> +
> +static enum btrace_error
> +linux_read_etm (struct btrace_data_etm *btrace,
> +		struct btrace_target_info *tinfo,
> +		enum btrace_read_type type)
> +{
> +  struct perf_event_buffer *etm;
> +  etm = &tinfo->variant.etm.etm;
> +
> +  linux_fill_btrace_etm_config (tinfo, &btrace->config);
> +
> +  switch (type)
> +    {
> +    case BTRACE_READ_DELTA:
> +      /* We don't support delta reads.  The data head (i.e. aux_head) wraps
> +	 around to stay inside the aux buffer.  */
> +      return BTRACE_ERR_NOT_SUPPORTED;
> +
> +    case BTRACE_READ_NEW:
> +      if (!perf_event_new_data (etm))
> +	return BTRACE_ERR_NONE;
> +
> +      /* Fall through.  */
> +    case BTRACE_READ_ALL:
> +      perf_event_read_all (etm, &(btrace->data),&(btrace->size));
> +      return BTRACE_ERR_NONE;
> +    }
> +
> +  internal_error (__FILE__, __LINE__, _("Unkown btrace read type."));
> +}
> +
> +
>   /* See linux-btrace.h.  */
>   
>   enum btrace_error
> @@ -924,6 +1285,14 @@ linux_read_btrace (struct btrace_data *btrace,
>         btrace->variant.pt.size = 0;
>   
>         return linux_read_pt (&btrace->variant.pt, tinfo, type);
> +
> +    case BTRACE_FORMAT_ETM:
> +      /* We read btrace in ARM CoreSight ETM Trace format.  */
> +      btrace->format = BTRACE_FORMAT_ETM;
> +      btrace->variant.etm.data = NULL;
> +      btrace->variant.etm.size = 0;
> +
> +      return linux_read_etm (&btrace->variant.etm, tinfo, type);
>       }
>   
>     internal_error (__FILE__, __LINE__, _("Unkown branch trace format."));
> diff --git a/gdb/nat/linux-btrace.h b/gdb/nat/linux-btrace.h
> index 607182da144..3038d2f45a0 100644
> --- a/gdb/nat/linux-btrace.h
> +++ b/gdb/nat/linux-btrace.h
> @@ -78,6 +78,22 @@ struct btrace_tinfo_pt
>     /* The trace perf event buffer.  */
>     struct perf_event_buffer pt;
>   };
> +
> +/* Branch trace target information for ARM CoreSight ETM Trace.  */
> +struct btrace_tinfo_etm
> +{
> +  /* The Linux perf_event configuration for collecting the branch trace.  */
> +  struct perf_event_attr attr;
> +
> +  /* The perf event file.  */
> +  int file;
> +
> +  /* The perf event configuration page.  */
> +  volatile struct perf_event_mmap_page *header;
> +
> +  /* The trace perf event buffer.  */
> +  struct perf_event_buffer etm;
> +};
>   #endif /* HAVE_LINUX_PERF_EVENT_H */
>   
>   /* Branch trace target information per thread.  */
> @@ -98,6 +114,9 @@ struct btrace_target_info
>   
>       /* CONF.FORMAT == BTRACE_FORMAT_PT.  */
>       struct btrace_tinfo_pt pt;
> +
> +    /* CONF.FORMAT == BTRACE_FORMAT_ETM.  */
> +    struct btrace_tinfo_etm etm;
>     } variant;
>   #endif /* HAVE_LINUX_PERF_EVENT_H */
>   };
> 

Otherwise looks OK to me.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant
  2021-06-22 14:59   ` Metzger, Markus T
@ 2022-04-07 16:33     ` Zied Guermazi
  2022-04-13  7:00       ` Metzger, Markus T
  0 siblings, 1 reply; 35+ messages in thread
From: Zied Guermazi @ 2022-04-07 16:33 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

Hello Markus,

thanks for your feedback. Below are the answers to your queries.


On 22.06.21 16:59, Metzger, Markus T wrote:
> Hello Zied,
>
>> This patch extend branch tracing by adding the functions needed
>> to collect parameters for decoding ETM traces and decoding them.
> Looks good, overall.  Comments are mostly about simplifications.
> Shall we discuss them in this email thread before going to v7?
>
>
>> diff --git a/gdb/btrace.c b/gdb/btrace.c
>> index 5e689c11d4b..2676389b63e 100644
>> --- a/gdb/btrace.c
>> +++ b/gdb/btrace.c
>> @@ -671,6 +672,38 @@ ftrace_update_insns (struct btrace_function *bfun,
>> const btrace_insn &insn)
>>      ftrace_debug (bfun, "update insn");
>> }
>>
>> +#if defined (HAVE_LIBOPENCSD_C_API)
>> +/* Remove last instruction from BFUN's list.
>> +   This function is not generic and does not undo functions chaining.
>> +   When adding an instruction after using it, the caller must ensure
>> +   that the instruction produces the same chaining.
>> +   An example of good case, is when the same removed instruction
>> +   is added later.  */
> This function is called in one place and the way I understood it, we remove
> an undefined instruction that was used as a breakpoint.  I assume the behavior
> is that this undefined instruction is counted as executed in the trace and we
> want to fix that up.
>
> We don't add that instruction back, though, do we?
>
> And if we wanted to support that remove-then-re-insert use-case,
> wouldn't we want to return the removed instruction?
>
> I'd rather we document exactly that one use-case in which we need
> this functionality.  This allows documenting exactly what we're doing
> and why this is necessary.

[Zied] yes, the code is patched with an undefined instruction that rises 
an exception to trigger the breakpoint.

the traces decoder gets the attempt to execute the instruction in the 
trace and the exception, but decodes it as the original instruction, 
since the code was 'unpatched' in between.

we do not add the instruction back manually. but when the execution 
continue, and depending on the user action, the same instruction may be 
re-executed. we do not re-insert it manually, the decoder will decode it 
from the traces. therefore we can ignore the removed instruction.

following comment is added in cs_etm_update_btrace_with_exception

"Handle the implementation of breakpoints in gdb for arm (v7) architecture
      using undefined instructions.
      In arm (v7), breakpoints are implemented by patching the code with an
      undefined instruction. When the breakpoint is hit the code is 
'un-patched'.
      In the traces we see the exception of the undefined instruction
      and we get the original instruction from the decoder.
      When we continue the execution the original instruction will be
      re-executed, and we will get it again in the traces. To solve
      getting the instruction twice, the first occurrence is removed,
      since it was actually not executed."


>
>> +
>> +static void
>> +ftrace_remove_last_insn (struct btrace_thread_info *btinfo)
>> +{
>> +  /* If we didn't have a function, we return.  */
>> +  if (btinfo->functions.empty ())
>> +    return;
> Should this be an error?
>
>> +
>> +  struct btrace_function *bfun = &btinfo->functions.back ();
>> +  /* If we had a gap before, we return.  */
>> +  if (bfun->errcode != 0)
>> +    return;
> In which case can we have a gap?
[Zied] this is to make sure that following calls are safe to be executed.
>> +
>> +  if (!bfun->insn.empty ())
>> +    bfun->insn.pop_back ();
>> +  else
>> +    {
>> +      /* A valid function must have at least one instruction.  */
>> +      internal_error (__FILE__, __LINE__,
>> +		       _("Attempt to remove last instruction"
>> +			 "from an empty function"));
> We just removed an instruction in the then statement, which
> could well result in BFUN->INSN to become empty().
>
> The statement about valid functions above may be a bit too
> generic.  Also, ftrace_new_* () may create empty function
> segments.
>
> Isn't it rather that in our use-case, the caller knows that the
> function segment cannot be empty?
[Zied] yes, we are sure that we have at least the last executed 
instruction that raised the breakpoint execption.
>
>> +    case ocsd_isa_custom:
>> +      return BTRACE_INSN_FLAG_ISA_CUSTOM;
>> +
>> +    case ocsd_isa_unknown:
>> +      return BTRACE_INSN_FLAG_ISA_UNKNOWN;
>> +
>> +    default:
>> +      internal_error (__FILE__, __LINE__,
>> +		       _("Undefined elem->isa value returned by OpenCsd."));
> This internal error kills GDB.  Should this be a normal error that just
> stops trace processing for this unknown ISA?

[Zied] since I listed all possible values that may get returned by the 
decoder, the default case means that gdb and the decoder are doing 
something wrong and this should be handled as a fatal error.

This code is to be revisited if in the future, ARM adds other ISAs, and 
we would like to support them.

>
>
>> +  insn.iclass = BTRACE_INSN_OTHER;
>> +  insn.pc = elem->st_addr;
>> +  for (int i = 0; i< elem->num_instr_range; i++)
>> +    {
>> +      try
>> +	{
>> +	  insn.size = gdb_insn_length (gdbarch, insn.pc);
>> +	}
>> +      catch (const gdb_exception_error &err)
>> +	{
>> +	  error (_("Failed to get the size of the instruction."));
> Isn't the original exception good enough?
>
>> +	}
>> +
>> +      struct btrace_function *bfun = ftrace_update_function (btinfo, insn.pc);
>> +      if (etm_decoder->arch_version == ARCH_V7)
>> +	insn.flags = cs_etm_get_isa_flag (elem);
> ELEM isn't changing so we could set this once outside of the loop.
[Zied] moved outside
>
>> +
>> +      if (i == elem->num_instr_range -1)
>> +	insn.iclass = cs_etm_get_instruction_class (elem);
>> +
>> +      ftrace_update_insns (bfun, insn);
>> +      insn.pc = insn.pc + insn.size;
>> +    }
>> +}
>> +
>> +/* Update btrace in the case of an exception.  */
>> +
>> +static void
>> +cs_etm_update_btrace_with_exception (const struct cs_etm_decoder
>> *etm_decoder,
>> +				      const ocsd_generic_trace_elem *elem)
>> +{
>> +  gdb_assert (elem->elem_type == OCSD_GEN_TRC_ELEM_EXCEPTION);
>> +
>> +  struct thread_info *tp = etm_decoder->t_info;
>> +  struct btrace_thread_info *btinfo = &tp->btrace;
>> +
>> +  /* Handle the implementation of breakpoints in gdb for arm (v7) architecture
>> +     using undefined instructions.  */
>> +  if (etm_decoder->arch_version == ARCH_V7)
>> +    {
>> +      if (elem->exception_number
>> +	   == CS_ETMV3_4_CORTEX_A_R_EXCEPTION_UNDEFINED_INSTRUCTION)
>> +	{
>> +	  DEBUG ("handle breakpoints implementation in gdb for ARMv7");
>> +	  ftrace_remove_last_insn (btinfo);
> Here we just discard the 'breakpoint' instruction, correct?  We don't really plan
> to re-insert it again.
[Zied] we will not insert it manually. it will be inserted as part of 
the executed code when the user continues the execution
>
> Can we ensure that we actually inserted that instruction into the current BFUN?
> We wouldn't need the helper function and all those checks would become asserts.
>
> I like little helper functions but if we just removed the insn here it would be more
> obvious why we're doing it.
>
>> +	}
>> +    }
>> +}
>> +
>> +/* Update btrace in the case of a trace on.  */
>> +
>> +static void
>> +cs_etm_update_btrace_with_trace_on (const struct cs_etm_decoder
>> *etm_decoder,
>> +				     const ocsd_generic_trace_elem *elem)
>> +{
>> +  gdb_assert (elem->elem_type == OCSD_GEN_TRC_ELEM_TRACE_ON);
>> +
>> +  if (elem->trace_on_reason != TRACE_ON_NORMAL)
>> +    {
>> +      struct thread_info *tp = etm_decoder->t_info;
>> +      struct btrace_thread_info *btinfo = &tp->btrace;
>> +      ftrace_new_gap (btinfo, elem->trace_on_reason, etm_decoder->gaps);
>> +    }
> Even for normal trace off/on pairs, we'd want a gap if PC is moving.
[Zied] yes, but I do not have here a mean for checking if the PC has 
moved. The pc field is not valid in this kind of elements
>
>
>> +}
>> +
>> +/* Callback function when a ocsd_generic_trace_elem is emitted.  */
>> +
>> +static ocsd_datapath_resp_t
>> +cs_etm_trace_element_callback (const void *context,
>> +				const ocsd_trc_index_t index,
>> +				const uint8_t trace_chan_id,
>> +				const ocsd_generic_trace_elem *elem)
>> +{
>> +  if (record_debug != 0)
>> +    {
>> +      char str_buffer[128];
>> +      if (ocsd_gen_elem_str (elem, str_buffer, 128) == OCSD_OK)
> sizeof (str_buffer)
[Zied] done.
>
>> +/* Callback to print error log.  */
>> +
>> +static void cs_etm_print_error_log (const void *p_context, const char
>> *psz_msg_str, const int str_len)
>> +{
>> +    char string_buffer[128];
>> +    memcpy (string_buffer, psz_msg_str, std::min(str_len, 127));
>> +    if (str_len >127)
>> +      string_buffer[127] = 0;
>> +    else
>> +      string_buffer[str_len] = 0;
> We can simply adjust str_len before the memcpy.  There's no reason to declare
> it const; it is passed by-value.
>
>
>> +  uint8_t csid;
>> +  errcode = ocsd_dt_create_decoder (decoder->dcd_tree, decoder_name,
>> +			      OCSD_CREATE_FLG_FULL_DECODER,
>> +			      trace_config, &csid);
>> +  if (errcode != OCSD_OK)
>> +    {
>> +      warning (_("ocsd_dt_create_decoder failed with error: %d"), errcode);
>> +      return errcode;
>> +    }
>> +
>> +  errcode = ocsd_dt_set_gen_elem_outfn (decoder->dcd_tree,
>> +					 cs_etm_trace_element_callback,
>> +					 decoder);
>> +  if (errcode != OCSD_OK)
>> +    {
>> +      warning (_("ocsd_dt_set_gen_elem_outfn failed failed with error: %d"),
>> +		   errcode);
>> +      return errcode;
>> +    }
>> +
>> +  errcode = ocsd_def_errlog_init (OCSD_ERR_SEV_ERROR, 1);
>> +  if (errcode != OCSD_OK)
>> +    {
>> +      warning (_("ocsd_def_errlog_init failed failed with error: %d"),
>> +		   errcode);
>> +      return errcode;
>> +    }
>> +
>> +  /* Initialize error printer.  */
>> +  errcode = ocsd_def_errlog_config_output (C_API_MSGLOGOUT_FLG_NONE,
>> nullptr);
>> +  if (errcode != OCSD_OK)
>> +    {
>> +      warning (_("ocsd_def_errlog_init failed failed with error: %d"),
>> +		   errcode);
>> +      return errcode;
>> +    }
>> +
>> +  errcode = ocsd_def_errlog_set_strprint_cb (decoder->dcd_tree, nullptr,
>> +					      cs_etm_print_error_log);
>> +  if (errcode != OCSD_OK)
>> +    {
>> +      warning (_("ocsd_def_errlog_set_strprint_cb failed failed with error: %d"),
>> +		   errcode);
>> +      return errcode;
>> +    }
>> +
>> +  decoder->prev_return = OCSD_RESP_CONT;
> There are several error returns that leave this field uninitialized.  I assume that
> the decoder is not working in those cases and will not be used.  Don't we need to
> undo anything before returning with an error?

[Zied] all those functions are setting some fields in the decoder. If 
something goes wrong the caller frees the decoder.


>
> I see that we call cs_etm_free_decoder () below on errors, which probably takes
> care of cleaning up partially complete decoders.  It still feels odd to return an error
> and leave a decoder partially initialized.
>
> Let's at least note that in the comment that on error, the caller is expected to
> call cs_etm_free_decoder ().
[Zied] Documented in the comment.
>
>
>> +  struct cs_etm_decoder *decoder;
>> +
>> +  decoder = (struct cs_etm_decoder*) xmalloc (sizeof (struct cs_etm_decoder));
>> +  decoder->dcd_tree = dcdtree_handle;
>> +
>> +  for (int i = 0; i < cpu_count; i++)
>> +    {
>> +      ocsd_err_t errcode = cs_etm_create_decoder (&(t_params->at (i)), decoder);
>> +      if (errcode != OCSD_OK)
>> +	{
>> +	  cs_etm_free_decoder (decoder);
>> +	  return nullptr;
>> +	}
>> +    }
>
>> +/* Process an etm traces data block.
>> +   In case of an error it resets the decoder and tries to process further.  */
>> +
>> +static void
>> +cs_etm_process_data_block (struct btrace_thread_info *btinfo,
>> +			   struct cs_etm_decoder *decoder,
>> +			   uint64_t index, const uint8_t *buf,
>> +			   size_t len, size_t *consumed)
>> +{
>> +  ocsd_datapath_resp_t data_path_return = OCSD_RESP_CONT;
>> +  size_t processed = 0;
>> +  uint32_t count;
>> +
>> +  while (processed < len)
>> +    {
>> +      if (OCSD_DATA_RESP_IS_CONT (data_path_return))
>> +	{
>> +	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
>> +					OCSD_OP_DATA,
>> +					index + processed, len - processed,
>> +					&buf[processed], &count);
>> +	  processed += count;
>> +
>> +	}
> There seems to be an extra empty line.
>
> Should we assert COUNT > 0 || ! OCSD_DATA_RESP_IS_CONT (data_path_return)
> to ensure forward progress?
>
>> +      else if (OCSD_DATA_RESP_IS_WAIT (data_path_return))
>> +	{
>> +	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
>> +					OCSD_OP_FLUSH,
>> +					0, 0, nullptr, nullptr);
> Same here for ! OCSD_DATA_RESP_IS_WAIT (data_path_return), although we
> still wouldn't be able to detect switching back and forth between those two.
>
>> +	}
>> +      else
>> +	{
>> +	  warning (_("error %d in ocsd_dt_process_data after processing %zu"),
>> +		      data_path_return, processed);
>> +	  ftrace_new_gap (btinfo, data_path_return, decoder->gaps);
>> +	  data_path_return = ocsd_dt_process_data (decoder->dcd_tree,
>> +					OCSD_OP_RESET,
>> +					0, 0, nullptr, nullptr);
>> +	  //todo: shall we increase processed? by 1, or 4 do we need to manually
>> align to 16 bytes?
> Please use /* */ for comments.  Most people use FIXME instead of todo.
>
> We may end up in an infinite loop if trying to reset the decoder triggers another
> error indefinitely.
>
> We should still be able to interrupt GDB itself using ^C on the CLI if we ever actually
> run into an infinite decode loop.  So maybe this is all overkill.
>
[Zied] after discussion with OpenCSD engineer. the current 
implementation is fine and the comment can be removed
>
>> +  ocsd_err_t ocsd_error
>> +   = cs_etm_add_mem_access_callback (decoder,
>> +				      (CORE_ADDR) 0x0L, (CORE_ADDR) -1L,
>> +				      btrace_etm_readmem_callback);
>> +  if (ocsd_error != OCSD_OK)
>> +    error (_("Failed to add CoreSight Trace decoder memory access callback."));
> Could we print ICSD_ERROR in the error message?  Even printing it as an int would help.
[Zied] done
>
>
>> diff --git a/gdb/record-btrace.c b/gdb/record-btrace.c
>> index 16ffb76272b..57b9c8ec487 100644
>> --- a/gdb/record-btrace.c
>> +++ b/gdb/record-btrace.c
>> @@ -1557,6 +1558,38 @@ record_btrace_target::remove_breakpoint (struct
>> gdbarch *gdbarch,
>>    return ret;
>> }
>>
>> +/* Reconstruct the instruction set state bits of CPSR register
>> +   according to instruction flags.
>> +   See Table A2-1 in DDI0406B_arm_architecture_reference_manual
>> +   for more details.  */
> So we do not need to store CPSR after all?  We can infer the relevant bits
> from ISA information we get from the trace?  Nice.
[Zied]  few bits  in the insn->flags are used for this purpose, no 
dedicated field for the cspr is used nymore.
>
>> +
>> +static unsigned int
>> +cs_etm_reconstruct_cpsr_iset_state (const struct btrace_insn *insn)
>> +{
>> +  switch (insn->flags & BTRACE_INSN_FLAG_ISA_MASK)
>> +    {
>> +    case BTRACE_INSN_FLAG_ISA_ARM:
>> +      /* ARM state: J and T bits are not set.  */
>> +      return 0;
>> +
>> +    case BTRACE_INSN_FLAG_ISA_THUMB2:
>> +      /* THUMB state: J bit is not set, T bit is set.  */
>> +      return 0x20;
>> +
>> +    case BTRACE_INSN_FLAG_ISA_TEE:
>> +      /* THUMB EE state: J and T bits are set.  */
>> +      return 0x1000020;
>> +
>> +    case BTRACE_INSN_FLAG_ISA_JAZELLE:
>> +      /* JAZELLE state: J bit is set, T bit is not set.  */
>> +      return 0x1000000;
>> +
>> +    default:
>> +      /* Default is ARM mode.  */
>> +      return 0;
> How can we run into this default case?  Should this be an error
> in case we add new ISA modes in the future?
[Zied] this should not happens. changed to  internal_error
>
>> +    }
>> +}
>> +
>> /* The fetch_registers method of target record-btrace.  */
>>
>> void
>> @@ -1581,16 +1614,32 @@ record_btrace_target::fetch_registers (struct
>> regcache *regcache, int regno)
>>        pcreg = gdbarch_pc_regnum (gdbarch);
>>        if (pcreg < 0)
>> 	return;
>> -
>> -      /* We can only provide the PC register.  */
>> -      if (regno >= 0 && regno != pcreg)
>> +      /* We can only provide the PC or CPSR registers here.  */
> Let's extend the comment to say that CPSR is ARM and that we're checking
> the architecture below to avoid redundant checks when we want to fetch
> other registers.

[Zied] done, comment changed to

"      /* We can only provide the PC or PS registers here.
          PS is provided in the case of an ARMv7 target and it maps to CSPR.
          A check for the architecture is done below before providing it */

"

>
>> +      if (regno >= 0 && !(regno == pcreg || regno == ARM_PS_REGNUM))
>> 	return;
>>
>>        insn = btrace_insn_get (replay);
>> -      gdb_assert (insn != NULL);
>> +      gdb_assert (insn != nullptr);
>>
>> +      if ((regno < 0) || (regno == pcreg))
>> +	{
>> 	  regcache->raw_supply (regno, &insn->pc);
>> 	}
>> +      if ((regno < 0) || (regno == ARM_PS_REGNUM))
>> +	{
>> +	  /*  Provide CPSR register in the case of an armv7 target.  */
>> +	  const struct target_desc *tdesc = gdbarch_target_desc (gdbarch);
>> +
>> +	  const char *tdesc_name = tdesc_architecture_name (tdesc);
>> +	  if (strcmp (tdesc_name, "arm") == 0)
>> +	    {
>> +	      int cpsr;
>> +	      cpsr = cs_etm_reconstruct_cpsr_iset_state (insn);
> Please initialize in the declaration.
[Zied] done
>
>> +	      regcache->raw_supply (regno, &cpsr);
>> +	    }
>> +	}
>> +      return;
>> +    }
>>    else
>>      this->beneath ()->fetch_registers (regcache, regno);
>> }
>> diff --git a/gdbsupport/btrace-common.h b/gdbsupport/btrace-common.h
>> index 153b977723a..ee05ecb8b10 100644
>> --- a/gdbsupport/btrace-common.h
>> +++ b/gdbsupport/btrace-common.h
>
>> +/* Parameters of trace source.  */
>> +struct cs_etm_trace_params
>> +{
>> +  /* Architecture version of trace source.  */
>> +  int arch_ver;
>> +  /* Core profile of the trace source.  */
>> +  int core_profile;
>> +  /* Traces protocol.  */
>> +  int protocol;
>> +  union {
>> +    struct cs_etmv3_trace_params etmv3;
>> +    struct cs_etmv4_trace_params etmv4;
>> +  };
>> +};
> Please add empty lines between members.
[Zied] done
>
>> +
>> +/* Configuration information to go with the etm trace data.  */
>> +struct btrace_data_etm_config
>> +{
>> +  /* Count of the CPUs (trace sources).  */
>> +  int    cpu_count;
>> +  /* List of traces sources parameters.  */
>> +  std::vector<struct cs_etm_trace_params> *etm_trace_params;
>> +  /* Trace sink parameters.  */
>> +  struct cs_etm_decoder_params etm_decoder_params;
>> +};
> Also here.
>
>> +
>> +/* Branch trace in ARM Processor Trace format.  */
>> +struct btrace_data_etm
> Is that the correct term?

[Zied] done. changed to

"/* Branch trace in ARM Processor Extended Trace Macrocell format.  */"

>
>
> Regards,
> Markus.
> Intel Deutschland GmbH
> Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> Tel: +49 89 99 8853-0,www.intel.de  <http://www.intel.de>
> Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Office: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928
>
Kind Regards
Zied Guermazi
-- 

*Zied Guermazi*
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de <mailto:zied.guermazi@trande.de>

*Trande GmbH*
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 
- Geschäftsführung: Zied Guermazi

*Confidentiality Note*
This message is intended only for the use of the named recipient(s) and 
may contain confidential and/or privileged information. If you are not 
the intended recipient, please contact the sender and delete the 
message. Any unauthorized use of the information contained in this 
message is prohibited.


^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant
  2022-04-07 16:33     ` Zied Guermazi
@ 2022-04-13  7:00       ` Metzger, Markus T
  2022-05-10 12:58         ` Zied Guermazi
  0 siblings, 1 reply; 35+ messages in thread
From: Metzger, Markus T @ 2022-04-13  7:00 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches

Hello Zied,


+

+static void

+ftrace_remove_last_insn (struct btrace_thread_info *btinfo)

+{

+  /* If we didn't have a function, we return.  */

+  if (btinfo->functions.empty ())

+    return;



Should this be an error?



+

+  struct btrace_function *bfun = &btinfo->functions.back ();

+  /* If we had a gap before, we return.  */

+  if (bfun->errcode != 0)

+    return;



In which case can we have a gap?
[Zied] this is to make sure that following calls are safe to be executed.




+

+  if (!bfun->insn.empty ())

+    bfun->insn.pop_back ();

+  else

+    {

+      /* A valid function must have at least one instruction.  */

+      internal_error (__FILE__, __LINE__,

+                 _("Attempt to remove last instruction"

+                   "from an empty function"));



We just removed an instruction in the then statement, which

could well result in BFUN->INSN to become empty().



The statement about valid functions above may be a bit too

generic.  Also, ftrace_new_* () may create empty function

segments.



Isn't it rather that in our use-case, the caller knows that the

function segment cannot be empty?
[Zied] yes, we are sure that we have at least the last executed instruction that raised the breakpoint execption.

Would it make more sense to simply do ‘bfun->insn.pop_back ()’ inside the caller?

Apparently, the caller knows that there must be an instruction so it could safely remove it.  If we turn this into a separate function, we need to handle the general case.



+    case ocsd_isa_custom:

+      return BTRACE_INSN_FLAG_ISA_CUSTOM;

+

+    case ocsd_isa_unknown:

+      return BTRACE_INSN_FLAG_ISA_UNKNOWN;

+

+    default:

+      internal_error (__FILE__, __LINE__,

+                 _("Undefined elem->isa value returned by OpenCsd."));



This internal error kills GDB.  Should this be a normal error that just

stops trace processing for this unknown ISA?
[Zied] since I listed all possible values that may get returned by the decoder, the default case means that gdb and the decoder are doing something wrong and this should be handled as a fatal error.

The error is fatal for trace decode but not for GDB.  I think we should use error () instead of internal_error () for all those cases.



Even for normal trace off/on pairs, we'd want a gap if PC is moving.
[Zied] yes, but I do not have here a mean for checking if the PC has moved. The pc field is not valid in this kind of elements

In libipt, I compare the IPs of the stop and the resume trace packets.  If something like that doesn’t work in your case I won’t insist.

Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant
  2022-04-13  7:00       ` Metzger, Markus T
@ 2022-05-10 12:58         ` Zied Guermazi
  2022-05-10 13:21           ` Metzger, Markus T
  0 siblings, 1 reply; 35+ messages in thread
From: Zied Guermazi @ 2022-05-10 12:58 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

Hello Markus,

please see the comments below.

/Zied

On 13.04.22 09:00, Metzger, Markus T wrote:
>
> Hello Zied,
>
> +
> +static void
> +ftrace_remove_last_insn (struct btrace_thread_info *btinfo)
> +{
> +  /* If we didn't have a function, we return.  */
> +  if (btinfo->functions.empty ())
> +    return;
> Should this be an error?
> +
> +  struct btrace_function *bfun = &btinfo->functions.back ();
> +  /* If we had a gap before, we return.  */
> +  if (bfun->errcode != 0)
> +    return;
> In which case can we have a gap?
>
> [Zied] this is to make sure that following calls are safe to be executed.
>
> +
> +  if (!bfun->insn.empty ())
> +    bfun->insn.pop_back ();
> +  else
> +    {
> +      /* A valid function must have at least one instruction.  */
> +      internal_error (__FILE__, __LINE__,
> +                 _("Attempt to remove last instruction"
> +                   "from an empty function"));
> We just removed an instruction in the then statement, which
> could well result in BFUN->INSN to become empty().
> The statement about valid functions above may be a bit too
> generic.  Also, ftrace_new_* () may create empty function
> segments.
> Isn't it rather that in our use-case, the caller knows that the
> function segment cannot be empty?
>
> [Zied] yes, we are sure that we have at least the last executed 
> instruction that raised the breakpoint execption.
>
> Would it make more sense to simply do ‘bfun->insn.pop_back ()’ inside 
> the caller?
>
> Apparently, the caller knows that there must be an instruction so it 
> could safely remove it.  If we turn this into a separate function, we 
> need to handle the general case.
>
[Zied] what will happens in the case  of a gap proceeding current 
instruction, the function is still not created at this point, isn't it?

to be sure that none tries to use the function as a generic one, we can 
remove it and put the code in the caller function. It is called only 
once. will this be fine from your point of view?

> +    case ocsd_isa_custom:
> +      return BTRACE_INSN_FLAG_ISA_CUSTOM;
> +
> +    case ocsd_isa_unknown:
> +      return BTRACE_INSN_FLAG_ISA_UNKNOWN;
> +
> +    default:
> +      internal_error (__FILE__, __LINE__,
> +                 _("Undefined elem->isa value returned by OpenCsd."));
> This internal error kills GDB.  Should this be a normal error that just
> stops trace processing for this unknown ISA?
>
> [Zied] since I listed all possible values that may get returned by the 
> decoder, the default case means that gdb and the decoder are doing 
> something wrong and this should be handled as a fatal error.
>
> The error is fatal for trace decode but not for GDB.  I think we 
> should use error () instead of internal_error () for all those cases.
>
[Zied] done.
> Even for normal trace off/on pairs, we'd want a gap if PC is moving.
>
> [Zied] yes, but I do not have here a mean for checking if the PC has 
> moved. The pc field is not valid in this kind of elements
>
> In libipt, I compare the IPs of the stop and the resume trace packets. 
> If something like that doesn’t work in your case I won’t insist.
>
[Zied] unfortunately such a feature is not available in OpenCsd
>
> Regards,
>
> Markus.
>
> Intel Deutschland GmbH
> Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
> Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Office: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928
>
-- 

*Zied Guermazi*
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de <mailto:zied.guermazi@trande.de>

*Trande GmbH*
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 
- Geschäftsführung: Zied Guermazi

*Confidentiality Note*
This message is intended only for the use of the named recipient(s) and 
may contain confidential and/or privileged information. If you are not 
the intended recipient, please contact the sender and delete the 
message. Any unauthorized use of the information contained in this 
message is prohibited.


^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant
  2022-05-10 12:58         ` Zied Guermazi
@ 2022-05-10 13:21           ` Metzger, Markus T
  0 siblings, 0 replies; 35+ messages in thread
From: Metzger, Markus T @ 2022-05-10 13:21 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches

Hello Zied,


+

+  if (!bfun->insn.empty ())

+    bfun->insn.pop_back ();

+  else

+    {

+      /* A valid function must have at least one instruction.  */

+      internal_error (__FILE__, __LINE__,

+                 _("Attempt to remove last instruction"

+                   "from an empty function"));



We just removed an instruction in the then statement, which

could well result in BFUN->INSN to become empty().



The statement about valid functions above may be a bit too

generic.  Also, ftrace_new_* () may create empty function

segments.



Isn't it rather that in our use-case, the caller knows that the

function segment cannot be empty?
[Zied] yes, we are sure that we have at least the last executed instruction that raised the breakpoint execption.

Would it make more sense to simply do ‘bfun->insn.pop_back ()’ inside the caller?

Apparently, the caller knows that there must be an instruction so it could safely remove it.  If we turn this into a separate function, we need to handle the general case.

[Zied] what will happens in the case  of a gap proceeding current instruction, the function is still not created at this point, isn't it?

to be sure that none tries to use the function as a generic one, we can remove it and put the code in the caller function. It is called only once. will this be fine from your point of view?

Yes, I think that would be better.  We’re making too many assumptions for a generic function, here, and we can probably eliminate most of the checks when inlined into the sole call site.

Regards,
Markus.
Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os
  2021-06-23  8:00   ` Metzger, Markus T
@ 2022-05-12 22:52     ` Zied Guermazi
  2022-05-13  5:31       ` Metzger, Markus T
  0 siblings, 1 reply; 35+ messages in thread
From: Zied Guermazi @ 2022-05-12 22:52 UTC (permalink / raw)
  To: Metzger, Markus T, gdb-patches

Hello Markus,

thanks for your feedback, below are the reworking comments.

/Zied

On 23.06.21 10:00, Metzger, Markus T wrote:
> Hello Zied,
>
>> This patch implement the lower layer for starting ad stopping
>> ARM CoreSight tracing on linux targets for arm and aarch64
> The patch looks good overall.  There are a few style nits and I'd ask you to
> split the PAGE_SIZE changes into a separate patch as they are unrelated.
>
> Then, there's the discussion about sharing perf_event buffer mapping.
> I pointed out which parts I believe can be shared.
>
>
>> +/* Teardown branch tracing.  */
>> +
>> +void
>> +arm_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)
>> +{
>> +  /* Ignore errors.  */
>> +  linux_disable_btrace (tinfo);
>> +}
>> +
>> +enum btrace_error
>> +arm_linux_nat_target::read_btrace (struct btrace_data *data,
>> +				   struct btrace_target_info *btinfo,
>> +				   enum btrace_read_type type)
>> +{
>> +  return linux_read_btrace (data, btinfo, type);
>> +}
>> +
>> +/* See to_btrace_conf in target.h.  */
>> +
>> +const struct btrace_config *
>> +arm_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)
>> +{
>> +  return linux_btrace_conf (btinfo);
>> +}
> There's some inconsistency in comments on functions ranging from no comment
> over referring to the original target struct, to an own comment.
[Zied] I will align the comments. please notice that the same applies to 
x86-linux-nat.c (it was a copy-paste from it)
>
>
>> @@ -483,10 +487,11 @@ linux_enable_bts (ptid_t ptid, const struct
>> btrace_config_bts *conf)
>>    scoped_fd fd (syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0));
>>    if (fd.get () < 0)
>>      diagnose_perf_event_open_fail ();
>> +  long page_size = sysconf (_SC_PAGESIZE);
> Please split those PAGE_SIZE changes into a separate patch.  This is unrelated
> to what this patch is doing.
>
> Note that PAGE_SIZE was unsigned whereas sysconf () returns a signed integer.
> I'd expect compilers to require proper casting.
[Zied] done
>
>
>> +/* Enable ARM CoreSight ETM tracing.  */
>> +
>> +static struct btrace_target_info *
>> +linux_enable_etm (ptid_t ptid, const struct btrace_config_etm *conf)
>> +{
> [...]
>> +  etm->attr.sample_type = PERF_SAMPLE_CPU;
>> +  etm->attr.read_format = PERF_FORMAT_ID;
>> +  etm->attr.sample_id_all = 1;
> You enable sampling.  Wouldn't you need to mmap the data buffer, as well?
[Zied] it is not needed for current implementation. removed.
>
>
> This ...
>
>> +  errno = 0;
>> +  scoped_fd fd (syscall (SYS_perf_event_open, &etm->attr, pid, -1, -1, 0));
>> +  if (fd.get () < 0)
>> +    diagnose_perf_event_open_fail ();
>> +
>> +  /* Allocate the configuration page.  */
>> +  long page_size = sysconf (_SC_PAGESIZE);
>> +  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE,
>> MAP_SHARED,
>> +		    fd.get (), 0);
>> +  if (data.get () == MAP_FAILED)
>> +    error (_("Failed to map trace user page: %s."), safe_strerror (errno));
>> +
>> +  struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)
>> +    data.get ();
>> +
>> +  header->aux_offset = header->data_offset + header->data_size;
>> +  /* Convert the requested size in bytes to pages (rounding up).  */
>> +  pages = ((size_t) conf->size / page_size
>> +	   + ((conf->size % page_size) == 0 ? 0 : 1));
>> +  /* We need at least one page.  */
>> +  if (pages == 0)
>> +    pages = 1;
>> +
>> +  /* The buffer size can be requested in powers of two pages.  Adjust PAGES
>> +     to the next power of two.  */
>> +  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
>> +    if ((pages & ((size_t) 1 << pg)) != 0)
>> +      pages += ((size_t) 1 << pg);
>> +
>> +  /* We try to allocate the requested size.
>> +     If that fails, try to get as much as we can.  */
>> +  scoped_mmap aux;
>> +  for (; pages > 0; pages >>= 1)
>> +    {
>> +      size_t length;
>> +      __u64 data_size;
>> +      data_size = (__u64) pages * page_size;
>> +
>> +      /* Don't ask for more than we can represent in the configuration.  */
>> +      if ((__u64) UINT_MAX < data_size)
>> +	continue;
>> +
>> +      length = (size_t) data_size;
>> +
>> +      /* Check for overflows.  */
>> +      if ((__u64) length != data_size)
>> +	continue;
>> +
>> +      header->aux_size = data_size;
>> +
>> +      errno = 0;
>> +      aux.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (),
>> +		 header->aux_offset);
>> +      if (aux.get () != MAP_FAILED)
>> +	break;
>> +    }
>> +  if (pages == 0)
>> +    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));
>> +
>> +  etm->etm.size = aux.size ();
>> +  etm->etm.mem = (const uint8_t *) aux.release ();
>> +  etm->etm.data_head = &header->aux_head;
>> +  etm->etm.last_head = header->aux_tail;
>> +  etm->header = (struct perf_event_mmap_page *) data.release ();
>> +  gdb_assert (etm->header == header);
> ... can be shared with btrace_enable_pt () by introducing some
>
> perf_event_open_aux (struct perf_event_buffer *, const struct perf_event_attr *)
>
> helper.
>
> And if you indeed need to mmap the data buffer, as well, we can share that with
> btrace_enable_bts (), although we'd need some more restructuring to leave
> perf_event_open to the caller and just allocate the data and aux buffers using
> two helpers - they would again look very similar but need to touch a different
> set of fields in the header, so I'd keep those separate.
>
> btrace_enable_foo () would then become
> {
>    perf_event_open ()
>    perf_event_mmap_data ()
>    perf_event_mmap_aux ()  /* not for bts */
> }
>
[Zied] I like the idea, there are two aspects that we need to consider 
to bring it to the mainstream code:

- 1: interface and scope definition

static scoped_fd perf_event_open ( const struct perf_event_attr 
*event_attributes,  const int pid )

{

   errno = 0;
   scoped_fd fd (syscall (SYS_perf_event_open, event_attributes, pid, 
-1, -1, 0));
   if (fd.get () < 0)
     diagnose_perf_event_open_fail ();

   return fd;

}

this function will only open the file descriptor and return it

static scoped_mmap data perf_event_mmap_data (const scoped_fd fd, size_t 
size, size_t page_size, int offset)

{

  //alternative 1 just create it and return it

   /* Allocate the configuration page. */
   scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
             fd.get (), 0);
   if (data.get () == MAP_FAILED)
     error (_("Failed to map trace user page: %s."), safe_strerror (errno));

   return data;

  //alternative 2, create it it and make sure that we resize it to the 
highest possible power of 2 supported by the system

/* Convert the requested size in bytes to pages (rounding up). */
   pages = size / page_size
        + (size % page_size) == 0 ? 0 : 1));
   /* We need at least one page.  */
   if (pages == 0)
     pages = 1;

   /* The buffer size can be requested in powers of two pages. Adjust PAGES
      to the next power of two.  */
   for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
     if ((pages & ((size_t) 1 << pg)) != 0)
       pages += ((size_t) 1 << pg);

   /* We try to allocate the requested size.
      If that fails, try to get as much as we can.  */
   scoped_mmap data;
   for (; pages > 0; pages >>= 1)
     {
       size_t length;
       __u64 data_size;

       data_size = (__u64) pages * page_size;

       /* Don't ask for more than we can represent in the configuration.  */
       if ((__u64) UINT_MAX < data_size)
     continue;

       size = (size_t) data_size;
       length = size + page_size;

       /* Check for overflows.  */
       if ((__u64) length != data_size + page_size)
     continue;

       errno = 0;
       /* The number of pages we request needs to be a power of two.  */
       data.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (), 
offset);
       if (data.get () != MAP_FAILED)
     break;
     }

   if (pages == 0)
     error (_("Failed to map trace buffer: %s."), safe_strerror (errno));

}

static scoped_mmap perf_event_mmap_aux (const scoped_fd fd, size_t size, 
size_t page_size, int offset)

{

//idem, the function is similar to previous one it is only the offset in 
data.reset call that changes

}

so both perf_event_mmap_data perf_event_mmap_aux can be in fact reduced 
to one function if we give size_t size, size_t page_size, int offset as 
parameters.

2- how to bring this change to the mainstream. here we have two 
alternatives : either I do this restructuring for etm only and then you 
take care of using the helper functions for bts and pt or I commit it as 
it is and then I issue a patch for this point only for bts PT and ETM. 
What do you prefer?


>> +  length = fread (buffer, 1, length, file.get ());
>> +  buffer[length]='\0';
> Spaces around =.
[Zied] done.
>
>> +  while ((--length) != 0)
>> +    {
>> +      if ((buffer[length] == ',') || (buffer[length] == '-'))
>> +	{
>> +	  length++;
>> +	  break;
>> +	}
>> +    }
>> +
>> +  int cpu_count;
>> +  int found = sscanf (&buffer[length], "%d", &cpu_count);
>> +  if (found < 1)
>> +    error (_("Failed to get cpu count in %s: %s."),
>> +	     buffer, safe_strerror (errno));
>> +
>> +  cpu_count ++;
>> +  return (cpu_count);
> No need for ().
[Zied] done.
>
>
>> +  char filename[PATH_MAX];
>> +  snprintf (filename, PATH_MAX,
> sizeof (filename)
[Zied] done.
>
>> +  char filename[PATH_MAX];
>> +
>> +  /* Get coresight register from sysfs.  */
>> +  snprintf (filename, PATH_MAX,
> sizeof (filename)
[Zied] done
>
>> +	    "/sys/bus/event_source/devices/cs_etm/cpu%d/%s", cpu, path);
>> +  errno = 0;
>> +  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
>> +  if (file.get () == nullptr)
>> +    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
>> +
>> +  uint32_t val = 0;
>> +
>> +  int  found = fscanf (file.get (), "0x%x", &val);
>> +  if (found != 1)
>> +    error (_("Failed to read coresight register from %s."), filename);
>> +  return val;
>> +}
> Empty line before return?  I'd also remove the empty line between the
> declaration of val and the call to fscanf ().
>
> There are several very similar functions in this patch and each is structured
> differently:
>
> +perf_event_etm_event_type ()
> +{
> [...]
> +
> +  int type, found = fscanf (file.get (), "%d", &type);
> +  if (found != 1)
> +    error (_("Failed to read the ETM event type from %s."), filename);
> +
> +  return type;
>
> +get_cpu_count (void)
> +{
> [...]
> +
> +  int cpu_count;
> +  int found = sscanf (&buffer[length], "%d", &cpu_count);
> +  if (found < 1)
> +    error (_("Failed to get cpu count in %s: %s."),
> +	     buffer, safe_strerror (errno));
> +
> +  cpu_count ++;
> +  return (cpu_count);
>
> +perf_event_etm_event_sink (const struct btrace_config_etm *conf)
> +{
> [...]
> +
> +  unsigned int sink;
> +  int  found = fscanf (file.get (), "0x%x", &sink);
> +  if (found != 1)
> +    error (_("Failed to read the ETM sink from %s."), filename);
> +
> +  return sink;
>
> +cs_etm_get_register (int cpu, const char *path)
> +{
> [...]
> +
> +  uint32_t val = 0;
> +
> +  int  found = fscanf (file.get (), "0x%x", &val);
> +  if (found != 1)
> +    error (_("Failed to read coresight register from %s."), filename);
> +  return val;
>
[Zied] existing code in this file has an empty line before the last 
return everywhere. shall I stick to this convention? eliminating the 
empty lines before returns will bring inconsistency in the file. which 
coding convention do we have to apply here?
>> +
>> +#define CORESIGHT_ETM_PMU_SEED  0x10
>> +
>> +/* Calculate trace_id for this cpu
>> +   to be kept aligned with coresight-pmu.h.  */
>> +
>> +static inline int
>> +coresight_get_trace_id (int cpu)
>> +{
>> +  return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));
> In patch 3, you wrote
>
> +  /* Trace id for this thread.
> +     On a linux system, trace_id is assigned per cpu. The kernel copies
> +     the traces of each thread in a dedicated ring buffer. By this,
> +     traces belonging to different threads are de-multiplexed.
> +     On an RTOS system, especially when routing the traces outside of the SoC,
> +     the OS has no other mean for de-multiplexing the traces than
> +     the trace_id. The hardware (ETM IP) reserves 7 bits for the trace_id.
> +     On linux system trace id is not needed, set it to 0xFF to ignore it
> +     during parsing.  */
> +  uint8_t trace_id;
>
> Should this function return uint8_t and check that the ID is 7 bit max?
[Zied] done, reserved and not allowed values are also generating a 
warning now.
>
>
>> +static void
>> +fill_etm_trace_params (struct cs_etm_trace_params *etm_trace_params, int
>> cpu)
>> +{
>> +  if (cs_etm_is_etmv4 (cpu) == true)
> No need for explicit checks on bool.
[Zied] done.
>
>
>> +static void
>> +linux_fill_btrace_etm_config (struct btrace_target_info *tinfo,
>> +			       struct btrace_data_etm_config *conf)
>> +{
>> +
>> +  cs_etm_trace_params etm_trace_params;
> Please declare at initialization time.
[Zied] pushed forwards before the for loop
>
>> +  conf->cpu_count = get_cpu_count ();
>> +  conf->etm_trace_params = new std::vector<cs_etm_trace_params>;
>> +  for (int i = 0; i < conf->cpu_count; i++)
>> +    {
>> +      fill_etm_trace_params (&etm_trace_params,i);
>> +      conf->etm_trace_params->push_back (etm_trace_params);
>> +    }
> We need to avoid leaking the vector when fill_etm_trace_params () throws.
>
>
>
>> +static enum btrace_error
>> +linux_read_etm (struct btrace_data_etm *btrace,
>> +		struct btrace_target_info *tinfo,
>> +		enum btrace_read_type type)
>> +{
>> +  struct perf_event_buffer *etm;
>> +  etm = &tinfo->variant.etm.etm;
> Please combine.  No forward declarations anymore.  The old code was written
> when GDB was still C.
[Zied] done.
>
>
> regards,
> markus.
> Intel Deutschland GmbH
> Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> Tel: +49 89 99 8853-0,www.intel.de  <http://www.intel.de>
> Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Office: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928
>
-- 

*Zied Guermazi*
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de <mailto:zied.guermazi@trande.de>

*Trande GmbH*
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 
- Geschäftsführung: Zied Guermazi

*Confidentiality Note*
This message is intended only for the use of the named recipient(s) and 
may contain confidential and/or privileged information. If you are not 
the intended recipient, please contact the sender and delete the 
message. Any unauthorized use of the information contained in this 
message is prohibited.


^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os
  2022-05-12 22:52     ` Zied Guermazi
@ 2022-05-13  5:31       ` Metzger, Markus T
  2022-06-12 21:02         ` Zied Guermazi
  0 siblings, 1 reply; 35+ messages in thread
From: Metzger, Markus T @ 2022-05-13  5:31 UTC (permalink / raw)
  To: Zied Guermazi, gdb-patches

Hello Zied,


+cs_etm_get_register (int cpu, const char *path)

+{

[...]

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;


[Zied] existing code in this file has an empty line before the last return everywhere. shall I stick to this convention? eliminating the empty lines before returns will bring inconsistency in the file. which coding convention do we have to apply here?

Yes, let’s stick to that and add the empty line.



so both perf_event_mmap_data perf_event_mmap_aux can be in fact reduced to one function if we give size_t size, size_t page_size, int offset as parameters.

BTS allocates the data buffer including the header, whereas PT allocates the aux buffer plus the header.  We can still make it one function, of course, but I don’t think that it will be any easier to read that way.



2- how to bring this change to the mainstream. here we have two alternatives : either I do this restructuring for etm only and then you take care of using the helper functions for bts and pt or I commit it as it is and then I issue a patch for this point only for bts PT and ETM. What do you prefer?

Ideally, we’d first restructure the existing code, then add the new use.  Are you able to test BTS and PT?

Regards,
Markus.


From: Zied Guermazi <zied.guermazi@trande.de>
Sent: Friday, May 13, 2022 12:52 AM
To: Metzger, Markus T <markus.t.metzger@intel.com>; gdb-patches@sourceware.org
Subject: Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os


Hello Markus,

thanks for your feedback, below are the reworking comments.

/Zied
On 23.06.21 10:00, Metzger, Markus T wrote:

Hello Zied,



This patch implement the lower layer for starting ad stopping

ARM CoreSight tracing on linux targets for arm and aarch64



The patch looks good overall.  There are a few style nits and I'd ask you to

split the PAGE_SIZE changes into a separate patch as they are unrelated.



Then, there's the discussion about sharing perf_event buffer mapping.

I pointed out which parts I believe can be shared.





+/* Teardown branch tracing.  */

+

+void

+arm_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)

+{

+  /* Ignore errors.  */

+  linux_disable_btrace (tinfo);

+}

+

+enum btrace_error

+arm_linux_nat_target::read_btrace (struct btrace_data *data,

+                           struct btrace_target_info *btinfo,

+                           enum btrace_read_type type)

+{

+  return linux_read_btrace (data, btinfo, type);

+}

+

+/* See to_btrace_conf in target.h.  */

+

+const struct btrace_config *

+arm_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)

+{

+  return linux_btrace_conf (btinfo);

+}



There's some inconsistency in comments on functions ranging from no comment

over referring to the original target struct, to an own comment.
[Zied] I will align the comments. please notice that the same applies to x86-linux-nat.c (it was a copy-paste from it)








@@ -483,10 +487,11 @@ linux_enable_bts (ptid_t ptid, const struct

btrace_config_bts *conf)

  scoped_fd fd (syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0));

  if (fd.get () < 0)

    diagnose_perf_event_open_fail ();

+  long page_size = sysconf (_SC_PAGESIZE);



Please split those PAGE_SIZE changes into a separate patch.  This is unrelated

to what this patch is doing.



Note that PAGE_SIZE was unsigned whereas sysconf () returns a signed integer.

I'd expect compilers to require proper casting.
[Zied] done








+/* Enable ARM CoreSight ETM tracing.  */

+

+static struct btrace_target_info *

+linux_enable_etm (ptid_t ptid, const struct btrace_config_etm *conf)

+{

[...]

+  etm->attr.sample_type = PERF_SAMPLE_CPU;

+  etm->attr.read_format = PERF_FORMAT_ID;

+  etm->attr.sample_id_all = 1;



You enable sampling.  Wouldn't you need to mmap the data buffer, as well?
[Zied] it is not needed for current implementation. removed.








This ...



+  errno = 0;

+  scoped_fd fd (syscall (SYS_perf_event_open, &etm->attr, pid, -1, -1, 0));

+  if (fd.get () < 0)

+    diagnose_perf_event_open_fail ();

+

+  /* Allocate the configuration page.  */

+  long page_size = sysconf (_SC_PAGESIZE);

+  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE,

MAP_SHARED,

+             fd.get (), 0);

+  if (data.get () == MAP_FAILED)

+    error (_("Failed to map trace user page: %s."), safe_strerror (errno));

+

+  struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)

+    data.get ();

+

+  header->aux_offset = header->data_offset + header->data_size;

+  /* Convert the requested size in bytes to pages (rounding up).  */

+  pages = ((size_t) conf->size / page_size

+     + ((conf->size % page_size) == 0 ? 0 : 1));

+  /* We need at least one page.  */

+  if (pages == 0)

+    pages = 1;

+

+  /* The buffer size can be requested in powers of two pages.  Adjust PAGES

+     to the next power of two.  */

+  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)

+    if ((pages & ((size_t) 1 << pg)) != 0)

+      pages += ((size_t) 1 << pg);

+

+  /* We try to allocate the requested size.

+     If that fails, try to get as much as we can.  */

+  scoped_mmap aux;

+  for (; pages > 0; pages >>= 1)

+    {

+      size_t length;

+      __u64 data_size;

+      data_size = (__u64) pages * page_size;

+

+      /* Don't ask for more than we can represent in the configuration.  */

+      if ((__u64) UINT_MAX < data_size)

+  continue;

+

+      length = (size_t) data_size;

+

+      /* Check for overflows.  */

+      if ((__u64) length != data_size)

+  continue;

+

+      header->aux_size = data_size;

+

+      errno = 0;

+      aux.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (),

+          header->aux_offset);

+      if (aux.get () != MAP_FAILED)

+  break;

+    }

+  if (pages == 0)

+    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));

+

+  etm->etm.size = aux.size ();

+  etm->etm.mem = (const uint8_t *) aux.release ();

+  etm->etm.data_head = &header->aux_head;

+  etm->etm.last_head = header->aux_tail;

+  etm->header = (struct perf_event_mmap_page *) data.release ();

+  gdb_assert (etm->header == header);



... can be shared with btrace_enable_pt () by introducing some



perf_event_open_aux (struct perf_event_buffer *, const struct perf_event_attr *)



helper.



And if you indeed need to mmap the data buffer, as well, we can share that with

btrace_enable_bts (), although we'd need some more restructuring to leave

perf_event_open to the caller and just allocate the data and aux buffers using

two helpers - they would again look very similar but need to touch a different

set of fields in the header, so I'd keep those separate.



btrace_enable_foo () would then become

{

  perf_event_open ()

  perf_event_mmap_data ()

  perf_event_mmap_aux ()  /* not for bts */

}



[Zied] I like the idea, there are two aspects that we need to consider to bring it to the mainstream code:

- 1: interface and scope definition

static scoped_fd perf_event_open ( const struct perf_event_attr *event_attributes,  const int pid )

{

  errno = 0;
  scoped_fd fd (syscall (SYS_perf_event_open, event_attributes, pid, -1, -1, 0));
  if (fd.get () < 0)
    diagnose_perf_event_open_fail ();

  return fd;

}

this function will only open the file descriptor and return it

static scoped_mmap data perf_event_mmap_data (const scoped_fd fd, size_t size, size_t page_size, int offset)

{

 //alternative 1 just create it and return it

  /* Allocate the configuration page. */
  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
            fd.get (), 0);
  if (data.get () == MAP_FAILED)
    error (_("Failed to map trace user page: %s."), safe_strerror (errno));

  return data;

 //alternative 2, create it it and make sure that we resize it to the highest possible power of 2 supported by the system

/* Convert the requested size in bytes to pages (rounding up).  */
  pages = size / page_size
       + (size % page_size) == 0 ? 0 : 1));
  /* We need at least one page.  */
  if (pages == 0)
    pages = 1;

  /* The buffer size can be requested in powers of two pages.  Adjust PAGES
     to the next power of two.  */
  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
    if ((pages & ((size_t) 1 << pg)) != 0)
      pages += ((size_t) 1 << pg);

  /* We try to allocate the requested size.
     If that fails, try to get as much as we can.  */
  scoped_mmap data;
  for (; pages > 0; pages >>= 1)
    {
      size_t length;
      __u64 data_size;

      data_size = (__u64) pages * page_size;

      /* Don't ask for more than we can represent in the configuration.  */
      if ((__u64) UINT_MAX < data_size)
    continue;

      size = (size_t) data_size;
      length = size + page_size;

      /* Check for overflows.  */
      if ((__u64) length != data_size + page_size)
    continue;

      errno = 0;
      /* The number of pages we request needs to be a power of two.  */
      data.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (), offset);
      if (data.get () != MAP_FAILED)
    break;
    }

  if (pages == 0)
    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));

}

static scoped_mmap perf_event_mmap_aux (const scoped_fd fd, size_t size, size_t page_size, int offset)

{

//idem, the function is similar to previous one it is only the offset in data.reset call that changes

}

so both perf_event_mmap_data perf_event_mmap_aux can be in fact reduced to one function if we give size_t size, size_t page_size, int offset as parameters.

2- how to bring this change to the mainstream. here we have two alternatives : either I do this restructuring for etm only and then you take care of using the helper functions for bts and pt or I commit it as it is and then I issue a patch for this point only for bts PT and ETM. What do you prefer?





+  length = fread (buffer, 1, length, file.get ());

+  buffer[length]='\0';



Spaces around =.
[Zied] done.






+  while ((--length) != 0)

+    {

+      if ((buffer[length] == ',') || (buffer[length] == '-'))

+  {

+    length++;

+    break;

+  }

+    }

+

+  int cpu_count;

+  int found = sscanf (&buffer[length], "%d", &cpu_count);

+  if (found < 1)

+    error (_("Failed to get cpu count in %s: %s."),

+       buffer, safe_strerror (errno));

+

+  cpu_count ++;

+  return (cpu_count);



No need for ().
[Zied] done.








+  char filename[PATH_MAX];

+  snprintf (filename, PATH_MAX,



sizeof (filename)
[Zied] done.






+  char filename[PATH_MAX];

+

+  /* Get coresight register from sysfs.  */

+  snprintf (filename, PATH_MAX,



sizeof (filename)
[Zied] done






+      "/sys/bus/event_source/devices/cs_etm/cpu%d/%s", cpu, path);

+  errno = 0;

+  gdb_file_up file = gdb_fopen_cloexec (filename, "r");

+  if (file.get () == nullptr)

+    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;

+}



Empty line before return?  I'd also remove the empty line between the

declaration of val and the call to fscanf ().



There are several very similar functions in this patch and each is structured

differently:



+perf_event_etm_event_type ()

+{

[...]

+

+  int type, found = fscanf (file.get (), "%d", &type);

+  if (found != 1)

+    error (_("Failed to read the ETM event type from %s."), filename);

+

+  return type;



+get_cpu_count (void)

+{

[...]

+

+  int cpu_count;

+  int found = sscanf (&buffer[length], "%d", &cpu_count);

+  if (found < 1)

+    error (_("Failed to get cpu count in %s: %s."),

+             buffer, safe_strerror (errno));

+

+  cpu_count ++;

+  return (cpu_count);



+perf_event_etm_event_sink (const struct btrace_config_etm *conf)

+{

[...]

+

+  unsigned int sink;

+  int  found = fscanf (file.get (), "0x%x", &sink);

+  if (found != 1)

+    error (_("Failed to read the ETM sink from %s."), filename);

+

+  return sink;



+cs_etm_get_register (int cpu, const char *path)

+{

[...]

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;


[Zied] existing code in this file has an empty line before the last return everywhere. shall I stick to this convention? eliminating the empty lines before returns will bring inconsistency in the file. which coding convention do we have to apply here?




+

+#define CORESIGHT_ETM_PMU_SEED  0x10

+

+/* Calculate trace_id for this cpu

+   to be kept aligned with coresight-pmu.h.  */

+

+static inline int

+coresight_get_trace_id (int cpu)

+{

+  return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));



In patch 3, you wrote



+  /* Trace id for this thread.

+     On a linux system, trace_id is assigned per cpu. The kernel copies

+     the traces of each thread in a dedicated ring buffer. By this,

+     traces belonging to different threads are de-multiplexed.

+     On an RTOS system, especially when routing the traces outside of the SoC,

+     the OS has no other mean for de-multiplexing the traces than

+     the trace_id. The hardware (ETM IP) reserves 7 bits for the trace_id.

+     On linux system trace id is not needed, set it to 0xFF to ignore it

+     during parsing.  */

+  uint8_t trace_id;



Should this function return uint8_t and check that the ID is 7 bit max?
[Zied] done, reserved and not allowed values are also generating a warning now.








+static void

+fill_etm_trace_params (struct cs_etm_trace_params *etm_trace_params, int

cpu)

+{

+  if (cs_etm_is_etmv4 (cpu) == true)



No need for explicit checks on bool.
[Zied] done.








+static void

+linux_fill_btrace_etm_config (struct btrace_target_info *tinfo,

+                        struct btrace_data_etm_config *conf)

+{

+

+  cs_etm_trace_params etm_trace_params;



Please declare at initialization time.
[Zied] pushed forwards before the for loop






+  conf->cpu_count = get_cpu_count ();

+  conf->etm_trace_params = new std::vector<cs_etm_trace_params>;

+  for (int i = 0; i < conf->cpu_count; i++)

+    {

+      fill_etm_trace_params (&etm_trace_params,i);

+      conf->etm_trace_params->push_back (etm_trace_params);

+    }



We need to avoid leaking the vector when fill_etm_trace_params () throws.







+static enum btrace_error

+linux_read_etm (struct btrace_data_etm *btrace,

+         struct btrace_target_info *tinfo,

+         enum btrace_read_type type)

+{

+  struct perf_event_buffer *etm;

+  etm = &tinfo->variant.etm.etm;



Please combine.  No forward declarations anymore.  The old code was written

when GDB was still C.
[Zied] done.








regards,

markus.

Intel Deutschland GmbH

Registered Address: Am Campeon 10, 85579 Neubiberg, Germany

Tel: +49 89 99 8853-0, www.intel.de<http://www.intel.de> <http://www.intel.de><http://www.intel.de>

Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva

Chairperson of the Supervisory Board: Nicole Lau

Registered Office: Munich

Commercial Register: Amtsgericht Muenchen HRB 186928


--


Zied Guermazi
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de

Trande GmbH
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 - Geschäftsführung: Zied Guermazi

Confidentiality Note
This message is intended only for the use of the named recipient(s) and may contain confidential and/or privileged information. If you are not the intended recipient, please contact the sender and delete the message. Any unauthorized use of the information contained in this message is prohibited.


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 7/7] adapt btrace testcases for arm target
  2021-05-31 21:33 ` [PATCH v6 7/7] adapt btrace testcases for arm target Zied Guermazi
  2021-06-22 21:28   ` Lancelot SIX
  2021-06-23 14:16   ` Metzger, Markus T
@ 2022-05-13 11:08   ` Richard Earnshaw
  2022-05-17  9:44     ` Zied Guermazi
  2 siblings, 1 reply; 35+ messages in thread
From: Richard Earnshaw @ 2022-05-13 11:08 UTC (permalink / raw)
  To: Zied Guermazi, gdb-patches, markus.t.metzger



On 31/05/2021 22:33, Zied Guermazi wrote:
> +   This file has been generated on an armv8 machine using:
> +   gcc -m32 -S -O2 -dA -g tailcall-only.c -o aarch64-tailcall-only.S

I find that very hard to believe, since the aarch64 port of gcc does not 
have a '-m32' option.

R.

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 7/7] adapt btrace testcases for arm target
  2022-05-13 11:08   ` Richard Earnshaw
@ 2022-05-17  9:44     ` Zied Guermazi
  0 siblings, 0 replies; 35+ messages in thread
From: Zied Guermazi @ 2022-05-17  9:44 UTC (permalink / raw)
  To: Richard Earnshaw, gdb-patches, markus.t.metzger

hi Richard,

thanks for spotting this error,  the file was generated without the -m32 
option. the comment will be changed accordingly.

Kind Regards

Zied Guermazi

On 13.05.22 13:08, Richard Earnshaw wrote:
>
>
> On 31/05/2021 22:33, Zied Guermazi wrote:
>> +   This file has been generated on an armv8 machine using:
>> +   gcc -m32 -S -O2 -dA -g tailcall-only.c -o aarch64-tailcall-only.S
>
> I find that very hard to believe, since the aarch64 port of gcc does 
> not have a '-m32' option.
>
> R.
-- 

*Zied Guermazi*
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de <mailto:zied.guermazi@trande.de>

*Trande GmbH*
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 
- Geschäftsführung: Zied Guermazi

*Confidentiality Note*
This message is intended only for the use of the named recipient(s) and 
may contain confidential and/or privileged information. If you are not 
the intended recipient, please contact the sender and delete the 
message. Any unauthorized use of the information contained in this 
message is prohibited.


^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os
  2022-05-13  5:31       ` Metzger, Markus T
@ 2022-06-12 21:02         ` Zied Guermazi
  2022-06-20 12:52           ` Metzger, Markus T
  0 siblings, 1 reply; 35+ messages in thread
From: Zied Guermazi @ 2022-06-12 21:02 UTC (permalink / raw)
  To: Metzger, Markus T, gdb-patches

Hi,

I was not able to refactor the code as following:

add three functions to encapsulate basic operations

/* open the scoped fd. */

static scoped_fd *
perf_event_open ( const struct perf_event_attr *event_attributes, const 
int pid)

/*Open the scoped mmap data */

static scoped_mmap*
perf_event_mmap_data (const scoped_fd* fd, size_t* size, size_t page_size)

and

/*Open the scoped mmap aux */

static scoped_mmap*
perf_event_mmap_aux (const scoped_fd* fd, struct perf_event_mmap_page 
*header, size_t* size, size_t page_size)

and call them within linux_enable_bts, linux_enable_pt and linux_enable_etm

I tried two alternatives and both of them failed:

- Alternative 1: instantiate the returned scoped_mmap on the stack of 
the perf_event_mmap_data and perf_event_mmap_aux: the compiler refused 
to assign the scoped_mmap variables and they will not be valid anymore 
in the linux_enable_bts, linux_enable_pt and linux_enable_etm

- Alternative 2: instantiate the returned scoped_mmap on the heap by 
allocating them. the compiler compiles but, once we stop the tracing we 
ca can not start it again cause the resources are busy.

for the time being I will put this refactoring action on hold.

Any idea or support to progress further are welcome


Kind Regards

Zied Guermazi


On 13.05.22 07:31, Metzger, Markus T wrote:
>
> Hello Zied,
>
> +cs_etm_get_register (int cpu, const char *path)
> +{
> [...]
> +
> +  uint32_t val = 0;
> +
> +  int  found = fscanf (file.get (), "0x%x", &val);
> +  if (found != 1)
> +    error (_("Failed to read coresight register from %s."), filename);
> +  return val;
>
> [Zied] existing code in this file has an empty line before the last 
> return everywhere. shall I stick to this convention? eliminating the 
> empty lines before returns will bring inconsistency in the file. which 
> coding convention do we have to apply here?
>
> Yes, let’s stick to that and add the empty line.
>
> so both perf_event_mmap_data perf_event_mmap_aux can be in fact 
> reduced to one function if we give size_t size, size_t page_size, int 
> offset as parameters.
>
> BTS allocates the data buffer including the header, whereas PT 
> allocates the aux buffer plus the header.  We can still make it one 
> function, of course, but I don’t think that it will be any easier to 
> read that way.
>
> 2- how to bring this change to the mainstream. here we have two 
> alternatives : either I do this restructuring for etm only and then 
> you take care of using the helper functions for bts and pt or I commit 
> it as it is and then I issue a patch for this point only for bts PT 
> and ETM. What do you prefer?
>
> Ideally, we’d first restructure the existing code, then add the new 
> use.  Are you able to test BTS and PT?
>
> Regards,
>
> Markus.
>
> *From:* Zied Guermazi <zied.guermazi@trande.de>
> *Sent:* Friday, May 13, 2022 12:52 AM
> *To:* Metzger, Markus T <markus.t.metzger@intel.com>; 
> gdb-patches@sourceware.org
> *Subject:* Re: [PATCH v6 4/7] start/stop btrace with coresight etm and 
> collect etm buffer on linux os
>
> Hello Markus,
>
> thanks for your feedback, below are the reworking comments.
>
> /Zied
>
> On 23.06.21 10:00, Metzger, Markus T wrote:
>
>     Hello Zied,
>
>         This patch implement the lower layer for starting ad stopping
>
>         ARM CoreSight tracing on linux targets for arm and aarch64
>
>     The patch looks good overall.  There are a few style nits and I'd ask you to
>
>     split the PAGE_SIZE changes into a separate patch as they are unrelated.
>
>     Then, there's the discussion about sharing perf_event buffer mapping.
>
>     I pointed out which parts I believe can be shared.
>
>         +/* Teardown branch tracing.  */
>
>         +
>
>         +void
>
>         +arm_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)
>
>         +{
>
>         +  /* Ignore errors.  */
>
>         +  linux_disable_btrace (tinfo);
>
>         +}
>
>         +
>
>         +enum btrace_error
>
>         +arm_linux_nat_target::read_btrace (struct btrace_data *data,
>
>         +                           struct btrace_target_info *btinfo,
>
>         +                           enum btrace_read_type type)
>
>         +{
>
>         +  return linux_read_btrace (data, btinfo, type);
>
>         +}
>
>         +
>
>         +/* See to_btrace_conf in target.h.  */
>
>         +
>
>         +const struct btrace_config *
>
>         +arm_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)
>
>         +{
>
>         +  return linux_btrace_conf (btinfo);
>
>         +}
>
>     There's some inconsistency in comments on functions ranging from no comment
>
>     over referring to the original target struct, to an own comment.
>
> [Zied] I will align the comments. please notice that the same applies 
> to x86-linux-nat.c (it was a copy-paste from it)
>
>         @@ -483,10 +487,11 @@ linux_enable_bts (ptid_t ptid, const struct
>
>         btrace_config_bts *conf)
>
>            scoped_fd fd (syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0));
>
>            if (fd.get () < 0)
>
>              diagnose_perf_event_open_fail ();
>
>         +  long page_size = sysconf (_SC_PAGESIZE);
>
>     Please split those PAGE_SIZE changes into a separate patch.  This is unrelated
>
>     to what this patch is doing.
>
>     Note that PAGE_SIZE was unsigned whereas sysconf () returns a signed integer.
>
>     I'd expect compilers to require proper casting.
>
> [Zied] done
>
>         +/* Enable ARM CoreSight ETM tracing.  */
>
>         +
>
>         +static struct btrace_target_info *
>
>         +linux_enable_etm (ptid_t ptid, const struct btrace_config_etm *conf)
>
>         +{
>
>     [...]
>
>         +  etm->attr.sample_type = PERF_SAMPLE_CPU;
>
>         +  etm->attr.read_format = PERF_FORMAT_ID;
>
>         +  etm->attr.sample_id_all = 1;
>
>     You enable sampling.  Wouldn't you need to mmap the data buffer, as well?
>
> [Zied] it is not needed for current implementation. removed.
>
>     This ...
>
>         +  errno = 0;
>
>         +  scoped_fd fd (syscall (SYS_perf_event_open, &etm->attr, pid, -1, -1, 0));
>
>         +  if (fd.get () < 0)
>
>         +    diagnose_perf_event_open_fail ();
>
>         +
>
>         +  /* Allocate the configuration page.  */
>
>         +  long page_size = sysconf (_SC_PAGESIZE);
>
>         +  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE,
>
>         MAP_SHARED,
>
>         +             fd.get (), 0);
>
>         +  if (data.get () == MAP_FAILED)
>
>         +    error (_("Failed to map trace user page: %s."), safe_strerror (errno));
>
>         +
>
>         +  struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)
>
>         +    data.get ();
>
>         +
>
>         +  header->aux_offset = header->data_offset + header->data_size;
>
>         +  /* Convert the requested size in bytes to pages (rounding up).  */
>
>         +  pages = ((size_t) conf->size / page_size
>
>         +     + ((conf->size % page_size) == 0 ? 0 : 1));
>
>         +  /* We need at least one page.  */
>
>         +  if (pages == 0)
>
>         +    pages = 1;
>
>         +
>
>         +  /* The buffer size can be requested in powers of two pages.  Adjust PAGES
>
>         +     to the next power of two.  */
>
>         +  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
>
>         +    if ((pages & ((size_t) 1 << pg)) != 0)
>
>         +      pages += ((size_t) 1 << pg);
>
>         +
>
>         +  /* We try to allocate the requested size.
>
>         +     If that fails, try to get as much as we can.  */
>
>         +  scoped_mmap aux;
>
>         +  for (; pages > 0; pages >>= 1)
>
>         +    {
>
>         +      size_t length;
>
>         +      __u64 data_size;
>
>         +      data_size = (__u64) pages * page_size;
>
>         +
>
>         +      /* Don't ask for more than we can represent in the configuration.  */
>
>         +      if ((__u64) UINT_MAX < data_size)
>
>         +  continue;
>
>         +
>
>         +      length = (size_t) data_size;
>
>         +
>
>         +      /* Check for overflows.  */
>
>         +      if ((__u64) length != data_size)
>
>         +  continue;
>
>         +
>
>         +      header->aux_size = data_size;
>
>         +
>
>         +      errno = 0;
>
>         +      aux.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (),
>
>         +          header->aux_offset);
>
>         +      if (aux.get () != MAP_FAILED)
>
>         +  break;
>
>         +    }
>
>         +  if (pages == 0)
>
>         +    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));
>
>         +
>
>         +  etm->etm.size = aux.size ();
>
>         +  etm->etm.mem = (const uint8_t *) aux.release ();
>
>         +  etm->etm.data_head = &header->aux_head;
>
>         +  etm->etm.last_head = header->aux_tail;
>
>         +  etm->header = (struct perf_event_mmap_page *) data.release ();
>
>         +  gdb_assert (etm->header == header);
>
>     ... can be shared with btrace_enable_pt () by introducing some
>
>     perf_event_open_aux (struct perf_event_buffer *, const struct perf_event_attr *)
>
>     helper.
>
>     And if you indeed need to mmap the data buffer, as well, we can share that with
>
>     btrace_enable_bts (), although we'd need some more restructuring to leave
>
>     perf_event_open to the caller and just allocate the data and aux buffers using
>
>     two helpers - they would again look very similar but need to touch a different
>
>     set of fields in the header, so I'd keep those separate.
>
>     btrace_enable_foo () would then become
>
>     {
>
>        perf_event_open ()
>
>        perf_event_mmap_data ()
>
>        perf_event_mmap_aux ()  /* not for bts */
>
>     }
>
> [Zied] I like the idea, there are two aspects that we need to consider 
> to bring it to the mainstream code:
>
> - 1: interface and scope definition
>
> static scoped_fd perf_event_open ( const struct perf_event_attr 
> *event_attributes,  const int pid )
>
> {
>
>   errno = 0;
>   scoped_fd fd (syscall (SYS_perf_event_open, event_attributes, pid, 
> -1, -1, 0));
>   if (fd.get () < 0)
>     diagnose_perf_event_open_fail ();
>
>   return fd;
>
> }
>
> this function will only open the file descriptor and return it
>
> static scoped_mmap data perf_event_mmap_data (const scoped_fd fd, 
> size_t size, size_t page_size, int offset)
>
> {
>
>  //alternative 1 just create it and return it
>
>   /* Allocate the configuration page. */
>   scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE, 
> MAP_SHARED,
>             fd.get (), 0);
>   if (data.get () == MAP_FAILED)
>     error (_("Failed to map trace user page: %s."), safe_strerror 
> (errno));
>
>   return data;
>
>  //alternative 2, create it it and make sure that we resize it to the 
> highest possible power of 2 supported by the system
>
> /* Convert the requested size in bytes to pages (rounding up).  */
>   pages = size / page_size
>        + (size % page_size) == 0 ? 0 : 1));
>   /* We need at least one page.  */
>   if (pages == 0)
>     pages = 1;
>
>   /* The buffer size can be requested in powers of two pages.  Adjust 
> PAGES
>      to the next power of two.  */
>   for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
>     if ((pages & ((size_t) 1 << pg)) != 0)
>       pages += ((size_t) 1 << pg);
>
>   /* We try to allocate the requested size.
>      If that fails, try to get as much as we can.  */
>   scoped_mmap data;
>   for (; pages > 0; pages >>= 1)
>     {
>       size_t length;
>       __u64 data_size;
>
>       data_size = (__u64) pages * page_size;
>
>       /* Don't ask for more than we can represent in the 
> configuration.  */
>       if ((__u64) UINT_MAX < data_size)
>     continue;
>
>       size = (size_t) data_size;
>       length = size + page_size;
>
>       /* Check for overflows.  */
>       if ((__u64) length != data_size + page_size)
>     continue;
>
>       errno = 0;
>       /* The number of pages we request needs to be a power of two.  */
>       data.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (), 
> offset);
>       if (data.get () != MAP_FAILED)
>     break;
>     }
>
>   if (pages == 0)
>     error (_("Failed to map trace buffer: %s."), safe_strerror (errno));
>
> }
>
> static scoped_mmap perf_event_mmap_aux (const scoped_fd fd, size_t 
> size, size_t page_size, int offset)
>
> {
>
> //idem, the function is similar to previous one it is only the offset 
> in data.reset call that changes
>
> }
>
> so both perf_event_mmap_data perf_event_mmap_aux can be in fact 
> reduced to one function if we give size_t size, size_t page_size, int 
> offset as parameters.
>
> 2- how to bring this change to the mainstream. here we have two 
> alternatives : either I do this restructuring for etm only and then 
> you take care of using the helper functions for bts and pt or I commit 
> it as it is and then I issue a patch for this point only for bts PT 
> and ETM. What do you prefer?
>
>         +  length = fread (buffer, 1, length, file.get ());
>
>         +  buffer[length]='\0';
>
>     Spaces around =.
>
> [Zied] done.
>
>         +  while ((--length) != 0)
>
>         +    {
>
>         +      if ((buffer[length] == ',') || (buffer[length] == '-'))
>
>         +  {
>
>         +    length++;
>
>         +    break;
>
>         +  }
>
>         +    }
>
>         +
>
>         +  int cpu_count;
>
>         +  int found = sscanf (&buffer[length], "%d", &cpu_count);
>
>         +  if (found < 1)
>
>         +    error (_("Failed to get cpu count in %s: %s."),
>
>         +       buffer, safe_strerror (errno));
>
>         +
>
>         +  cpu_count ++;
>
>         +  return (cpu_count);
>
>     No need for ().
>
> [Zied] done.
>
>         +  char filename[PATH_MAX];
>
>         +  snprintf (filename, PATH_MAX,
>
>     sizeof (filename)
>
> [Zied] done.
>
>         +  char filename[PATH_MAX];
>
>         +
>
>         +  /* Get coresight register from sysfs.  */
>
>         +  snprintf (filename, PATH_MAX,
>
>     sizeof (filename)
>
> [Zied] done
>
>         +      "/sys/bus/event_source/devices/cs_etm/cpu%d/%s", cpu, path);
>
>         +  errno = 0;
>
>         +  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
>
>         +  if (file.get () == nullptr)
>
>         +    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
>
>         +
>
>         +  uint32_t val = 0;
>
>         +
>
>         +  int  found = fscanf (file.get (), "0x%x", &val);
>
>         +  if (found != 1)
>
>         +    error (_("Failed to read coresight register from %s."), filename);
>
>         +  return val;
>
>         +}
>
>     Empty line before return?  I'd also remove the empty line between the
>
>     declaration of val and the call to fscanf ().
>
>     There are several very similar functions in this patch and each is structured
>
>     differently:
>
>     +perf_event_etm_event_type ()
>
>     +{
>
>     [...]
>
>     +
>
>     +  int type, found = fscanf (file.get (), "%d", &type);
>
>     +  if (found != 1)
>
>     +    error (_("Failed to read the ETM event type from %s."), filename);
>
>     +
>
>     +  return type;
>
>     +get_cpu_count (void)
>
>     +{
>
>     [...]
>
>     +
>
>     +  int cpu_count;
>
>     +  int found = sscanf (&buffer[length], "%d", &cpu_count);
>
>     +  if (found < 1)
>
>     +    error (_("Failed to get cpu count in %s: %s."),
>
>     +             buffer, safe_strerror (errno));
>
>     +
>
>     +  cpu_count ++;
>
>     +  return (cpu_count);
>
>     +perf_event_etm_event_sink (const struct btrace_config_etm *conf)
>
>     +{
>
>     [...]
>
>     +
>
>     +  unsigned int sink;
>
>     +  int  found = fscanf (file.get (), "0x%x", &sink);
>
>     +  if (found != 1)
>
>     +    error (_("Failed to read the ETM sink from %s."), filename);
>
>     +
>
>     +  return sink;
>
>     +cs_etm_get_register (int cpu, const char *path)
>
>     +{
>
>     [...]
>
>     +
>
>     +  uint32_t val = 0;
>
>     +
>
>     +  int  found = fscanf (file.get (), "0x%x", &val);
>
>     +  if (found != 1)
>
>     +    error (_("Failed to read coresight register from %s."), filename);
>
>     +  return val;
>
> [Zied] existing code in this file has an empty line before the last 
> return everywhere. shall I stick to this convention? eliminating the 
> empty lines before returns will bring inconsistency in the file. which 
> coding convention do we have to apply here?
>
>         +
>
>         +#define CORESIGHT_ETM_PMU_SEED  0x10
>
>         +
>
>         +/* Calculate trace_id for this cpu
>
>         +   to be kept aligned with coresight-pmu.h.  */
>
>         +
>
>         +static inline int
>
>         +coresight_get_trace_id (int cpu)
>
>         +{
>
>         +  return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));
>
>     In patch 3, you wrote
>
>     +  /* Trace id for this thread.
>
>     +     On a linux system, trace_id is assigned per cpu. The kernel copies
>
>     +     the traces of each thread in a dedicated ring buffer. By this,
>
>     +     traces belonging to different threads are de-multiplexed.
>
>     +     On an RTOS system, especially when routing the traces outside of the SoC,
>
>     +     the OS has no other mean for de-multiplexing the traces than
>
>     +     the trace_id. The hardware (ETM IP) reserves 7 bits for the trace_id.
>
>     +     On linux system trace id is not needed, set it to 0xFF to ignore it
>
>     +     during parsing.  */
>
>     +  uint8_t trace_id;
>
>     Should this function return uint8_t and check that the ID is 7 bit max?
>
> [Zied] done, reserved and not allowed values are also generating a 
> warning now.
>
>         +static void
>
>         +fill_etm_trace_params (struct cs_etm_trace_params *etm_trace_params, int
>
>         cpu)
>
>         +{
>
>         +  if (cs_etm_is_etmv4 (cpu) == true)
>
>     No need for explicit checks on bool.
>
> [Zied] done.
>
>         +static void
>
>         +linux_fill_btrace_etm_config (struct btrace_target_info *tinfo,
>
>         +                        struct btrace_data_etm_config *conf)
>
>         +{
>
>         +
>
>         +  cs_etm_trace_params etm_trace_params;
>
>     Please declare at initialization time.
>
> [Zied] pushed forwards before the for loop
>
>         +  conf->cpu_count = get_cpu_count ();
>
>         +  conf->etm_trace_params = new std::vector<cs_etm_trace_params>;
>
>         +  for (int i = 0; i < conf->cpu_count; i++)
>
>         +    {
>
>         +      fill_etm_trace_params (&etm_trace_params,i);
>
>         +      conf->etm_trace_params->push_back (etm_trace_params);
>
>         +    }
>
>     We need to avoid leaking the vector when fill_etm_trace_params () throws.
>
>         +static enum btrace_error
>
>         +linux_read_etm (struct btrace_data_etm *btrace,
>
>         +         struct btrace_target_info *tinfo,
>
>         +         enum btrace_read_type type)
>
>         +{
>
>         +  struct perf_event_buffer *etm;
>
>         +  etm = &tinfo->variant.etm.etm;
>
>     Please combine.  No forward declarations anymore.  The old code was written
>
>     when GDB was still C.
>
> [Zied] done.
>
>     regards,
>
>     markus.
>
>     Intel Deutschland GmbH
>
>     Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
>
>     Tel: +49 89 99 8853-0,www.intel.de  <http://www.intel.de>  <http://www.intel.de>  <http://www.intel.de>
>
>     Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
>
>     Chairperson of the Supervisory Board: Nicole Lau
>
>     Registered Office: Munich
>
>     Commercial Register: Amtsgericht Muenchen HRB 186928
>
> -- 
>
> *Zied Guermazi*
> founder
>
> Trande GmbH
> Leuschnerstraße 2
> 69469 Weinheim/Germany
>
> Mobile: +491722645127
> mailto:zied.guermazi@trande.de <mailto:zied.guermazi@trande.de>
>
> *Trande GmbH*
> Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
> Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 
> 736209 - Geschäftsführung: Zied Guermazi
>
> *Confidentiality Note*
> This message is intended only for the use of the named recipient(s) 
> and may contain confidential and/or privileged information. If you are 
> not the intended recipient, please contact the sender and delete the 
> message. Any unauthorized use of the information contained in this 
> message is prohibited.
>
> Intel Deutschland GmbH
> Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
> Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Office: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928
>


^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os
  2022-06-12 21:02         ` Zied Guermazi
@ 2022-06-20 12:52           ` Metzger, Markus T
  2022-07-18 19:06             ` Zied Guermazi
  0 siblings, 1 reply; 35+ messages in thread
From: Metzger, Markus T @ 2022-06-20 12:52 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches

Hello Zied,


/* open the scoped fd. */

static scoped_fd *
perf_event_open ( const struct perf_event_attr *event_attributes, const int pid)

The purpose of those scoped_ classes is to allocated objects on the stack to have them destroy their content in case of exceptions.

In the above example, perf_event_open() may use a scoped_fd internally but would return the actual file descriptor after releasing it.  The caller may put the returned file descriptor into its own scoped_fd object on its stack.

regards,
markus.



/* open the scoped fd. */

static scoped_fd *
perf_event_open ( const struct perf_event_attr *event_attributes, const int pid)

/*Open the scoped mmap data */

static scoped_mmap*
perf_event_mmap_data (const scoped_fd* fd, size_t* size, size_t page_size)

and

/*Open the scoped mmap aux */
static scoped_mmap*
perf_event_mmap_aux (const scoped_fd* fd, struct perf_event_mmap_page *header, size_t* size, size_t page_size)

and call them within linux_enable_bts, linux_enable_pt and linux_enable_etm

I tried two alternatives and both of them failed:

- Alternative 1: instantiate the returned scoped_mmap on the stack of the perf_event_mmap_data and perf_event_mmap_aux: the compiler refused to assign the scoped_mmap variables and they will not be valid anymore in the linux_enable_bts, linux_enable_pt and linux_enable_etm

- Alternative 2: instantiate the returned scoped_mmap on the heap by allocating them. the compiler compiles but, once we stop the tracing we ca can not start it again cause the resources are busy.

for the time being I will put this refactoring action on hold.

Any idea or support to progress further are welcome



Kind Regards

Zied Guermazi


On 13.05.22 07:31, Metzger, Markus T wrote:
Hello Zied,


+cs_etm_get_register (int cpu, const char *path)

+{

[...]

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;


[Zied] existing code in this file has an empty line before the last return everywhere. shall I stick to this convention? eliminating the empty lines before returns will bring inconsistency in the file. which coding convention do we have to apply here?


Yes, let’s stick to that and add the empty line.



so both perf_event_mmap_data perf_event_mmap_aux can be in fact reduced to one function if we give size_t size, size_t page_size, int offset as parameters.

BTS allocates the data buffer including the header, whereas PT allocates the aux buffer plus the header.  We can still make it one function, of course, but I don’t think that it will be any easier to read that way.



2- how to bring this change to the mainstream. here we have two alternatives : either I do this restructuring for etm only and then you take care of using the helper functions for bts and pt or I commit it as it is and then I issue a patch for this point only for bts PT and ETM. What do you prefer?

Ideally, we’d first restructure the existing code, then add the new use.  Are you able to test BTS and PT?

Regards,
Markus.


From: Zied Guermazi <zied.guermazi@trande.de><mailto:zied.guermazi@trande.de>
Sent: Friday, May 13, 2022 12:52 AM
To: Metzger, Markus T <markus.t.metzger@intel.com><mailto:markus.t.metzger@intel.com>; gdb-patches@sourceware.org<mailto:gdb-patches@sourceware.org>
Subject: Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os


Hello Markus,

thanks for your feedback, below are the reworking comments.

/Zied
On 23.06.21 10:00, Metzger, Markus T wrote:

Hello Zied,



This patch implement the lower layer for starting ad stopping

ARM CoreSight tracing on linux targets for arm and aarch64



The patch looks good overall.  There are a few style nits and I'd ask you to

split the PAGE_SIZE changes into a separate patch as they are unrelated.



Then, there's the discussion about sharing perf_event buffer mapping.

I pointed out which parts I believe can be shared.





+/* Teardown branch tracing.  */

+

+void

+arm_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)

+{

+  /* Ignore errors.  */

+  linux_disable_btrace (tinfo);

+}

+

+enum btrace_error

+arm_linux_nat_target::read_btrace (struct btrace_data *data,

+                           struct btrace_target_info *btinfo,

+                           enum btrace_read_type type)

+{

+  return linux_read_btrace (data, btinfo, type);

+}

+

+/* See to_btrace_conf in target.h.  */

+

+const struct btrace_config *

+arm_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)

+{

+  return linux_btrace_conf (btinfo);

+}



There's some inconsistency in comments on functions ranging from no comment

over referring to the original target struct, to an own comment.
[Zied] I will align the comments. please notice that the same applies to x86-linux-nat.c (it was a copy-paste from it)









@@ -483,10 +487,11 @@ linux_enable_bts (ptid_t ptid, const struct

btrace_config_bts *conf)

  scoped_fd fd (syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0));

  if (fd.get () < 0)

    diagnose_perf_event_open_fail ();

+  long page_size = sysconf (_SC_PAGESIZE);



Please split those PAGE_SIZE changes into a separate patch.  This is unrelated

to what this patch is doing.



Note that PAGE_SIZE was unsigned whereas sysconf () returns a signed integer.

I'd expect compilers to require proper casting.
[Zied] done









+/* Enable ARM CoreSight ETM tracing.  */

+

+static struct btrace_target_info *

+linux_enable_etm (ptid_t ptid, const struct btrace_config_etm *conf)

+{

[...]

+  etm->attr.sample_type = PERF_SAMPLE_CPU;

+  etm->attr.read_format = PERF_FORMAT_ID;

+  etm->attr.sample_id_all = 1;



You enable sampling.  Wouldn't you need to mmap the data buffer, as well?
[Zied] it is not needed for current implementation. removed.









This ...



+  errno = 0;

+  scoped_fd fd (syscall (SYS_perf_event_open, &etm->attr, pid, -1, -1, 0));

+  if (fd.get () < 0)

+    diagnose_perf_event_open_fail ();

+

+  /* Allocate the configuration page.  */

+  long page_size = sysconf (_SC_PAGESIZE);

+  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE,

MAP_SHARED,

+             fd.get (), 0);

+  if (data.get () == MAP_FAILED)

+    error (_("Failed to map trace user page: %s."), safe_strerror (errno));

+

+  struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)

+    data.get ();

+

+  header->aux_offset = header->data_offset + header->data_size;

+  /* Convert the requested size in bytes to pages (rounding up).  */

+  pages = ((size_t) conf->size / page_size

+     + ((conf->size % page_size) == 0 ? 0 : 1));

+  /* We need at least one page.  */

+  if (pages == 0)

+    pages = 1;

+

+  /* The buffer size can be requested in powers of two pages.  Adjust PAGES

+     to the next power of two.  */

+  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)

+    if ((pages & ((size_t) 1 << pg)) != 0)

+      pages += ((size_t) 1 << pg);

+

+  /* We try to allocate the requested size.

+     If that fails, try to get as much as we can.  */

+  scoped_mmap aux;

+  for (; pages > 0; pages >>= 1)

+    {

+      size_t length;

+      __u64 data_size;

+      data_size = (__u64) pages * page_size;

+

+      /* Don't ask for more than we can represent in the configuration.  */

+      if ((__u64) UINT_MAX < data_size)

+  continue;

+

+      length = (size_t) data_size;

+

+      /* Check for overflows.  */

+      if ((__u64) length != data_size)

+  continue;

+

+      header->aux_size = data_size;

+

+      errno = 0;

+      aux.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (),

+          header->aux_offset);

+      if (aux.get () != MAP_FAILED)

+  break;

+    }

+  if (pages == 0)

+    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));

+

+  etm->etm.size = aux.size ();

+  etm->etm.mem = (const uint8_t *) aux.release ();

+  etm->etm.data_head = &header->aux_head;

+  etm->etm.last_head = header->aux_tail;

+  etm->header = (struct perf_event_mmap_page *) data.release ();

+  gdb_assert (etm->header == header);



... can be shared with btrace_enable_pt () by introducing some



perf_event_open_aux (struct perf_event_buffer *, const struct perf_event_attr *)



helper.



And if you indeed need to mmap the data buffer, as well, we can share that with

btrace_enable_bts (), although we'd need some more restructuring to leave

perf_event_open to the caller and just allocate the data and aux buffers using

two helpers - they would again look very similar but need to touch a different

set of fields in the header, so I'd keep those separate.



btrace_enable_foo () would then become

{

  perf_event_open ()

  perf_event_mmap_data ()

  perf_event_mmap_aux ()  /* not for bts */

}



[Zied] I like the idea, there are two aspects that we need to consider to bring it to the mainstream code:

- 1: interface and scope definition

static scoped_fd perf_event_open ( const struct perf_event_attr *event_attributes,  const int pid )

{

  errno = 0;
  scoped_fd fd (syscall (SYS_perf_event_open, event_attributes, pid, -1, -1, 0));
  if (fd.get () < 0)
    diagnose_perf_event_open_fail ();

  return fd;

}

this function will only open the file descriptor and return it

static scoped_mmap data perf_event_mmap_data (const scoped_fd fd, size_t size, size_t page_size, int offset)

{

 //alternative 1 just create it and return it

  /* Allocate the configuration page. */
  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
            fd.get (), 0);
  if (data.get () == MAP_FAILED)
    error (_("Failed to map trace user page: %s."), safe_strerror (errno));

  return data;

 //alternative 2, create it it and make sure that we resize it to the highest possible power of 2 supported by the system

/* Convert the requested size in bytes to pages (rounding up).  */
  pages = size / page_size
       + (size % page_size) == 0 ? 0 : 1));
  /* We need at least one page.  */
  if (pages == 0)
    pages = 1;

  /* The buffer size can be requested in powers of two pages.  Adjust PAGES
     to the next power of two.  */
  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
    if ((pages & ((size_t) 1 << pg)) != 0)
      pages += ((size_t) 1 << pg);

  /* We try to allocate the requested size.
     If that fails, try to get as much as we can.  */
  scoped_mmap data;
  for (; pages > 0; pages >>= 1)
    {
      size_t length;
      __u64 data_size;

      data_size = (__u64) pages * page_size;

      /* Don't ask for more than we can represent in the configuration.  */
      if ((__u64) UINT_MAX < data_size)
    continue;

      size = (size_t) data_size;
      length = size + page_size;

      /* Check for overflows.  */
      if ((__u64) length != data_size + page_size)
    continue;

      errno = 0;
      /* The number of pages we request needs to be a power of two.  */
      data.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (), offset);
      if (data.get () != MAP_FAILED)
    break;
    }

  if (pages == 0)
    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));

}

static scoped_mmap perf_event_mmap_aux (const scoped_fd fd, size_t size, size_t page_size, int offset)

{

//idem, the function is similar to previous one it is only the offset in data.reset call that changes

}

so both perf_event_mmap_data perf_event_mmap_aux can be in fact reduced to one function if we give size_t size, size_t page_size, int offset as parameters.

2- how to bring this change to the mainstream. here we have two alternatives : either I do this restructuring for etm only and then you take care of using the helper functions for bts and pt or I commit it as it is and then I issue a patch for this point only for bts PT and ETM. What do you prefer?





+  length = fread (buffer, 1, length, file.get ());

+  buffer[length]='\0';



Spaces around =.
[Zied] done.







+  while ((--length) != 0)

+    {

+      if ((buffer[length] == ',') || (buffer[length] == '-'))

+  {

+    length++;

+    break;

+  }

+    }

+

+  int cpu_count;

+  int found = sscanf (&buffer[length], "%d", &cpu_count);

+  if (found < 1)

+    error (_("Failed to get cpu count in %s: %s."),

+       buffer, safe_strerror (errno));

+

+  cpu_count ++;

+  return (cpu_count);



No need for ().
[Zied] done.









+  char filename[PATH_MAX];

+  snprintf (filename, PATH_MAX,



sizeof (filename)
[Zied] done.







+  char filename[PATH_MAX];

+

+  /* Get coresight register from sysfs.  */

+  snprintf (filename, PATH_MAX,



sizeof (filename)
[Zied] done







+      "/sys/bus/event_source/devices/cs_etm/cpu%d/%s", cpu, path);

+  errno = 0;

+  gdb_file_up file = gdb_fopen_cloexec (filename, "r");

+  if (file.get () == nullptr)

+    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;

+}



Empty line before return?  I'd also remove the empty line between the

declaration of val and the call to fscanf ().



There are several very similar functions in this patch and each is structured

differently:



+perf_event_etm_event_type ()

+{

[...]

+

+  int type, found = fscanf (file.get (), "%d", &type);

+  if (found != 1)

+    error (_("Failed to read the ETM event type from %s."), filename);

+

+  return type;



+get_cpu_count (void)

+{

[...]

+

+  int cpu_count;

+  int found = sscanf (&buffer[length], "%d", &cpu_count);

+  if (found < 1)

+    error (_("Failed to get cpu count in %s: %s."),

+             buffer, safe_strerror (errno));

+

+  cpu_count ++;

+  return (cpu_count);



+perf_event_etm_event_sink (const struct btrace_config_etm *conf)

+{

[...]

+

+  unsigned int sink;

+  int  found = fscanf (file.get (), "0x%x", &sink);

+  if (found != 1)

+    error (_("Failed to read the ETM sink from %s."), filename);

+

+  return sink;



+cs_etm_get_register (int cpu, const char *path)

+{

[...]

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;


[Zied] existing code in this file has an empty line before the last return everywhere. shall I stick to this convention? eliminating the empty lines before returns will bring inconsistency in the file. which coding convention do we have to apply here?





+

+#define CORESIGHT_ETM_PMU_SEED  0x10

+

+/* Calculate trace_id for this cpu

+   to be kept aligned with coresight-pmu.h.  */

+

+static inline int

+coresight_get_trace_id (int cpu)

+{

+  return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));



In patch 3, you wrote



+  /* Trace id for this thread.

+     On a linux system, trace_id is assigned per cpu. The kernel copies

+     the traces of each thread in a dedicated ring buffer. By this,

+     traces belonging to different threads are de-multiplexed.

+     On an RTOS system, especially when routing the traces outside of the SoC,

+     the OS has no other mean for de-multiplexing the traces than

+     the trace_id. The hardware (ETM IP) reserves 7 bits for the trace_id.

+     On linux system trace id is not needed, set it to 0xFF to ignore it

+     during parsing.  */

+  uint8_t trace_id;



Should this function return uint8_t and check that the ID is 7 bit max?
[Zied] done, reserved and not allowed values are also generating a warning now.









+static void

+fill_etm_trace_params (struct cs_etm_trace_params *etm_trace_params, int

cpu)

+{

+  if (cs_etm_is_etmv4 (cpu) == true)



No need for explicit checks on bool.
[Zied] done.









+static void

+linux_fill_btrace_etm_config (struct btrace_target_info *tinfo,

+                        struct btrace_data_etm_config *conf)

+{

+

+  cs_etm_trace_params etm_trace_params;



Please declare at initialization time.
[Zied] pushed forwards before the for loop







+  conf->cpu_count = get_cpu_count ();

+  conf->etm_trace_params = new std::vector<cs_etm_trace_params>;

+  for (int i = 0; i < conf->cpu_count; i++)

+    {

+      fill_etm_trace_params (&etm_trace_params,i);

+      conf->etm_trace_params->push_back (etm_trace_params);

+    }



We need to avoid leaking the vector when fill_etm_trace_params () throws.







+static enum btrace_error

+linux_read_etm (struct btrace_data_etm *btrace,

+         struct btrace_target_info *tinfo,

+         enum btrace_read_type type)

+{

+  struct perf_event_buffer *etm;

+  etm = &tinfo->variant.etm.etm;



Please combine.  No forward declarations anymore.  The old code was written

when GDB was still C.
[Zied] done.









regards,

markus.

Intel Deutschland GmbH

Registered Address: Am Campeon 10, 85579 Neubiberg, Germany

Tel: +49 89 99 8853-0, www.intel.de<http://www.intel.de> <http://www.intel.de><http://www.intel.de>

Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva

Chairperson of the Supervisory Board: Nicole Lau

Registered Office: Munich

Commercial Register: Amtsgericht Muenchen HRB 186928


--



Zied Guermazi
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de

Trande GmbH
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 - Geschäftsführung: Zied Guermazi

Confidentiality Note
This message is intended only for the use of the named recipient(s) and may contain confidential and/or privileged information. If you are not the intended recipient, please contact the sender and delete the message. Any unauthorized use of the information contained in this message is prohibited.



Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de<http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928



Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os
  2022-06-20 12:52           ` Metzger, Markus T
@ 2022-07-18 19:06             ` Zied Guermazi
  2022-07-19  5:04               ` Metzger, Markus T
  0 siblings, 1 reply; 35+ messages in thread
From: Zied Guermazi @ 2022-07-18 19:06 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

hello Markus,

the proposal solves the issue for scoped_fd, we can use it and create a 
second scoped_fd using the returned file descriptor since scoped_fd has 
this constructor

explicit scoped_fd (int fd = -1) noexcept : m_fd (fd) {}.

for scoped_mmap we can not do the same since the class is missing a 
constructor taking void *mem and size_t length as arguments.

will it be fine to add such a constructor to the class? will it be 
enough to assign the memory pointer and the length to the class members?


Kind Regards

Zied Guermazi


On 20.06.22 14:52, Metzger, Markus T wrote:
>
> Hello Zied,
>
> /* open the scoped fd. */
>
> static scoped_fd *
> perf_event_open ( const struct perf_event_attr *event_attributes, 
> const int pid)
>
> The purpose of those scoped_ classes is to allocated objects on the 
> stack to have them destroy their content in case of exceptions.
>
> In the above example, perf_event_open() may use a scoped_fd internally 
> but would return the actual file descriptor after releasing it.  The 
> caller may put the returned file descriptor into its own scoped_fd 
> object on its stack.
>
> regards,
>
> markus.
>
> /* open the scoped fd. */
>
> static scoped_fd *
> perf_event_open ( const struct perf_event_attr *event_attributes, 
> const int pid)
>
> /*Open the scoped mmap data */
>
> static scoped_mmap*
> perf_event_mmap_data (const scoped_fd* fd, size_t* size, size_t page_size)
>
> and
>
> /*Open the scoped mmap aux */
>
> static scoped_mmap*
> perf_event_mmap_aux (const scoped_fd* fd, struct perf_event_mmap_page 
> *header, size_t* size, size_t page_size)
>
> and call them within linux_enable_bts, linux_enable_pt and 
> linux_enable_etm
>
> I tried two alternatives and both of them failed:
>
> - Alternative 1: instantiate the returned scoped_mmap on the stack of 
> the perf_event_mmap_data and perf_event_mmap_aux: the compiler refused 
> to assign the scoped_mmap variables and they will not be valid anymore 
> in the linux_enable_bts, linux_enable_pt and linux_enable_etm
>
> - Alternative 2: instantiate the returned scoped_mmap on the heap by 
> allocating them. the compiler compiles but, once we stop the tracing 
> we ca can not start it again cause the resources are busy.
>
> for the time being I will put this refactoring action on hold.
>
> Any idea or support to progress further are welcome
>
> Kind Regards
>
> Zied Guermazi
>
> On 13.05.22 07:31, Metzger, Markus T wrote:
>
>     Hello Zied,
>
>     +cs_etm_get_register (int cpu, const char *path)
>
>     +{
>
>     [...]
>
>     +
>
>     +  uint32_t val = 0;
>
>     +
>
>     +  int  found = fscanf (file.get (), "0x%x", &val);
>
>     +  if (found != 1)
>
>     +    error (_("Failed to read coresight register from %s."), filename);
>
>     +  return val;
>
>       
>
>     [Zied] existing code in this file has an empty line before the
>     last return everywhere. shall I stick to this convention?
>     eliminating the empty lines before returns will bring
>     inconsistency in the file. which coding convention do we have to
>     apply here?
>
>
>     Yes, let’s stick to that and add the empty line.
>
>     so both perf_event_mmap_data perf_event_mmap_aux can be in fact
>     reduced to one function if we give size_t size, size_t page_size,
>     int offset as parameters.
>
>     BTS allocates the data buffer including the header, whereas PT
>     allocates the aux buffer plus the header.  We can still make it
>     one function, of course, but I don’t think that it will be any
>     easier to read that way.
>
>     2- how to bring this change to the mainstream. here we have two
>     alternatives : either I do this restructuring for etm only and
>     then you take care of using the helper functions for bts and pt or
>     I commit it as it is and then I issue a patch for this point only
>     for bts PT and ETM. What do you prefer?
>
>     Ideally, we’d first restructure the existing code, then add the
>     new use.  Are you able to test BTS and PT?
>
>     Regards,
>
>     Markus.
>
>     *From:* Zied Guermazi <zied.guermazi@trande.de>
>     <mailto:zied.guermazi@trande.de>
>     *Sent:* Friday, May 13, 2022 12:52 AM
>     *To:* Metzger, Markus T <markus.t.metzger@intel.com>
>     <mailto:markus.t.metzger@intel.com>; gdb-patches@sourceware.org
>     *Subject:* Re: [PATCH v6 4/7] start/stop btrace with coresight etm
>     and collect etm buffer on linux os
>
>     Hello Markus,
>
>     thanks for your feedback, below are the reworking comments.
>
>     /Zied
>
>     On 23.06.21 10:00, Metzger, Markus T wrote:
>
>         Hello Zied,
>
>           
>
>             This patch implement the lower layer for starting ad stopping
>
>             ARM CoreSight tracing on linux targets for arm and aarch64
>
>           
>
>         The patch looks good overall.  There are a few style nits and I'd ask you to
>
>         split the PAGE_SIZE changes into a separate patch as they are unrelated.
>
>           
>
>         Then, there's the discussion about sharing perf_event buffer mapping.
>
>         I pointed out which parts I believe can be shared.
>
>           
>
>           
>
>             +/* Teardown branch tracing.  */
>
>             +
>
>             +void
>
>             +arm_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)
>
>             +{
>
>             +  /* Ignore errors.  */
>
>             +  linux_disable_btrace (tinfo);
>
>             +}
>
>             +
>
>             +enum btrace_error
>
>             +arm_linux_nat_target::read_btrace (struct btrace_data *data,
>
>             +                           struct btrace_target_info *btinfo,
>
>             +                           enum btrace_read_type type)
>
>             +{
>
>             +  return linux_read_btrace (data, btinfo, type);
>
>             +}
>
>             +
>
>             +/* See to_btrace_conf in target.h.  */
>
>             +
>
>             +const struct btrace_config *
>
>             +arm_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)
>
>             +{
>
>             +  return linux_btrace_conf (btinfo);
>
>             +}
>
>           
>
>         There's some inconsistency in comments on functions ranging from no comment
>
>         over referring to the original target struct, to an own comment.
>
>     [Zied] I will align the comments. please notice that the same
>     applies to x86-linux-nat.c (it was a copy-paste from it)
>
>
>           
>
>           
>
>           
>
>             @@ -483,10 +487,11 @@ linux_enable_bts (ptid_t ptid, const struct
>
>             btrace_config_bts *conf)
>
>                scoped_fd fd (syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0));
>
>                if (fd.get () < 0)
>
>                  diagnose_perf_event_open_fail ();
>
>             +  long page_size = sysconf (_SC_PAGESIZE);
>
>           
>
>         Please split those PAGE_SIZE changes into a separate patch.  This is unrelated
>
>         to what this patch is doing.
>
>           
>
>         Note that PAGE_SIZE was unsigned whereas sysconf () returns a signed integer.
>
>         I'd expect compilers to require proper casting.
>
>     [Zied] done
>
>
>           
>
>           
>
>           
>
>             +/* Enable ARM CoreSight ETM tracing.  */
>
>             +
>
>             +static struct btrace_target_info *
>
>             +linux_enable_etm (ptid_t ptid, const struct btrace_config_etm *conf)
>
>             +{
>
>         [...]
>
>             +  etm->attr.sample_type = PERF_SAMPLE_CPU;
>
>             +  etm->attr.read_format = PERF_FORMAT_ID;
>
>             +  etm->attr.sample_id_all = 1;
>
>           
>
>         You enable sampling.  Wouldn't you need to mmap the data buffer, as well?
>
>     [Zied] it is not needed for current implementation. removed.
>
>
>           
>
>           
>
>           
>
>         This ...
>
>           
>
>             +  errno = 0;
>
>             +  scoped_fd fd (syscall (SYS_perf_event_open, &etm->attr, pid, -1, -1, 0));
>
>             +  if (fd.get () < 0)
>
>             +    diagnose_perf_event_open_fail ();
>
>             +
>
>             +  /* Allocate the configuration page.  */
>
>             +  long page_size = sysconf (_SC_PAGESIZE);
>
>             +  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE,
>
>             MAP_SHARED,
>
>             +             fd.get (), 0);
>
>             +  if (data.get () == MAP_FAILED)
>
>             +    error (_("Failed to map trace user page: %s."), safe_strerror (errno));
>
>             +
>
>             +  struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)
>
>             +    data.get ();
>
>             +
>
>             +  header->aux_offset = header->data_offset + header->data_size;
>
>             +  /* Convert the requested size in bytes to pages (rounding up).  */
>
>             +  pages = ((size_t) conf->size / page_size
>
>             +     + ((conf->size % page_size) == 0 ? 0 : 1));
>
>             +  /* We need at least one page.  */
>
>             +  if (pages == 0)
>
>             +    pages = 1;
>
>             +
>
>             +  /* The buffer size can be requested in powers of two pages.  Adjust PAGES
>
>             +     to the next power of two.  */
>
>             +  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
>
>             +    if ((pages & ((size_t) 1 << pg)) != 0)
>
>             +      pages += ((size_t) 1 << pg);
>
>             +
>
>             +  /* We try to allocate the requested size.
>
>             +     If that fails, try to get as much as we can.  */
>
>             +  scoped_mmap aux;
>
>             +  for (; pages > 0; pages >>= 1)
>
>             +    {
>
>             +      size_t length;
>
>             +      __u64 data_size;
>
>             +      data_size = (__u64) pages * page_size;
>
>             +
>
>             +      /* Don't ask for more than we can represent in the configuration.  */
>
>             +      if ((__u64) UINT_MAX < data_size)
>
>             +  continue;
>
>             +
>
>             +      length = (size_t) data_size;
>
>             +
>
>             +      /* Check for overflows.  */
>
>             +      if ((__u64) length != data_size)
>
>             +  continue;
>
>             +
>
>             +      header->aux_size = data_size;
>
>             +
>
>             +      errno = 0;
>
>             +      aux.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (),
>
>             +          header->aux_offset);
>
>             +      if (aux.get () != MAP_FAILED)
>
>             +  break;
>
>             +    }
>
>             +  if (pages == 0)
>
>             +    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));
>
>             +
>
>             +  etm->etm.size = aux.size ();
>
>             +  etm->etm.mem = (const uint8_t *) aux.release ();
>
>             +  etm->etm.data_head = &header->aux_head;
>
>             +  etm->etm.last_head = header->aux_tail;
>
>             +  etm->header = (struct perf_event_mmap_page *) data.release ();
>
>             +  gdb_assert (etm->header == header);
>
>           
>
>         ... can be shared with btrace_enable_pt () by introducing some
>
>           
>
>         perf_event_open_aux (struct perf_event_buffer *, const struct perf_event_attr *)
>
>           
>
>         helper.
>
>           
>
>         And if you indeed need to mmap the data buffer, as well, we can share that with
>
>         btrace_enable_bts (), although we'd need some more restructuring to leave
>
>         perf_event_open to the caller and just allocate the data and aux buffers using
>
>         two helpers - they would again look very similar but need to touch a different
>
>         set of fields in the header, so I'd keep those separate.
>
>           
>
>         btrace_enable_foo () would then become
>
>         {
>
>            perf_event_open ()
>
>            perf_event_mmap_data ()
>
>            perf_event_mmap_aux ()  /* not for bts */
>
>         }
>
>           
>
>     [Zied] I like the idea, there are two aspects that we need to
>     consider to bring it to the mainstream code:
>
>     - 1: interface and scope definition
>
>     static scoped_fd perf_event_open ( const struct perf_event_attr
>     *event_attributes,  const int pid )
>
>     {
>
>       errno = 0;
>       scoped_fd fd (syscall (SYS_perf_event_open, event_attributes,
>     pid, -1, -1, 0));
>       if (fd.get () < 0)
>         diagnose_perf_event_open_fail ();
>
>       return fd;
>
>     }
>
>     this function will only open the file descriptor and return it
>
>     static scoped_mmap data perf_event_mmap_data (const scoped_fd fd,
>     size_t size, size_t page_size, int offset)
>
>     {
>
>      //alternative 1 just create it and return it
>
>       /* Allocate the configuration page. */
>       scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE,
>     MAP_SHARED,
>                 fd.get (), 0);
>       if (data.get () == MAP_FAILED)
>         error (_("Failed to map trace user page: %s."), safe_strerror
>     (errno));
>
>       return data;
>
>      //alternative 2, create it it and make sure that we resize it to
>     the highest possible power of 2 supported by the system
>
>     /* Convert the requested size in bytes to pages (rounding up).  */
>       pages = size / page_size
>            + (size % page_size) == 0 ? 0 : 1));
>       /* We need at least one page.  */
>       if (pages == 0)
>         pages = 1;
>
>       /* The buffer size can be requested in powers of two pages. 
>     Adjust PAGES
>          to the next power of two.  */
>       for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
>         if ((pages & ((size_t) 1 << pg)) != 0)
>           pages += ((size_t) 1 << pg);
>
>       /* We try to allocate the requested size.
>          If that fails, try to get as much as we can.  */
>       scoped_mmap data;
>       for (; pages > 0; pages >>= 1)
>         {
>           size_t length;
>           __u64 data_size;
>
>           data_size = (__u64) pages * page_size;
>
>           /* Don't ask for more than we can represent in the
>     configuration.  */
>           if ((__u64) UINT_MAX < data_size)
>         continue;
>
>           size = (size_t) data_size;
>           length = size + page_size;
>
>           /* Check for overflows.  */
>           if ((__u64) length != data_size + page_size)
>         continue;
>
>           errno = 0;
>           /* The number of pages we request needs to be a power of
>     two.  */
>           data.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get
>     (), offset);
>           if (data.get () != MAP_FAILED)
>         break;
>         }
>
>       if (pages == 0)
>         error (_("Failed to map trace buffer: %s."), safe_strerror
>     (errno));
>
>     }
>
>     static scoped_mmap perf_event_mmap_aux (const scoped_fd fd, size_t
>     size, size_t page_size, int offset)
>
>     {
>
>     //idem, the function is similar to previous one it is only the
>     offset in data.reset call that changes
>
>     }
>
>     so both perf_event_mmap_data perf_event_mmap_aux can be in fact
>     reduced to one function if we give size_t size, size_t page_size,
>     int offset as parameters.
>
>     2- how to bring this change to the mainstream. here we have two
>     alternatives : either I do this restructuring for etm only and
>     then you take care of using the helper functions for bts and pt or
>     I commit it as it is and then I issue a patch for this point only
>     for bts PT and ETM. What do you prefer?
>
>           
>
>             +  length = fread (buffer, 1, length, file.get ());
>
>             +  buffer[length]='\0';
>
>           
>
>         Spaces around =.
>
>     [Zied] done.
>
>
>           
>
>           
>
>             +  while ((--length) != 0)
>
>             +    {
>
>             +      if ((buffer[length] == ',') || (buffer[length] == '-'))
>
>             +  {
>
>             +    length++;
>
>             +    break;
>
>             +  }
>
>             +    }
>
>             +
>
>             +  int cpu_count;
>
>             +  int found = sscanf (&buffer[length], "%d", &cpu_count);
>
>             +  if (found < 1)
>
>             +    error (_("Failed to get cpu count in %s: %s."),
>
>             +       buffer, safe_strerror (errno));
>
>             +
>
>             +  cpu_count ++;
>
>             +  return (cpu_count);
>
>           
>
>         No need for ().
>
>     [Zied] done.
>
>
>           
>
>           
>
>           
>
>             +  char filename[PATH_MAX];
>
>             +  snprintf (filename, PATH_MAX,
>
>           
>
>         sizeof (filename)
>
>     [Zied] done.
>
>
>           
>
>           
>
>             +  char filename[PATH_MAX];
>
>             +
>
>             +  /* Get coresight register from sysfs.  */
>
>             +  snprintf (filename, PATH_MAX,
>
>           
>
>         sizeof (filename)
>
>     [Zied] done
>
>
>           
>
>           
>
>             +      "/sys/bus/event_source/devices/cs_etm/cpu%d/%s", cpu, path);
>
>             +  errno = 0;
>
>             +  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
>
>             +  if (file.get () == nullptr)
>
>             +    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
>
>             +
>
>             +  uint32_t val = 0;
>
>             +
>
>             +  int  found = fscanf (file.get (), "0x%x", &val);
>
>             +  if (found != 1)
>
>             +    error (_("Failed to read coresight register from %s."), filename);
>
>             +  return val;
>
>             +}
>
>           
>
>         Empty line before return?  I'd also remove the empty line between the
>
>         declaration of val and the call to fscanf ().
>
>           
>
>         There are several very similar functions in this patch and each is structured
>
>         differently:
>
>           
>
>         +perf_event_etm_event_type ()
>
>         +{
>
>         [...]
>
>         +
>
>         +  int type, found = fscanf (file.get (), "%d", &type);
>
>         +  if (found != 1)
>
>         +    error (_("Failed to read the ETM event type from %s."), filename);
>
>         +
>
>         +  return type;
>
>           
>
>         +get_cpu_count (void)
>
>         +{
>
>         [...]
>
>         +
>
>         +  int cpu_count;
>
>         +  int found = sscanf (&buffer[length], "%d", &cpu_count);
>
>         +  if (found < 1)
>
>         +    error (_("Failed to get cpu count in %s: %s."),
>
>         +             buffer, safe_strerror (errno));
>
>         +
>
>         +  cpu_count ++;
>
>         +  return (cpu_count);
>
>           
>
>         +perf_event_etm_event_sink (const struct btrace_config_etm *conf)
>
>         +{
>
>         [...]
>
>         +
>
>         +  unsigned int sink;
>
>         +  int  found = fscanf (file.get (), "0x%x", &sink);
>
>         +  if (found != 1)
>
>         +    error (_("Failed to read the ETM sink from %s."), filename);
>
>         +
>
>         +  return sink;
>
>           
>
>         +cs_etm_get_register (int cpu, const char *path)
>
>         +{
>
>         [...]
>
>         +
>
>         +  uint32_t val = 0;
>
>         +
>
>         +  int  found = fscanf (file.get (), "0x%x", &val);
>
>         +  if (found != 1)
>
>         +    error (_("Failed to read coresight register from %s."), filename);
>
>         +  return val;
>
>           
>
>     [Zied] existing code in this file has an empty line before the
>     last return everywhere. shall I stick to this convention?
>     eliminating the empty lines before returns will bring
>     inconsistency in the file. which coding convention do we have to
>     apply here?
>
>
>           
>
>             +
>
>             +#define CORESIGHT_ETM_PMU_SEED  0x10
>
>             +
>
>             +/* Calculate trace_id for this cpu
>
>             +   to be kept aligned with coresight-pmu.h.  */
>
>             +
>
>             +static inline int
>
>             +coresight_get_trace_id (int cpu)
>
>             +{
>
>             +  return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));
>
>           
>
>         In patch 3, you wrote
>
>           
>
>         +  /* Trace id for this thread.
>
>         +     On a linux system, trace_id is assigned per cpu. The kernel copies
>
>         +     the traces of each thread in a dedicated ring buffer. By this,
>
>         +     traces belonging to different threads are de-multiplexed.
>
>         +     On an RTOS system, especially when routing the traces outside of the SoC,
>
>         +     the OS has no other mean for de-multiplexing the traces than
>
>         +     the trace_id. The hardware (ETM IP) reserves 7 bits for the trace_id.
>
>         +     On linux system trace id is not needed, set it to 0xFF to ignore it
>
>         +     during parsing.  */
>
>         +  uint8_t trace_id;
>
>           
>
>         Should this function return uint8_t and check that the ID is 7 bit max?
>
>     [Zied] done, reserved and not allowed values are also generating a
>     warning now.
>
>
>           
>
>           
>
>           
>
>             +static void
>
>             +fill_etm_trace_params (struct cs_etm_trace_params *etm_trace_params, int
>
>             cpu)
>
>             +{
>
>             +  if (cs_etm_is_etmv4 (cpu) == true)
>
>           
>
>         No need for explicit checks on bool.
>
>     [Zied] done.
>
>
>           
>
>           
>
>           
>
>             +static void
>
>             +linux_fill_btrace_etm_config (struct btrace_target_info *tinfo,
>
>             +                        struct btrace_data_etm_config *conf)
>
>             +{
>
>             +
>
>             +  cs_etm_trace_params etm_trace_params;
>
>           
>
>         Please declare at initialization time.
>
>     [Zied] pushed forwards before the for loop
>
>
>           
>
>           
>
>             +  conf->cpu_count = get_cpu_count ();
>
>             +  conf->etm_trace_params = new std::vector<cs_etm_trace_params>;
>
>             +  for (int i = 0; i < conf->cpu_count; i++)
>
>             +    {
>
>             +      fill_etm_trace_params (&etm_trace_params,i);
>
>             +      conf->etm_trace_params->push_back (etm_trace_params);
>
>             +    }
>
>           
>
>         We need to avoid leaking the vector when fill_etm_trace_params () throws.
>
>           
>
>           
>
>           
>
>             +static enum btrace_error
>
>             +linux_read_etm (struct btrace_data_etm *btrace,
>
>             +         struct btrace_target_info *tinfo,
>
>             +         enum btrace_read_type type)
>
>             +{
>
>             +  struct perf_event_buffer *etm;
>
>             +  etm = &tinfo->variant.etm.etm;
>
>           
>
>         Please combine.  No forward declarations anymore.  The old code was written
>
>         when GDB was still C.
>
>     [Zied] done.
>
>
>           
>
>           
>
>           
>
>         regards,
>
>         markus.
>
>         Intel Deutschland GmbH
>
>         Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
>
>         Tel: +49 89 99 8853-0,www.intel.de  <http://www.intel.de>  <http://www.intel.de>  <http://www.intel.de>
>
>         Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
>
>         Chairperson of the Supervisory Board: Nicole Lau
>
>         Registered Office: Munich
>
>         Commercial Register: Amtsgericht Muenchen HRB 186928
>
>           
>
>     -- 
>
>
>     *Zied Guermazi*
>     founder
>
>     Trande GmbH
>     Leuschnerstraße 2
>     69469 Weinheim/Germany
>
>     Mobile: +491722645127
>     mailto:zied.guermazi@trande.de <mailto:zied.guermazi@trande.de>
>
>     *Trande GmbH*
>     Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
>     Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB
>     736209 - Geschäftsführung: Zied Guermazi
>
>     *Confidentiality Note*
>     This message is intended only for the use of the named
>     recipient(s) and may contain confidential and/or privileged
>     information. If you are not the intended recipient, please contact
>     the sender and delete the message. Any unauthorized use of the
>     information contained in this message is prohibited.
>
>     Intel Deutschland GmbH
>     Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
>     Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
>     Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany
>     Doon Silva
>     Chairperson of the Supervisory Board: Nicole Lau
>     Registered Office: Munich
>     Commercial Register: Amtsgericht Muenchen HRB 186928
>
> Intel Deutschland GmbH
> Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
> Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Office: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928
>
-- 

*Zied Guermazi*
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de <mailto:zied.guermazi@trande.de>

*Trande GmbH*
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 
- Geschäftsführung: Zied Guermazi

*Confidentiality Note*
This message is intended only for the use of the named recipient(s) and 
may contain confidential and/or privileged information. If you are not 
the intended recipient, please contact the sender and delete the 
message. Any unauthorized use of the information contained in this 
message is prohibited.


^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os
  2022-07-18 19:06             ` Zied Guermazi
@ 2022-07-19  5:04               ` Metzger, Markus T
  2022-07-21 22:20                 ` Zied Guermazi
  0 siblings, 1 reply; 35+ messages in thread
From: Metzger, Markus T @ 2022-07-19  5:04 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches

Hello Zied,

I was suggesting some

    perf_event_open_aux (struct perf_event_buffer *, const struct perf_event_attr *)

helper, that does all the mmaps necessary for the AUX buffer and a similar helper for the DATA buffer.
I don't think anybody is using both so the caller would do the perf_event_open () and then call the
respective helper to mmap the buffer it needs.  Would that work?

Since you need to fill in the header fields before mmaping the AUX buffer I see little benefit in
trying to share this with mmaping the DATA buffer.

Also, perf_event_open () is just a syscall wrapper and the configuration will be different for all
recording methods.  Not sure if it is worth trying to construct a helper, here.

regards,
markus.

From: Zied Guermazi <zied.guermazi@trande.de>
Sent: Montag, 18. Juli 2022 21:06
To: Metzger, Markus T <markus.t.metzger@intel.com>
Cc: gdb-patches@sourceware.org
Subject: Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os


hello Markus,

the proposal solves the issue for scoped_fd, we can use it and create a second scoped_fd using the returned file descriptor since scoped_fd has this constructor

explicit scoped_fd (int fd = -1) noexcept : m_fd (fd) {}.

for scoped_mmap we can not do the same since the class is missing a constructor taking void *mem and size_t length as arguments.

will it be fine to add such a constructor to the class? will it be enough to assign the memory pointer and the length to the class members?



Kind Regards

Zied Guermazi


On 20.06.22 14:52, Metzger, Markus T wrote:
Hello Zied,


/* open the scoped fd. */

static scoped_fd *
perf_event_open ( const struct perf_event_attr *event_attributes, const int pid)

The purpose of those scoped_ classes is to allocated objects on the stack to have them destroy their content in case of exceptions.

In the above example, perf_event_open() may use a scoped_fd internally but would return the actual file descriptor after releasing it.  The caller may put the returned file descriptor into its own scoped_fd object on its stack.

regards,
markus.



/* open the scoped fd. */

static scoped_fd *
perf_event_open ( const struct perf_event_attr *event_attributes, const int pid)

/*Open the scoped mmap data */

static scoped_mmap*
perf_event_mmap_data (const scoped_fd* fd, size_t* size, size_t page_size)

and

/*Open the scoped mmap aux */
static scoped_mmap*
perf_event_mmap_aux (const scoped_fd* fd, struct perf_event_mmap_page *header, size_t* size, size_t page_size)

and call them within linux_enable_bts, linux_enable_pt and linux_enable_etm

I tried two alternatives and both of them failed:

- Alternative 1: instantiate the returned scoped_mmap on the stack of the perf_event_mmap_data and perf_event_mmap_aux: the compiler refused to assign the scoped_mmap variables and they will not be valid anymore in the linux_enable_bts, linux_enable_pt and linux_enable_etm

- Alternative 2: instantiate the returned scoped_mmap on the heap by allocating them. the compiler compiles but, once we stop the tracing we ca can not start it again cause the resources are busy.

for the time being I will put this refactoring action on hold.

Any idea or support to progress further are welcome



Kind Regards

Zied Guermazi


On 13.05.22 07:31, Metzger, Markus T wrote:
Hello Zied,


+cs_etm_get_register (int cpu, const char *path)

+{

[...]

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;


[Zied] existing code in this file has an empty line before the last return everywhere. shall I stick to this convention? eliminating the empty lines before returns will bring inconsistency in the file. which coding convention do we have to apply here?



Yes, let’s stick to that and add the empty line.



so both perf_event_mmap_data perf_event_mmap_aux can be in fact reduced to one function if we give size_t size, size_t page_size, int offset as parameters.

BTS allocates the data buffer including the header, whereas PT allocates the aux buffer plus the header.  We can still make it one function, of course, but I don’t think that it will be any easier to read that way.



2- how to bring this change to the mainstream. here we have two alternatives : either I do this restructuring for etm only and then you take care of using the helper functions for bts and pt or I commit it as it is and then I issue a patch for this point only for bts PT and ETM. What do you prefer?

Ideally, we’d first restructure the existing code, then add the new use.  Are you able to test BTS and PT?

Regards,
Markus.


From: Zied Guermazi <zied.guermazi@trande.de><mailto:zied.guermazi@trande.de>
Sent: Friday, May 13, 2022 12:52 AM
To: Metzger, Markus T <markus.t.metzger@intel.com><mailto:markus.t.metzger@intel.com>; gdb-patches@sourceware.org<mailto:gdb-patches@sourceware.org>
Subject: Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os


Hello Markus,

thanks for your feedback, below are the reworking comments.

/Zied
On 23.06.21 10:00, Metzger, Markus T wrote:

Hello Zied,



This patch implement the lower layer for starting ad stopping

ARM CoreSight tracing on linux targets for arm and aarch64



The patch looks good overall.  There are a few style nits and I'd ask you to

split the PAGE_SIZE changes into a separate patch as they are unrelated.



Then, there's the discussion about sharing perf_event buffer mapping.

I pointed out which parts I believe can be shared.





+/* Teardown branch tracing.  */

+

+void

+arm_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)

+{

+  /* Ignore errors.  */

+  linux_disable_btrace (tinfo);

+}

+

+enum btrace_error

+arm_linux_nat_target::read_btrace (struct btrace_data *data,

+                           struct btrace_target_info *btinfo,

+                           enum btrace_read_type type)

+{

+  return linux_read_btrace (data, btinfo, type);

+}

+

+/* See to_btrace_conf in target.h.  */

+

+const struct btrace_config *

+arm_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)

+{

+  return linux_btrace_conf (btinfo);

+}



There's some inconsistency in comments on functions ranging from no comment

over referring to the original target struct, to an own comment.
[Zied] I will align the comments. please notice that the same applies to x86-linux-nat.c (it was a copy-paste from it)










@@ -483,10 +487,11 @@ linux_enable_bts (ptid_t ptid, const struct

btrace_config_bts *conf)

  scoped_fd fd (syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0));

  if (fd.get () < 0)

    diagnose_perf_event_open_fail ();

+  long page_size = sysconf (_SC_PAGESIZE);



Please split those PAGE_SIZE changes into a separate patch.  This is unrelated

to what this patch is doing.



Note that PAGE_SIZE was unsigned whereas sysconf () returns a signed integer.

I'd expect compilers to require proper casting.
[Zied] done










+/* Enable ARM CoreSight ETM tracing.  */

+

+static struct btrace_target_info *

+linux_enable_etm (ptid_t ptid, const struct btrace_config_etm *conf)

+{

[...]

+  etm->attr.sample_type = PERF_SAMPLE_CPU;

+  etm->attr.read_format = PERF_FORMAT_ID;

+  etm->attr.sample_id_all = 1;



You enable sampling.  Wouldn't you need to mmap the data buffer, as well?
[Zied] it is not needed for current implementation. removed.










This ...



+  errno = 0;

+  scoped_fd fd (syscall (SYS_perf_event_open, &etm->attr, pid, -1, -1, 0));

+  if (fd.get () < 0)

+    diagnose_perf_event_open_fail ();

+

+  /* Allocate the configuration page.  */

+  long page_size = sysconf (_SC_PAGESIZE);

+  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE,

MAP_SHARED,

+             fd.get (), 0);

+  if (data.get () == MAP_FAILED)

+    error (_("Failed to map trace user page: %s."), safe_strerror (errno));

+

+  struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)

+    data.get ();

+

+  header->aux_offset = header->data_offset + header->data_size;

+  /* Convert the requested size in bytes to pages (rounding up).  */

+  pages = ((size_t) conf->size / page_size

+     + ((conf->size % page_size) == 0 ? 0 : 1));

+  /* We need at least one page.  */

+  if (pages == 0)

+    pages = 1;

+

+  /* The buffer size can be requested in powers of two pages.  Adjust PAGES

+     to the next power of two.  */

+  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)

+    if ((pages & ((size_t) 1 << pg)) != 0)

+      pages += ((size_t) 1 << pg);

+

+  /* We try to allocate the requested size.

+     If that fails, try to get as much as we can.  */

+  scoped_mmap aux;

+  for (; pages > 0; pages >>= 1)

+    {

+      size_t length;

+      __u64 data_size;

+      data_size = (__u64) pages * page_size;

+

+      /* Don't ask for more than we can represent in the configuration.  */

+      if ((__u64) UINT_MAX < data_size)

+  continue;

+

+      length = (size_t) data_size;

+

+      /* Check for overflows.  */

+      if ((__u64) length != data_size)

+  continue;

+

+      header->aux_size = data_size;

+

+      errno = 0;

+      aux.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (),

+          header->aux_offset);

+      if (aux.get () != MAP_FAILED)

+  break;

+    }

+  if (pages == 0)

+    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));

+

+  etm->etm.size = aux.size ();

+  etm->etm.mem = (const uint8_t *) aux.release ();

+  etm->etm.data_head = &header->aux_head;

+  etm->etm.last_head = header->aux_tail;

+  etm->header = (struct perf_event_mmap_page *) data.release ();

+  gdb_assert (etm->header == header);



... can be shared with btrace_enable_pt () by introducing some



perf_event_open_aux (struct perf_event_buffer *, const struct perf_event_attr *)



helper.



And if you indeed need to mmap the data buffer, as well, we can share that with

btrace_enable_bts (), although we'd need some more restructuring to leave

perf_event_open to the caller and just allocate the data and aux buffers using

two helpers - they would again look very similar but need to touch a different

set of fields in the header, so I'd keep those separate.



btrace_enable_foo () would then become

{

  perf_event_open ()

  perf_event_mmap_data ()

  perf_event_mmap_aux ()  /* not for bts */

}



[Zied] I like the idea, there are two aspects that we need to consider to bring it to the mainstream code:

- 1: interface and scope definition

static scoped_fd perf_event_open ( const struct perf_event_attr *event_attributes,  const int pid )

{

  errno = 0;
  scoped_fd fd (syscall (SYS_perf_event_open, event_attributes, pid, -1, -1, 0));
  if (fd.get () < 0)
    diagnose_perf_event_open_fail ();

  return fd;

}

this function will only open the file descriptor and return it

static scoped_mmap data perf_event_mmap_data (const scoped_fd fd, size_t size, size_t page_size, int offset)

{

 //alternative 1 just create it and return it

  /* Allocate the configuration page. */
  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
            fd.get (), 0);
  if (data.get () == MAP_FAILED)
    error (_("Failed to map trace user page: %s."), safe_strerror (errno));

  return data;

 //alternative 2, create it it and make sure that we resize it to the highest possible power of 2 supported by the system

/* Convert the requested size in bytes to pages (rounding up).  */
  pages = size / page_size
       + (size % page_size) == 0 ? 0 : 1));
  /* We need at least one page.  */
  if (pages == 0)
    pages = 1;

  /* The buffer size can be requested in powers of two pages.  Adjust PAGES
     to the next power of two.  */
  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
    if ((pages & ((size_t) 1 << pg)) != 0)
      pages += ((size_t) 1 << pg);

  /* We try to allocate the requested size.
     If that fails, try to get as much as we can.  */
  scoped_mmap data;
  for (; pages > 0; pages >>= 1)
    {
      size_t length;
      __u64 data_size;

      data_size = (__u64) pages * page_size;

      /* Don't ask for more than we can represent in the configuration.  */
      if ((__u64) UINT_MAX < data_size)
    continue;

      size = (size_t) data_size;
      length = size + page_size;

      /* Check for overflows.  */
      if ((__u64) length != data_size + page_size)
    continue;

      errno = 0;
      /* The number of pages we request needs to be a power of two.  */
      data.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (), offset);
      if (data.get () != MAP_FAILED)
    break;
    }

  if (pages == 0)
    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));

}

static scoped_mmap perf_event_mmap_aux (const scoped_fd fd, size_t size, size_t page_size, int offset)

{

//idem, the function is similar to previous one it is only the offset in data.reset call that changes

}

so both perf_event_mmap_data perf_event_mmap_aux can be in fact reduced to one function if we give size_t size, size_t page_size, int offset as parameters.

2- how to bring this change to the mainstream. here we have two alternatives : either I do this restructuring for etm only and then you take care of using the helper functions for bts and pt or I commit it as it is and then I issue a patch for this point only for bts PT and ETM. What do you prefer?





+  length = fread (buffer, 1, length, file.get ());

+  buffer[length]='\0';



Spaces around =.
[Zied] done.








+  while ((--length) != 0)

+    {

+      if ((buffer[length] == ',') || (buffer[length] == '-'))

+  {

+    length++;

+    break;

+  }

+    }

+

+  int cpu_count;

+  int found = sscanf (&buffer[length], "%d", &cpu_count);

+  if (found < 1)

+    error (_("Failed to get cpu count in %s: %s."),

+       buffer, safe_strerror (errno));

+

+  cpu_count ++;

+  return (cpu_count);



No need for ().
[Zied] done.










+  char filename[PATH_MAX];

+  snprintf (filename, PATH_MAX,



sizeof (filename)
[Zied] done.








+  char filename[PATH_MAX];

+

+  /* Get coresight register from sysfs.  */

+  snprintf (filename, PATH_MAX,



sizeof (filename)
[Zied] done








+      "/sys/bus/event_source/devices/cs_etm/cpu%d/%s", cpu, path);

+  errno = 0;

+  gdb_file_up file = gdb_fopen_cloexec (filename, "r");

+  if (file.get () == nullptr)

+    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;

+}



Empty line before return?  I'd also remove the empty line between the

declaration of val and the call to fscanf ().



There are several very similar functions in this patch and each is structured

differently:



+perf_event_etm_event_type ()

+{

[...]

+

+  int type, found = fscanf (file.get (), "%d", &type);

+  if (found != 1)

+    error (_("Failed to read the ETM event type from %s."), filename);

+

+  return type;



+get_cpu_count (void)

+{

[...]

+

+  int cpu_count;

+  int found = sscanf (&buffer[length], "%d", &cpu_count);

+  if (found < 1)

+    error (_("Failed to get cpu count in %s: %s."),

+             buffer, safe_strerror (errno));

+

+  cpu_count ++;

+  return (cpu_count);



+perf_event_etm_event_sink (const struct btrace_config_etm *conf)

+{

[...]

+

+  unsigned int sink;

+  int  found = fscanf (file.get (), "0x%x", &sink);

+  if (found != 1)

+    error (_("Failed to read the ETM sink from %s."), filename);

+

+  return sink;



+cs_etm_get_register (int cpu, const char *path)

+{

[...]

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;


[Zied] existing code in this file has an empty line before the last return everywhere. shall I stick to this convention? eliminating the empty lines before returns will bring inconsistency in the file. which coding convention do we have to apply here?






+

+#define CORESIGHT_ETM_PMU_SEED  0x10

+

+/* Calculate trace_id for this cpu

+   to be kept aligned with coresight-pmu.h.  */

+

+static inline int

+coresight_get_trace_id (int cpu)

+{

+  return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));



In patch 3, you wrote



+  /* Trace id for this thread.

+     On a linux system, trace_id is assigned per cpu. The kernel copies

+     the traces of each thread in a dedicated ring buffer. By this,

+     traces belonging to different threads are de-multiplexed.

+     On an RTOS system, especially when routing the traces outside of the SoC,

+     the OS has no other mean for de-multiplexing the traces than

+     the trace_id. The hardware (ETM IP) reserves 7 bits for the trace_id.

+     On linux system trace id is not needed, set it to 0xFF to ignore it

+     during parsing.  */

+  uint8_t trace_id;



Should this function return uint8_t and check that the ID is 7 bit max?
[Zied] done, reserved and not allowed values are also generating a warning now.










+static void

+fill_etm_trace_params (struct cs_etm_trace_params *etm_trace_params, int

cpu)

+{

+  if (cs_etm_is_etmv4 (cpu) == true)



No need for explicit checks on bool.
[Zied] done.










+static void

+linux_fill_btrace_etm_config (struct btrace_target_info *tinfo,

+                        struct btrace_data_etm_config *conf)

+{

+

+  cs_etm_trace_params etm_trace_params;



Please declare at initialization time.
[Zied] pushed forwards before the for loop








+  conf->cpu_count = get_cpu_count ();

+  conf->etm_trace_params = new std::vector<cs_etm_trace_params>;

+  for (int i = 0; i < conf->cpu_count; i++)

+    {

+      fill_etm_trace_params (&etm_trace_params,i);

+      conf->etm_trace_params->push_back (etm_trace_params);

+    }



We need to avoid leaking the vector when fill_etm_trace_params () throws.







+static enum btrace_error

+linux_read_etm (struct btrace_data_etm *btrace,

+         struct btrace_target_info *tinfo,

+         enum btrace_read_type type)

+{

+  struct perf_event_buffer *etm;

+  etm = &tinfo->variant.etm.etm;



Please combine.  No forward declarations anymore.  The old code was written

when GDB was still C.
[Zied] done.










regards,

markus.

Intel Deutschland GmbH

Registered Address: Am Campeon 10, 85579 Neubiberg, Germany

Tel: +49 89 99 8853-0, www.intel.de<http://www.intel.de> <http://www.intel.de><http://www.intel.de>

Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva

Chairperson of the Supervisory Board: Nicole Lau

Registered Office: Munich

Commercial Register: Amtsgericht Muenchen HRB 186928


--




Zied Guermazi
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de

Trande GmbH
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 - Geschäftsführung: Zied Guermazi

Confidentiality Note
This message is intended only for the use of the named recipient(s) and may contain confidential and/or privileged information. If you are not the intended recipient, please contact the sender and delete the message. Any unauthorized use of the information contained in this message is prohibited.



Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de<http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928




Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de<http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
--


Zied Guermazi
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de

Trande GmbH
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 - Geschäftsführung: Zied Guermazi

Confidentiality Note
This message is intended only for the use of the named recipient(s) and may contain confidential and/or privileged information. If you are not the intended recipient, please contact the sender and delete the message. Any unauthorized use of the information contained in this message is prohibited.


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 35+ messages in thread

* Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os
  2022-07-19  5:04               ` Metzger, Markus T
@ 2022-07-21 22:20                 ` Zied Guermazi
  2022-07-25 14:33                   ` Metzger, Markus T
  0 siblings, 1 reply; 35+ messages in thread
From: Zied Guermazi @ 2022-07-21 22:20 UTC (permalink / raw)
  To: Metzger, Markus T; +Cc: gdb-patches

Hello Markus,

linux_enable_pt and linux_enable_etm are taking a struct perf_event_attr 
attras input and are filling struct perf_event_buffer aux_buffer, struct 
perf_event_mmap_page *header and int file

theses are the fields of btrace_tinfo_pt or btrace_tinfo_etm.

perf_event_open_aux will then have the signature

     perf_event_open_aux (struct perf_event_buffer *aux_buffer, struct 
perf_event_mmap_page **header,  int *file, const struct perf_event_attr *)

alternatively we can unify  struct btrace_tinfo_pt and struct 
btrace_tinfo_etm and even struct btrace_tinfo_etm to

struct btrace_tinfo
{
   /* The Linux perf_event configuration for collecting the trace. */
   struct perf_event_attr attr;

   /* The perf event file.  */
   int file;

   /* The perf event configuration page. */
   volatile struct perf_event_mmap_page *header;

   /* The perf event buffer.  */
   struct perf_event_buffer event_buffer;
};

perf_event_open_aux will then have the signature

perf_event_open_aux (struct btrace_tinfo *, const struct perf_event_attr *)


which alternative do you prefer?


Kind Regards

Zied Guermazi


On 19.07.22 07:04, Metzger, Markus T wrote:
>
> Hello Zied,
>
> I was suggesting some
>
> perf_event_open_aux (struct perf_event_buffer *, const struct 
> perf_event_attr *)
>
> helper, that does all the mmaps necessary for the AUX buffer and a 
> similar helper for the DATA buffer.
>
> I don't think anybody is using both so the caller would do the 
> perf_event_open () and then call the
>
> respective helper to mmap the buffer it needs.  Would that work?
>
> Since you need to fill in the header fields before mmaping the AUX 
> buffer I see little benefit in
>
> trying to share this with mmaping the DATA buffer.
>
> Also, perf_event_open () is just a syscall wrapper and the 
> configuration will be different for all
>
> recording methods.  Not sure if it is worth trying to construct a 
> helper, here.
>
> regards,
>
> markus.
>
> *From:* Zied Guermazi <zied.guermazi@trande.de>
> *Sent:* Montag, 18. Juli 2022 21:06
> *To:* Metzger, Markus T <markus.t.metzger@intel.com>
> *Cc:* gdb-patches@sourceware.org
> *Subject:* Re: [PATCH v6 4/7] start/stop btrace with coresight etm and 
> collect etm buffer on linux os
>
> hello Markus,
>
> the proposal solves the issue for scoped_fd, we can use it and create 
> a second scoped_fd using the returned file descriptor since scoped_fd 
> has this constructor
>
> explicit scoped_fd (int fd = -1) noexcept : m_fd (fd) {}.
>
> for scoped_mmap we can not do the same since the class is missing a 
> constructor taking void *mem and size_t length as arguments.
>
> will it be fine to add such a constructor to the class? will it be 
> enough to assign the memory pointer and the length to the class members?
>
> Kind Regards
>
> Zied Guermazi
>
> On 20.06.22 14:52, Metzger, Markus T wrote:
>
>     Hello Zied,
>
>     /* open the scoped fd. */
>
>     static scoped_fd *
>     perf_event_open ( const struct perf_event_attr *event_attributes,
>     const int pid)
>
>     The purpose of those scoped_ classes is to allocated objects on
>     the stack to have them destroy their content in case of exceptions.
>
>     In the above example, perf_event_open() may use a scoped_fd
>     internally but would return the actual file descriptor after
>     releasing it.  The caller may put the returned file descriptor
>     into its own scoped_fd object on its stack.
>
>     regards,
>
>     markus.
>
>     /* open the scoped fd. */
>
>     static scoped_fd *
>     perf_event_open ( const struct perf_event_attr *event_attributes,
>     const int pid)
>
>     /*Open the scoped mmap data */
>
>     static scoped_mmap*
>     perf_event_mmap_data (const scoped_fd* fd, size_t* size, size_t
>     page_size)
>
>     and
>
>     /*Open the scoped mmap aux */
>
>     static scoped_mmap*
>     perf_event_mmap_aux (const scoped_fd* fd, struct
>     perf_event_mmap_page *header, size_t* size, size_t page_size)
>
>     and call them within linux_enable_bts, linux_enable_pt and
>     linux_enable_etm
>
>     I tried two alternatives and both of them failed:
>
>     - Alternative 1: instantiate the returned scoped_mmap on the stack
>     of the perf_event_mmap_data and perf_event_mmap_aux: the compiler
>     refused to assign the scoped_mmap variables and they will not be
>     valid anymore in the linux_enable_bts, linux_enable_pt and
>     linux_enable_etm
>
>     - Alternative 2: instantiate the returned scoped_mmap on the heap
>     by allocating them. the compiler compiles but, once we stop the
>     tracing we ca can not start it again cause the resources are busy.
>
>     for the time being I will put this refactoring action on hold.
>
>     Any idea or support to progress further are welcome
>
>     Kind Regards
>
>     Zied Guermazi
>
>     On 13.05.22 07:31, Metzger, Markus T wrote:
>
>         Hello Zied,
>
>         +cs_etm_get_register (int cpu, const char *path)
>
>         +{
>
>         [...]
>
>         +
>
>         +  uint32_t val = 0;
>
>         +
>
>         +  int  found = fscanf (file.get (), "0x%x", &val);
>
>         +  if (found != 1)
>
>         +    error (_("Failed to read coresight register from %s."), filename);
>
>         +  return val;
>
>           
>
>         [Zied] existing code in this file has an empty line before the
>         last return everywhere. shall I stick to this convention?
>         eliminating the empty lines before returns will bring
>         inconsistency in the file. which coding convention do we have
>         to apply here?
>
>
>
>         Yes, let’s stick to that and add the empty line.
>
>         so both perf_event_mmap_data perf_event_mmap_aux can be in
>         fact reduced to one function if we give size_t size, size_t
>         page_size, int offset as parameters.
>
>         BTS allocates the data buffer including the header, whereas PT
>         allocates the aux buffer plus the header.  We can still make
>         it one function, of course, but I don’t think that it will be
>         any easier to read that way.
>
>         2- how to bring this change to the mainstream. here we have
>         two alternatives : either I do this restructuring for etm only
>         and then you take care of using the helper functions for bts
>         and pt or I commit it as it is and then I issue a patch for
>         this point only for bts PT and ETM. What do you prefer?
>
>         Ideally, we’d first restructure the existing code, then add
>         the new use.  Are you able to test BTS and PT?
>
>         Regards,
>
>         Markus.
>
>         *From:* Zied Guermazi <zied.guermazi@trande.de>
>         <mailto:zied.guermazi@trande.de>
>         *Sent:* Friday, May 13, 2022 12:52 AM
>         *To:* Metzger, Markus T <markus.t.metzger@intel.com>
>         <mailto:markus.t.metzger@intel.com>; gdb-patches@sourceware.org
>         *Subject:* Re: [PATCH v6 4/7] start/stop btrace with coresight
>         etm and collect etm buffer on linux os
>
>         Hello Markus,
>
>         thanks for your feedback, below are the reworking comments.
>
>         /Zied
>
>         On 23.06.21 10:00, Metzger, Markus T wrote:
>
>             Hello Zied,
>
>               
>
>                 This patch implement the lower layer for starting ad stopping
>
>                 ARM CoreSight tracing on linux targets for arm and aarch64
>
>               
>
>             The patch looks good overall.  There are a few style nits and I'd ask you to
>
>             split the PAGE_SIZE changes into a separate patch as they are unrelated.
>
>               
>
>             Then, there's the discussion about sharing perf_event buffer mapping.
>
>             I pointed out which parts I believe can be shared.
>
>               
>
>               
>
>                 +/* Teardown branch tracing.  */
>
>                 +
>
>                 +void
>
>                 +arm_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)
>
>                 +{
>
>                 +  /* Ignore errors.  */
>
>                 +  linux_disable_btrace (tinfo);
>
>                 +}
>
>                 +
>
>                 +enum btrace_error
>
>                 +arm_linux_nat_target::read_btrace (struct btrace_data *data,
>
>                 +                           struct btrace_target_info *btinfo,
>
>                 +                           enum btrace_read_type type)
>
>                 +{
>
>                 +  return linux_read_btrace (data, btinfo, type);
>
>                 +}
>
>                 +
>
>                 +/* See to_btrace_conf in target.h.  */
>
>                 +
>
>                 +const struct btrace_config *
>
>                 +arm_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)
>
>                 +{
>
>                 +  return linux_btrace_conf (btinfo);
>
>                 +}
>
>               
>
>             There's some inconsistency in comments on functions ranging from no comment
>
>             over referring to the original target struct, to an own comment.
>
>         [Zied] I will align the comments. please notice that the same
>         applies to x86-linux-nat.c (it was a copy-paste from it)
>
>
>
>               
>
>               
>
>               
>
>                 @@ -483,10 +487,11 @@ linux_enable_bts (ptid_t ptid, const struct
>
>                 btrace_config_bts *conf)
>
>                    scoped_fd fd (syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0));
>
>                    if (fd.get () < 0)
>
>                      diagnose_perf_event_open_fail ();
>
>                 +  long page_size = sysconf (_SC_PAGESIZE);
>
>               
>
>             Please split those PAGE_SIZE changes into a separate patch.  This is unrelated
>
>             to what this patch is doing.
>
>               
>
>             Note that PAGE_SIZE was unsigned whereas sysconf () returns a signed integer.
>
>             I'd expect compilers to require proper casting.
>
>         [Zied] done
>
>
>
>               
>
>               
>
>               
>
>                 +/* Enable ARM CoreSight ETM tracing.  */
>
>                 +
>
>                 +static struct btrace_target_info *
>
>                 +linux_enable_etm (ptid_t ptid, const struct btrace_config_etm *conf)
>
>                 +{
>
>             [...]
>
>                 +  etm->attr.sample_type = PERF_SAMPLE_CPU;
>
>                 +  etm->attr.read_format = PERF_FORMAT_ID;
>
>                 +  etm->attr.sample_id_all = 1;
>
>               
>
>             You enable sampling.  Wouldn't you need to mmap the data buffer, as well?
>
>         [Zied] it is not needed for current implementation. removed.
>
>
>
>               
>
>               
>
>               
>
>             This ...
>
>               
>
>                 +  errno = 0;
>
>                 +  scoped_fd fd (syscall (SYS_perf_event_open, &etm->attr, pid, -1, -1, 0));
>
>                 +  if (fd.get () < 0)
>
>                 +    diagnose_perf_event_open_fail ();
>
>                 +
>
>                 +  /* Allocate the configuration page.  */
>
>                 +  long page_size = sysconf (_SC_PAGESIZE);
>
>                 +  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE,
>
>                 MAP_SHARED,
>
>                 +             fd.get (), 0);
>
>                 +  if (data.get () == MAP_FAILED)
>
>                 +    error (_("Failed to map trace user page: %s."), safe_strerror (errno));
>
>                 +
>
>                 +  struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)
>
>                 +    data.get ();
>
>                 +
>
>                 +  header->aux_offset = header->data_offset + header->data_size;
>
>                 +  /* Convert the requested size in bytes to pages (rounding up).  */
>
>                 +  pages = ((size_t) conf->size / page_size
>
>                 +     + ((conf->size % page_size) == 0 ? 0 : 1));
>
>                 +  /* We need at least one page.  */
>
>                 +  if (pages == 0)
>
>                 +    pages = 1;
>
>                 +
>
>                 +  /* The buffer size can be requested in powers of two pages.  Adjust PAGES
>
>                 +     to the next power of two.  */
>
>                 +  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
>
>                 +    if ((pages & ((size_t) 1 << pg)) != 0)
>
>                 +      pages += ((size_t) 1 << pg);
>
>                 +
>
>                 +  /* We try to allocate the requested size.
>
>                 +     If that fails, try to get as much as we can.  */
>
>                 +  scoped_mmap aux;
>
>                 +  for (; pages > 0; pages >>= 1)
>
>                 +    {
>
>                 +      size_t length;
>
>                 +      __u64 data_size;
>
>                 +      data_size = (__u64) pages * page_size;
>
>                 +
>
>                 +      /* Don't ask for more than we can represent in the configuration.  */
>
>                 +      if ((__u64) UINT_MAX < data_size)
>
>                 +  continue;
>
>                 +
>
>                 +      length = (size_t) data_size;
>
>                 +
>
>                 +      /* Check for overflows.  */
>
>                 +      if ((__u64) length != data_size)
>
>                 +  continue;
>
>                 +
>
>                 +      header->aux_size = data_size;
>
>                 +
>
>                 +      errno = 0;
>
>                 +      aux.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (),
>
>                 +          header->aux_offset);
>
>                 +      if (aux.get () != MAP_FAILED)
>
>                 +  break;
>
>                 +    }
>
>                 +  if (pages == 0)
>
>                 +    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));
>
>                 +
>
>                 +  etm->etm.size = aux.size ();
>
>                 +  etm->etm.mem = (const uint8_t *) aux.release ();
>
>                 +  etm->etm.data_head = &header->aux_head;
>
>                 +  etm->etm.last_head = header->aux_tail;
>
>                 +  etm->header = (struct perf_event_mmap_page *) data.release ();
>
>                 +  gdb_assert (etm->header == header);
>
>               
>
>             ... can be shared with btrace_enable_pt () by introducing some
>
>               
>
>             perf_event_open_aux (struct perf_event_buffer *, const struct perf_event_attr *)
>
>               
>
>             helper.
>
>               
>
>             And if you indeed need to mmap the data buffer, as well, we can share that with
>
>             btrace_enable_bts (), although we'd need some more restructuring to leave
>
>             perf_event_open to the caller and just allocate the data and aux buffers using
>
>             two helpers - they would again look very similar but need to touch a different
>
>             set of fields in the header, so I'd keep those separate.
>
>               
>
>             btrace_enable_foo () would then become
>
>             {
>
>                perf_event_open ()
>
>                perf_event_mmap_data ()
>
>                perf_event_mmap_aux ()  /* not for bts */
>
>             }
>
>               
>
>         [Zied] I like the idea, there are two aspects that we need to
>         consider to bring it to the mainstream code:
>
>         - 1: interface and scope definition
>
>         static scoped_fd perf_event_open ( const struct
>         perf_event_attr *event_attributes,  const int pid )
>
>         {
>
>           errno = 0;
>           scoped_fd fd (syscall (SYS_perf_event_open,
>         event_attributes, pid, -1, -1, 0));
>           if (fd.get () < 0)
>             diagnose_perf_event_open_fail ();
>
>           return fd;
>
>         }
>
>         this function will only open the file descriptor and return it
>
>         static scoped_mmap data perf_event_mmap_data (const scoped_fd
>         fd, size_t size, size_t page_size, int offset)
>
>         {
>
>          //alternative 1 just create it and return it
>
>           /* Allocate the configuration page. */
>           scoped_mmap data (nullptr, page_size, PROT_READ |
>         PROT_WRITE, MAP_SHARED,
>                     fd.get (), 0);
>           if (data.get () == MAP_FAILED)
>             error (_("Failed to map trace user page: %s."),
>         safe_strerror (errno));
>
>           return data;
>
>          //alternative 2, create it it and make sure that we resize it
>         to the highest possible power of 2 supported by the system
>
>         /* Convert the requested size in bytes to pages (rounding up).  */
>           pages = size / page_size
>                + (size % page_size) == 0 ? 0 : 1));
>           /* We need at least one page.  */
>           if (pages == 0)
>             pages = 1;
>
>           /* The buffer size can be requested in powers of two pages. 
>         Adjust PAGES
>              to the next power of two.  */
>           for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
>             if ((pages & ((size_t) 1 << pg)) != 0)
>               pages += ((size_t) 1 << pg);
>
>           /* We try to allocate the requested size.
>              If that fails, try to get as much as we can. */
>           scoped_mmap data;
>           for (; pages > 0; pages >>= 1)
>             {
>               size_t length;
>               __u64 data_size;
>
>               data_size = (__u64) pages * page_size;
>
>               /* Don't ask for more than we can represent in the
>         configuration.  */
>               if ((__u64) UINT_MAX < data_size)
>             continue;
>
>               size = (size_t) data_size;
>               length = size + page_size;
>
>               /* Check for overflows.  */
>               if ((__u64) length != data_size + page_size)
>             continue;
>
>               errno = 0;
>               /* The number of pages we request needs to be a power of
>         two.  */
>               data.reset (nullptr, length, PROT_READ, MAP_SHARED,
>         fd.get (), offset);
>               if (data.get () != MAP_FAILED)
>             break;
>             }
>
>           if (pages == 0)
>             error (_("Failed to map trace buffer: %s."), safe_strerror
>         (errno));
>
>         }
>
>         static scoped_mmap perf_event_mmap_aux (const scoped_fd fd,
>         size_t size, size_t page_size, int offset)
>
>         {
>
>         //idem, the function is similar to previous one it is only the
>         offset in data.reset call that changes
>
>         }
>
>         so both perf_event_mmap_data perf_event_mmap_aux can be in
>         fact reduced to one function if we give size_t size, size_t
>         page_size, int offset as parameters.
>
>         2- how to bring this change to the mainstream. here we have
>         two alternatives : either I do this restructuring for etm only
>         and then you take care of using the helper functions for bts
>         and pt or I commit it as it is and then I issue a patch for
>         this point only for bts PT and ETM. What do you prefer?
>
>               
>
>                 +  length = fread (buffer, 1, length, file.get ());
>
>                 +  buffer[length]='\0';
>
>               
>
>             Spaces around =.
>
>         [Zied] done.
>
>
>
>               
>
>               
>
>                 +  while ((--length) != 0)
>
>                 +    {
>
>                 +      if ((buffer[length] == ',') || (buffer[length] == '-'))
>
>                 +  {
>
>                 +    length++;
>
>                 +    break;
>
>                 +  }
>
>                 +    }
>
>                 +
>
>                 +  int cpu_count;
>
>                 +  int found = sscanf (&buffer[length], "%d", &cpu_count);
>
>                 +  if (found < 1)
>
>                 +    error (_("Failed to get cpu count in %s: %s."),
>
>                 +       buffer, safe_strerror (errno));
>
>                 +
>
>                 +  cpu_count ++;
>
>                 +  return (cpu_count);
>
>               
>
>             No need for ().
>
>         [Zied] done.
>
>
>
>               
>
>               
>
>               
>
>                 +  char filename[PATH_MAX];
>
>                 +  snprintf (filename, PATH_MAX,
>
>               
>
>             sizeof (filename)
>
>         [Zied] done.
>
>
>
>               
>
>               
>
>                 +  char filename[PATH_MAX];
>
>                 +
>
>                 +  /* Get coresight register from sysfs.  */
>
>                 +  snprintf (filename, PATH_MAX,
>
>               
>
>             sizeof (filename)
>
>         [Zied] done
>
>
>
>               
>
>               
>
>                 +      "/sys/bus/event_source/devices/cs_etm/cpu%d/%s", cpu, path);
>
>                 +  errno = 0;
>
>                 +  gdb_file_up file = gdb_fopen_cloexec (filename, "r");
>
>                 +  if (file.get () == nullptr)
>
>                 +    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));
>
>                 +
>
>                 +  uint32_t val = 0;
>
>                 +
>
>                 +  int  found = fscanf (file.get (), "0x%x", &val);
>
>                 +  if (found != 1)
>
>                 +    error (_("Failed to read coresight register from %s."), filename);
>
>                 +  return val;
>
>                 +}
>
>               
>
>             Empty line before return?  I'd also remove the empty line between the
>
>             declaration of val and the call to fscanf ().
>
>               
>
>             There are several very similar functions in this patch and each is structured
>
>             differently:
>
>               
>
>             +perf_event_etm_event_type ()
>
>             +{
>
>             [...]
>
>             +
>
>             +  int type, found = fscanf (file.get (), "%d", &type);
>
>             +  if (found != 1)
>
>             +    error (_("Failed to read the ETM event type from %s."), filename);
>
>             +
>
>             +  return type;
>
>               
>
>             +get_cpu_count (void)
>
>             +{
>
>             [...]
>
>             +
>
>             +  int cpu_count;
>
>             +  int found = sscanf (&buffer[length], "%d", &cpu_count);
>
>             +  if (found < 1)
>
>             +    error (_("Failed to get cpu count in %s: %s."),
>
>             +             buffer, safe_strerror (errno));
>
>             +
>
>             +  cpu_count ++;
>
>             +  return (cpu_count);
>
>               
>
>             +perf_event_etm_event_sink (const struct btrace_config_etm *conf)
>
>             +{
>
>             [...]
>
>             +
>
>             +  unsigned int sink;
>
>             +  int  found = fscanf (file.get (), "0x%x", &sink);
>
>             +  if (found != 1)
>
>             +    error (_("Failed to read the ETM sink from %s."), filename);
>
>             +
>
>             +  return sink;
>
>               
>
>             +cs_etm_get_register (int cpu, const char *path)
>
>             +{
>
>             [...]
>
>             +
>
>             +  uint32_t val = 0;
>
>             +
>
>             +  int  found = fscanf (file.get (), "0x%x", &val);
>
>             +  if (found != 1)
>
>             +    error (_("Failed to read coresight register from %s."), filename);
>
>             +  return val;
>
>               
>
>         [Zied] existing code in this file has an empty line before the
>         last return everywhere. shall I stick to this convention?
>         eliminating the empty lines before returns will bring
>         inconsistency in the file. which coding convention do we have
>         to apply here?
>
>
>
>               
>
>                 +
>
>                 +#define CORESIGHT_ETM_PMU_SEED  0x10
>
>                 +
>
>                 +/* Calculate trace_id for this cpu
>
>                 +   to be kept aligned with coresight-pmu.h.  */
>
>                 +
>
>                 +static inline int
>
>                 +coresight_get_trace_id (int cpu)
>
>                 +{
>
>                 +  return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));
>
>               
>
>             In patch 3, you wrote
>
>               
>
>             +  /* Trace id for this thread.
>
>             +     On a linux system, trace_id is assigned per cpu. The kernel copies
>
>             +     the traces of each thread in a dedicated ring buffer. By this,
>
>             +     traces belonging to different threads are de-multiplexed.
>
>             +     On an RTOS system, especially when routing the traces outside of the SoC,
>
>             +     the OS has no other mean for de-multiplexing the traces than
>
>             +     the trace_id. The hardware (ETM IP) reserves 7 bits for the trace_id.
>
>             +     On linux system trace id is not needed, set it to 0xFF to ignore it
>
>             +     during parsing.  */
>
>             +  uint8_t trace_id;
>
>               
>
>             Should this function return uint8_t and check that the ID is 7 bit max?
>
>         [Zied] done, reserved and not allowed values are also
>         generating a warning now.
>
>
>
>               
>
>               
>
>               
>
>                 +static void
>
>                 +fill_etm_trace_params (struct cs_etm_trace_params *etm_trace_params, int
>
>                 cpu)
>
>                 +{
>
>                 +  if (cs_etm_is_etmv4 (cpu) == true)
>
>               
>
>             No need for explicit checks on bool.
>
>         [Zied] done.
>
>
>
>               
>
>               
>
>               
>
>                 +static void
>
>                 +linux_fill_btrace_etm_config (struct btrace_target_info *tinfo,
>
>                 +                        struct btrace_data_etm_config *conf)
>
>                 +{
>
>                 +
>
>                 +  cs_etm_trace_params etm_trace_params;
>
>               
>
>             Please declare at initialization time.
>
>         [Zied] pushed forwards before the for loop
>
>
>
>               
>
>               
>
>                 +  conf->cpu_count = get_cpu_count ();
>
>                 +  conf->etm_trace_params = new std::vector<cs_etm_trace_params>;
>
>                 +  for (int i = 0; i < conf->cpu_count; i++)
>
>                 +    {
>
>                 +      fill_etm_trace_params (&etm_trace_params,i);
>
>                 +      conf->etm_trace_params->push_back (etm_trace_params);
>
>                 +    }
>
>               
>
>             We need to avoid leaking the vector when fill_etm_trace_params () throws.
>
>               
>
>               
>
>               
>
>                 +static enum btrace_error
>
>                 +linux_read_etm (struct btrace_data_etm *btrace,
>
>                 +         struct btrace_target_info *tinfo,
>
>                 +         enum btrace_read_type type)
>
>                 +{
>
>                 +  struct perf_event_buffer *etm;
>
>                 +  etm = &tinfo->variant.etm.etm;
>
>               
>
>             Please combine.  No forward declarations anymore.  The old code was written
>
>             when GDB was still C.
>
>         [Zied] done.
>
>
>
>               
>
>               
>
>               
>
>             regards,
>
>             markus.
>
>             Intel Deutschland GmbH
>
>             Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
>
>             Tel: +49 89 99 8853-0,www.intel.de  <http://www.intel.de>  <http://www.intel.de>  <http://www.intel.de>
>
>             Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
>
>             Chairperson of the Supervisory Board: Nicole Lau
>
>             Registered Office: Munich
>
>             Commercial Register: Amtsgericht Muenchen HRB 186928
>
>               
>
>         -- 
>
>
>
>         *Zied Guermazi*
>         founder
>
>         Trande GmbH
>         Leuschnerstraße 2
>         69469 Weinheim/Germany
>
>         Mobile: +491722645127
>         mailto:zied.guermazi@trande.de <mailto:zied.guermazi@trande.de>
>
>         *Trande GmbH*
>         Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
>         Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim
>         HRB 736209 - Geschäftsführung: Zied Guermazi
>
>         *Confidentiality Note*
>         This message is intended only for the use of the named
>         recipient(s) and may contain confidential and/or privileged
>         information. If you are not the intended recipient, please
>         contact the sender and delete the message. Any unauthorized
>         use of the information contained in this message is prohibited.
>
>         Intel Deutschland GmbH
>         Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
>         Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
>         Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany
>         Doon Silva
>         Chairperson of the Supervisory Board: Nicole Lau
>         Registered Office: Munich
>         Commercial Register: Amtsgericht Muenchen HRB 186928
>
>     Intel Deutschland GmbH
>     Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
>     Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
>     Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany
>     Doon Silva
>     Chairperson of the Supervisory Board: Nicole Lau
>     Registered Office: Munich
>     Commercial Register: Amtsgericht Muenchen HRB 186928
>
> -- 
>
> *Zied Guermazi*
> founder
>
> Trande GmbH
> Leuschnerstraße 2
> 69469 Weinheim/Germany
>
> Mobile: +491722645127
> mailto:zied.guermazi@trande.de <mailto:zied.guermazi@trande.de>
>
> *Trande GmbH*
> Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
> Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 
> 736209 - Geschäftsführung: Zied Guermazi
>
> *Confidentiality Note*
> This message is intended only for the use of the named recipient(s) 
> and may contain confidential and/or privileged information. If you are 
> not the intended recipient, please contact the sender and delete the 
> message. Any unauthorized use of the information contained in this 
> message is prohibited.
>
> Intel Deutschland GmbH
> Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
> Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
> Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
> Chairperson of the Supervisory Board: Nicole Lau
> Registered Office: Munich
> Commercial Register: Amtsgericht Muenchen HRB 186928
>
-- 

*Zied Guermazi*
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de <mailto:zied.guermazi@trande.de>

*Trande GmbH*
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 
- Geschäftsführung: Zied Guermazi

*Confidentiality Note*
This message is intended only for the use of the named recipient(s) and 
may contain confidential and/or privileged information. If you are not 
the intended recipient, please contact the sender and delete the 
message. Any unauthorized use of the information contained in this 
message is prohibited.


^ permalink raw reply	[flat|nested] 35+ messages in thread

* RE: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os
  2022-07-21 22:20                 ` Zied Guermazi
@ 2022-07-25 14:33                   ` Metzger, Markus T
  0 siblings, 0 replies; 35+ messages in thread
From: Metzger, Markus T @ 2022-07-25 14:33 UTC (permalink / raw)
  To: Zied Guermazi; +Cc: gdb-patches

Hello Zied,

> linux_enable_pt and linux_enable_etm are taking a struct
> perf_event_attr attr as input and are filling struct perf_event_buffer
> aux_buffer , struct perf_event_mmap_page *header and int file

I'd leave perf_event_open() to the caller.  So the file descriptor
would be an in parameter and the attr would not be needed.  The header
is just a single mmap()ed data page.  I'd leave that to the caller,
too, in case we need to trace both AUX and DATA in the future.  So the
header becomes another in parameter, like the file descriptor.  And the
helper would mmap() the AUX buffer and fill in the perf_event_buffer
fields.

Would that work?

regards,
markus.

From: Zied Guermazi <zied.guermazi@trande.de>
Sent: Freitag, 22. Juli 2022 00:21
To: Metzger, Markus T <markus.t.metzger@intel.com>
Cc: gdb-patches@sourceware.org
Subject: Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os


Hello Markus,

linux_enable_pt and linux_enable_etm are taking a struct perf_event_attr attr as input and are filling struct perf_event_buffer aux_buffer , struct perf_event_mmap_page *header and int file

theses are the fields of btrace_tinfo_pt or btrace_tinfo_etm.

perf_event_open_aux will then have the signature

    perf_event_open_aux (struct perf_event_buffer *aux_buffer, struct perf_event_mmap_page **header,  int *file, const struct perf_event_attr *)

alternatively we can unify  struct btrace_tinfo_pt and struct btrace_tinfo_etm and even struct btrace_tinfo_etm to

struct btrace_tinfo
{
  /* The Linux perf_event configuration for collecting the trace.  */
  struct perf_event_attr attr;

  /* The perf event file.  */
  int file;

  /* The perf event configuration page. */
  volatile struct perf_event_mmap_page *header;

  /* The perf event buffer.  */
  struct perf_event_buffer event_buffer;
};

perf_event_open_aux will then have the signature

perf_event_open_aux (struct btrace_tinfo *, const struct perf_event_attr *)



which alternative do you prefer?



Kind Regards

Zied Guermazi


On 19.07.22 07:04, Metzger, Markus T wrote:
Hello Zied,

I was suggesting some

    perf_event_open_aux (struct perf_event_buffer *, const struct perf_event_attr *)

helper, that does all the mmaps necessary for the AUX buffer and a similar helper for the DATA buffer.
I don't think anybody is using both so the caller would do the perf_event_open () and then call the
respective helper to mmap the buffer it needs.  Would that work?

Since you need to fill in the header fields before mmaping the AUX buffer I see little benefit in
trying to share this with mmaping the DATA buffer.

Also, perf_event_open () is just a syscall wrapper and the configuration will be different for all
recording methods.  Not sure if it is worth trying to construct a helper, here.

regards,
markus.

From: Zied Guermazi <zied.guermazi@trande.de><mailto:zied.guermazi@trande.de>
Sent: Montag, 18. Juli 2022 21:06
To: Metzger, Markus T <markus.t.metzger@intel.com><mailto:markus.t.metzger@intel.com>
Cc: gdb-patches@sourceware.org<mailto:gdb-patches@sourceware.org>
Subject: Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os


hello Markus,

the proposal solves the issue for scoped_fd, we can use it and create a second scoped_fd using the returned file descriptor since scoped_fd has this constructor

explicit scoped_fd (int fd = -1) noexcept : m_fd (fd) {}.

for scoped_mmap we can not do the same since the class is missing a constructor taking void *mem and size_t length as arguments.

will it be fine to add such a constructor to the class? will it be enough to assign the memory pointer and the length to the class members?



Kind Regards

Zied Guermazi


On 20.06.22 14:52, Metzger, Markus T wrote:
Hello Zied,


/* open the scoped fd. */

static scoped_fd *
perf_event_open ( const struct perf_event_attr *event_attributes, const int pid)

The purpose of those scoped_ classes is to allocated objects on the stack to have them destroy their content in case of exceptions.

In the above example, perf_event_open() may use a scoped_fd internally but would return the actual file descriptor after releasing it.  The caller may put the returned file descriptor into its own scoped_fd object on its stack.

regards,
markus.



/* open the scoped fd. */

static scoped_fd *
perf_event_open ( const struct perf_event_attr *event_attributes, const int pid)

/*Open the scoped mmap data */

static scoped_mmap*
perf_event_mmap_data (const scoped_fd* fd, size_t* size, size_t page_size)

and

/*Open the scoped mmap aux */
static scoped_mmap*
perf_event_mmap_aux (const scoped_fd* fd, struct perf_event_mmap_page *header, size_t* size, size_t page_size)

and call them within linux_enable_bts, linux_enable_pt and linux_enable_etm

I tried two alternatives and both of them failed:

- Alternative 1: instantiate the returned scoped_mmap on the stack of the perf_event_mmap_data and perf_event_mmap_aux: the compiler refused to assign the scoped_mmap variables and they will not be valid anymore in the linux_enable_bts, linux_enable_pt and linux_enable_etm

- Alternative 2: instantiate the returned scoped_mmap on the heap by allocating them. the compiler compiles but, once we stop the tracing we ca can not start it again cause the resources are busy.

for the time being I will put this refactoring action on hold.

Any idea or support to progress further are welcome



Kind Regards

Zied Guermazi


On 13.05.22 07:31, Metzger, Markus T wrote:
Hello Zied,


+cs_etm_get_register (int cpu, const char *path)

+{

[...]

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;


[Zied] existing code in this file has an empty line before the last return everywhere. shall I stick to this convention? eliminating the empty lines before returns will bring inconsistency in the file. which coding convention do we have to apply here?




Yes, let’s stick to that and add the empty line.



so both perf_event_mmap_data perf_event_mmap_aux can be in fact reduced to one function if we give size_t size, size_t page_size, int offset as parameters.

BTS allocates the data buffer including the header, whereas PT allocates the aux buffer plus the header.  We can still make it one function, of course, but I don’t think that it will be any easier to read that way.



2- how to bring this change to the mainstream. here we have two alternatives : either I do this restructuring for etm only and then you take care of using the helper functions for bts and pt or I commit it as it is and then I issue a patch for this point only for bts PT and ETM. What do you prefer?

Ideally, we’d first restructure the existing code, then add the new use.  Are you able to test BTS and PT?

Regards,
Markus.


From: Zied Guermazi <zied.guermazi@trande.de><mailto:zied.guermazi@trande.de>
Sent: Friday, May 13, 2022 12:52 AM
To: Metzger, Markus T <markus.t.metzger@intel.com><mailto:markus.t.metzger@intel.com>; gdb-patches@sourceware.org<mailto:gdb-patches@sourceware.org>
Subject: Re: [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os


Hello Markus,

thanks for your feedback, below are the reworking comments.

/Zied
On 23.06.21 10:00, Metzger, Markus T wrote:

Hello Zied,



This patch implement the lower layer for starting ad stopping

ARM CoreSight tracing on linux targets for arm and aarch64



The patch looks good overall.  There are a few style nits and I'd ask you to

split the PAGE_SIZE changes into a separate patch as they are unrelated.



Then, there's the discussion about sharing perf_event buffer mapping.

I pointed out which parts I believe can be shared.





+/* Teardown branch tracing.  */

+

+void

+arm_linux_nat_target::teardown_btrace (struct btrace_target_info *tinfo)

+{

+  /* Ignore errors.  */

+  linux_disable_btrace (tinfo);

+}

+

+enum btrace_error

+arm_linux_nat_target::read_btrace (struct btrace_data *data,

+                           struct btrace_target_info *btinfo,

+                           enum btrace_read_type type)

+{

+  return linux_read_btrace (data, btinfo, type);

+}

+

+/* See to_btrace_conf in target.h.  */

+

+const struct btrace_config *

+arm_linux_nat_target::btrace_conf (const struct btrace_target_info *btinfo)

+{

+  return linux_btrace_conf (btinfo);

+}



There's some inconsistency in comments on functions ranging from no comment

over referring to the original target struct, to an own comment.
[Zied] I will align the comments. please notice that the same applies to x86-linux-nat.c (it was a copy-paste from it)











@@ -483,10 +487,11 @@ linux_enable_bts (ptid_t ptid, const struct

btrace_config_bts *conf)

  scoped_fd fd (syscall (SYS_perf_event_open, &bts->attr, pid, -1, -1, 0));

  if (fd.get () < 0)

    diagnose_perf_event_open_fail ();

+  long page_size = sysconf (_SC_PAGESIZE);



Please split those PAGE_SIZE changes into a separate patch.  This is unrelated

to what this patch is doing.



Note that PAGE_SIZE was unsigned whereas sysconf () returns a signed integer.

I'd expect compilers to require proper casting.
[Zied] done











+/* Enable ARM CoreSight ETM tracing.  */

+

+static struct btrace_target_info *

+linux_enable_etm (ptid_t ptid, const struct btrace_config_etm *conf)

+{

[...]

+  etm->attr.sample_type = PERF_SAMPLE_CPU;

+  etm->attr.read_format = PERF_FORMAT_ID;

+  etm->attr.sample_id_all = 1;



You enable sampling.  Wouldn't you need to mmap the data buffer, as well?
[Zied] it is not needed for current implementation. removed.











This ...



+  errno = 0;

+  scoped_fd fd (syscall (SYS_perf_event_open, &etm->attr, pid, -1, -1, 0));

+  if (fd.get () < 0)

+    diagnose_perf_event_open_fail ();

+

+  /* Allocate the configuration page.  */

+  long page_size = sysconf (_SC_PAGESIZE);

+  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE,

MAP_SHARED,

+             fd.get (), 0);

+  if (data.get () == MAP_FAILED)

+    error (_("Failed to map trace user page: %s."), safe_strerror (errno));

+

+  struct perf_event_mmap_page *header = (struct perf_event_mmap_page *)

+    data.get ();

+

+  header->aux_offset = header->data_offset + header->data_size;

+  /* Convert the requested size in bytes to pages (rounding up).  */

+  pages = ((size_t) conf->size / page_size

+     + ((conf->size % page_size) == 0 ? 0 : 1));

+  /* We need at least one page.  */

+  if (pages == 0)

+    pages = 1;

+

+  /* The buffer size can be requested in powers of two pages.  Adjust PAGES

+     to the next power of two.  */

+  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)

+    if ((pages & ((size_t) 1 << pg)) != 0)

+      pages += ((size_t) 1 << pg);

+

+  /* We try to allocate the requested size.

+     If that fails, try to get as much as we can.  */

+  scoped_mmap aux;

+  for (; pages > 0; pages >>= 1)

+    {

+      size_t length;

+      __u64 data_size;

+      data_size = (__u64) pages * page_size;

+

+      /* Don't ask for more than we can represent in the configuration.  */

+      if ((__u64) UINT_MAX < data_size)

+  continue;

+

+      length = (size_t) data_size;

+

+      /* Check for overflows.  */

+      if ((__u64) length != data_size)

+  continue;

+

+      header->aux_size = data_size;

+

+      errno = 0;

+      aux.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (),

+          header->aux_offset);

+      if (aux.get () != MAP_FAILED)

+  break;

+    }

+  if (pages == 0)

+    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));

+

+  etm->etm.size = aux.size ();

+  etm->etm.mem = (const uint8_t *) aux.release ();

+  etm->etm.data_head = &header->aux_head;

+  etm->etm.last_head = header->aux_tail;

+  etm->header = (struct perf_event_mmap_page *) data.release ();

+  gdb_assert (etm->header == header);



... can be shared with btrace_enable_pt () by introducing some



perf_event_open_aux (struct perf_event_buffer *, const struct perf_event_attr *)



helper.



And if you indeed need to mmap the data buffer, as well, we can share that with

btrace_enable_bts (), although we'd need some more restructuring to leave

perf_event_open to the caller and just allocate the data and aux buffers using

two helpers - they would again look very similar but need to touch a different

set of fields in the header, so I'd keep those separate.



btrace_enable_foo () would then become

{

  perf_event_open ()

  perf_event_mmap_data ()

  perf_event_mmap_aux ()  /* not for bts */

}



[Zied] I like the idea, there are two aspects that we need to consider to bring it to the mainstream code:

- 1: interface and scope definition

static scoped_fd perf_event_open ( const struct perf_event_attr *event_attributes,  const int pid )

{

  errno = 0;
  scoped_fd fd (syscall (SYS_perf_event_open, event_attributes, pid, -1, -1, 0));
  if (fd.get () < 0)
    diagnose_perf_event_open_fail ();

  return fd;

}

this function will only open the file descriptor and return it

static scoped_mmap data perf_event_mmap_data (const scoped_fd fd, size_t size, size_t page_size, int offset)

{

 //alternative 1 just create it and return it

  /* Allocate the configuration page. */
  scoped_mmap data (nullptr, page_size, PROT_READ | PROT_WRITE, MAP_SHARED,
            fd.get (), 0);
  if (data.get () == MAP_FAILED)
    error (_("Failed to map trace user page: %s."), safe_strerror (errno));

  return data;

 //alternative 2, create it it and make sure that we resize it to the highest possible power of 2 supported by the system

/* Convert the requested size in bytes to pages (rounding up).  */
  pages = size / page_size
       + (size % page_size) == 0 ? 0 : 1));
  /* We need at least one page.  */
  if (pages == 0)
    pages = 1;

  /* The buffer size can be requested in powers of two pages.  Adjust PAGES
     to the next power of two.  */
  for (pg = 0; pages != ((size_t) 1 << pg); ++pg)
    if ((pages & ((size_t) 1 << pg)) != 0)
      pages += ((size_t) 1 << pg);

  /* We try to allocate the requested size.
     If that fails, try to get as much as we can.  */
  scoped_mmap data;
  for (; pages > 0; pages >>= 1)
    {
      size_t length;
      __u64 data_size;

      data_size = (__u64) pages * page_size;

      /* Don't ask for more than we can represent in the configuration.  */
      if ((__u64) UINT_MAX < data_size)
    continue;

      size = (size_t) data_size;
      length = size + page_size;

      /* Check for overflows.  */
      if ((__u64) length != data_size + page_size)
    continue;

      errno = 0;
      /* The number of pages we request needs to be a power of two.  */
      data.reset (nullptr, length, PROT_READ, MAP_SHARED, fd.get (), offset);
      if (data.get () != MAP_FAILED)
    break;
    }

  if (pages == 0)
    error (_("Failed to map trace buffer: %s."), safe_strerror (errno));

}

static scoped_mmap perf_event_mmap_aux (const scoped_fd fd, size_t size, size_t page_size, int offset)

{

//idem, the function is similar to previous one it is only the offset in data.reset call that changes

}

so both perf_event_mmap_data perf_event_mmap_aux can be in fact reduced to one function if we give size_t size, size_t page_size, int offset as parameters.

2- how to bring this change to the mainstream. here we have two alternatives : either I do this restructuring for etm only and then you take care of using the helper functions for bts and pt or I commit it as it is and then I issue a patch for this point only for bts PT and ETM. What do you prefer?





+  length = fread (buffer, 1, length, file.get ());

+  buffer[length]='\0';



Spaces around =.
[Zied] done.









+  while ((--length) != 0)

+    {

+      if ((buffer[length] == ',') || (buffer[length] == '-'))

+  {

+    length++;

+    break;

+  }

+    }

+

+  int cpu_count;

+  int found = sscanf (&buffer[length], "%d", &cpu_count);

+  if (found < 1)

+    error (_("Failed to get cpu count in %s: %s."),

+       buffer, safe_strerror (errno));

+

+  cpu_count ++;

+  return (cpu_count);



No need for ().
[Zied] done.











+  char filename[PATH_MAX];

+  snprintf (filename, PATH_MAX,



sizeof (filename)
[Zied] done.









+  char filename[PATH_MAX];

+

+  /* Get coresight register from sysfs.  */

+  snprintf (filename, PATH_MAX,



sizeof (filename)
[Zied] done









+      "/sys/bus/event_source/devices/cs_etm/cpu%d/%s", cpu, path);

+  errno = 0;

+  gdb_file_up file = gdb_fopen_cloexec (filename, "r");

+  if (file.get () == nullptr)

+    error (_("Failed to open %s: %s."), filename, safe_strerror (errno));

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;

+}



Empty line before return?  I'd also remove the empty line between the

declaration of val and the call to fscanf ().



There are several very similar functions in this patch and each is structured

differently:



+perf_event_etm_event_type ()

+{

[...]

+

+  int type, found = fscanf (file.get (), "%d", &type);

+  if (found != 1)

+    error (_("Failed to read the ETM event type from %s."), filename);

+

+  return type;



+get_cpu_count (void)

+{

[...]

+

+  int cpu_count;

+  int found = sscanf (&buffer[length], "%d", &cpu_count);

+  if (found < 1)

+    error (_("Failed to get cpu count in %s: %s."),

+             buffer, safe_strerror (errno));

+

+  cpu_count ++;

+  return (cpu_count);



+perf_event_etm_event_sink (const struct btrace_config_etm *conf)

+{

[...]

+

+  unsigned int sink;

+  int  found = fscanf (file.get (), "0x%x", &sink);

+  if (found != 1)

+    error (_("Failed to read the ETM sink from %s."), filename);

+

+  return sink;



+cs_etm_get_register (int cpu, const char *path)

+{

[...]

+

+  uint32_t val = 0;

+

+  int  found = fscanf (file.get (), "0x%x", &val);

+  if (found != 1)

+    error (_("Failed to read coresight register from %s."), filename);

+  return val;


[Zied] existing code in this file has an empty line before the last return everywhere. shall I stick to this convention? eliminating the empty lines before returns will bring inconsistency in the file. which coding convention do we have to apply here?







+

+#define CORESIGHT_ETM_PMU_SEED  0x10

+

+/* Calculate trace_id for this cpu

+   to be kept aligned with coresight-pmu.h.  */

+

+static inline int

+coresight_get_trace_id (int cpu)

+{

+  return (CORESIGHT_ETM_PMU_SEED + (cpu * 2));



In patch 3, you wrote



+  /* Trace id for this thread.

+     On a linux system, trace_id is assigned per cpu. The kernel copies

+     the traces of each thread in a dedicated ring buffer. By this,

+     traces belonging to different threads are de-multiplexed.

+     On an RTOS system, especially when routing the traces outside of the SoC,

+     the OS has no other mean for de-multiplexing the traces than

+     the trace_id. The hardware (ETM IP) reserves 7 bits for the trace_id.

+     On linux system trace id is not needed, set it to 0xFF to ignore it

+     during parsing.  */

+  uint8_t trace_id;



Should this function return uint8_t and check that the ID is 7 bit max?
[Zied] done, reserved and not allowed values are also generating a warning now.











+static void

+fill_etm_trace_params (struct cs_etm_trace_params *etm_trace_params, int

cpu)

+{

+  if (cs_etm_is_etmv4 (cpu) == true)



No need for explicit checks on bool.
[Zied] done.











+static void

+linux_fill_btrace_etm_config (struct btrace_target_info *tinfo,

+                        struct btrace_data_etm_config *conf)

+{

+

+  cs_etm_trace_params etm_trace_params;



Please declare at initialization time.
[Zied] pushed forwards before the for loop









+  conf->cpu_count = get_cpu_count ();

+  conf->etm_trace_params = new std::vector<cs_etm_trace_params>;

+  for (int i = 0; i < conf->cpu_count; i++)

+    {

+      fill_etm_trace_params (&etm_trace_params,i);

+      conf->etm_trace_params->push_back (etm_trace_params);

+    }



We need to avoid leaking the vector when fill_etm_trace_params () throws.







+static enum btrace_error

+linux_read_etm (struct btrace_data_etm *btrace,

+         struct btrace_target_info *tinfo,

+         enum btrace_read_type type)

+{

+  struct perf_event_buffer *etm;

+  etm = &tinfo->variant.etm.etm;



Please combine.  No forward declarations anymore.  The old code was written

when GDB was still C.
[Zied] done.











regards,

markus.

Intel Deutschland GmbH

Registered Address: Am Campeon 10, 85579 Neubiberg, Germany

Tel: +49 89 99 8853-0, www.intel.de<http://www.intel.de> <http://www.intel.de><http://www.intel.de>

Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva

Chairperson of the Supervisory Board: Nicole Lau

Registered Office: Munich

Commercial Register: Amtsgericht Muenchen HRB 186928


--





Zied Guermazi
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de

Trande GmbH
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 - Geschäftsführung: Zied Guermazi

Confidentiality Note
This message is intended only for the use of the named recipient(s) and may contain confidential and/or privileged information. If you are not the intended recipient, please contact the sender and delete the message. Any unauthorized use of the information contained in this message is prohibited.



Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de<http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928




Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de<http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
--



Zied Guermazi
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de

Trande GmbH
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 - Geschäftsführung: Zied Guermazi

Confidentiality Note
This message is intended only for the use of the named recipient(s) and may contain confidential and/or privileged information. If you are not the intended recipient, please contact the sender and delete the message. Any unauthorized use of the information contained in this message is prohibited.



Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de<http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928
--


Zied Guermazi
founder

Trande GmbH
Leuschnerstraße 2
69469 Weinheim/Germany

Mobile: +491722645127
mailto:zied.guermazi@trande.de

Trande GmbH
Leuschnerstraße 2, D-69469 Weinheim; Telefon: +491722645127
Sitz der Gesellschaft: Weinheim- Registergericht: AG Mannheim HRB 736209 - Geschäftsführung: Zied Guermazi

Confidentiality Note
This message is intended only for the use of the named recipient(s) and may contain confidential and/or privileged information. If you are not the intended recipient, please contact the sender and delete the message. Any unauthorized use of the information contained in this message is prohibited.


Intel Deutschland GmbH
Registered Address: Am Campeon 10, 85579 Neubiberg, Germany
Tel: +49 89 99 8853-0, www.intel.de <http://www.intel.de>
Managing Directors: Christin Eisenschmid, Sharon Heck, Tiffany Doon Silva  
Chairperson of the Supervisory Board: Nicole Lau
Registered Office: Munich
Commercial Register: Amtsgericht Muenchen HRB 186928

^ permalink raw reply	[flat|nested] 35+ messages in thread

end of thread, other threads:[~2022-07-25 14:34 UTC | newest]

Thread overview: 35+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2021-05-31 21:33 [PATCH v6 0/7] extend branch tracing to use ARM CoreSight traces Zied Guermazi
2021-05-31 21:33 ` [PATCH v6 1/7] configure gdb build system for supporting btrace on arm processors Zied Guermazi
2021-06-30 12:17   ` Luis Machado
2021-05-31 21:33 ` [PATCH v6 2/7] add btrace coresight related commands Zied Guermazi
2021-06-01 12:07   ` Eli Zaretskii
2021-06-01 15:47     ` Zied Guermazi
2021-06-30 12:26   ` Luis Machado
2021-05-31 21:33 ` [PATCH v6 3/7] start/stop btrace with coresight etm and parse etm buffer. nat independant Zied Guermazi
2021-06-22 14:59   ` Metzger, Markus T
2022-04-07 16:33     ` Zied Guermazi
2022-04-13  7:00       ` Metzger, Markus T
2022-05-10 12:58         ` Zied Guermazi
2022-05-10 13:21           ` Metzger, Markus T
2021-06-30 12:54   ` Luis Machado
2021-05-31 21:33 ` [PATCH v6 4/7] start/stop btrace with coresight etm and collect etm buffer on linux os Zied Guermazi
2021-06-23  8:00   ` Metzger, Markus T
2022-05-12 22:52     ` Zied Guermazi
2022-05-13  5:31       ` Metzger, Markus T
2022-06-12 21:02         ` Zied Guermazi
2022-06-20 12:52           ` Metzger, Markus T
2022-07-18 19:06             ` Zied Guermazi
2022-07-19  5:04               ` Metzger, Markus T
2022-07-21 22:20                 ` Zied Guermazi
2022-07-25 14:33                   ` Metzger, Markus T
2021-06-30 13:24   ` Luis Machado
2021-05-31 21:33 ` [PATCH v6 5/7] fix issue: gdb hangs in the command following a commad returning with TARGET_WAITKIND_NO_HISTORY Zied Guermazi
2021-06-23  8:08   ` Metzger, Markus T
2021-05-31 21:33 ` [PATCH v6 6/7] add support for coresight btrace via remote protocol Zied Guermazi
2021-06-01 12:08   ` Eli Zaretskii
2021-06-23 10:59   ` Metzger, Markus T
2021-05-31 21:33 ` [PATCH v6 7/7] adapt btrace testcases for arm target Zied Guermazi
2021-06-22 21:28   ` Lancelot SIX
2021-06-23 14:16   ` Metzger, Markus T
2022-05-13 11:08   ` Richard Earnshaw
2022-05-17  9:44     ` Zied Guermazi

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).