From 393fd4c3768e4babdc112fded0bde4aedd40925e Mon Sep 17 00:00:00 2001 From: Yao Qi Date: Wed, 10 Apr 2013 09:42:57 +0000 Subject: [PATCH] gdb/ 2013-04-10 Hui Zhu Yao Qi * configure.ac: Check libbabeltrace is installed. * config.in: Regenerate. * configure: Regenerate. * Makefile.in (LIBBABELTRACE): New. (CLIBS): Add LIBBABELTRACE. * ctf.c: Include "exec.h". (CTF_EVENT_ID_STATUS, CTF_EVENT_ID_TSV_DEF): New macros. (CTF_EVENT_ID_TP_DEF, ctf_save_write_int32): New macros. (ctf_save_metadata_header): Define new type aliases in metadata. (ctf_write_header): Define event type "tsv_def" and "tp_def" in metadata. Start a new faked packet for trace status. (ctf_write_status): Write trace status to CTF. (ctf_write_uploaded_tsv): Write TSV to CTF. (ctf_write_uploaded_tp): Write tracepoint definition to CTF. (ctf_write_definition_end): End the faked packet. (ctx, ctf_iter, trace_dirname): New. (start_pos): New variable. (ctf_destroy, ctf_open_dir, ctf_open): New. (SET_INT32_FIELD, SET_ARRAY_FIELD, SET_STRING_FIELD): New macros. (ctf_read_tsv, ctf_read_tp, ctf_close, ctf_files_info): New. (ctf_fetch_registers, ctf_xfer_partial): New. (ctf_get_trace_state_variable_value): New. (ctf_get_tpnum_from_frame_event): New. (ctf_get_traceframe_address): New. (ctf_trace_find, ctf_has_stack): New. (ctf_has_registers, ctf_traceframe_info, init_ctf_ops): New. (ctf_get_trace_status, ctf_read_status): New. (_initialize_ctf): New. * tracepoint.c (get_tracepoint_number): New (get_uploaded_tsv): Remove 'static'. (struct traceframe_info, trace_regblock_size): Move it to ... * tracepoint.h: ... here. (get_tracepoint_number): Declare it. (get_uploaded_tsv): Declare it. * NEWS: Mention new configure option. gdb/doc/ 2013-04-10 Yao Qi * gdb.texinfo (Trace Files): Add "target ctf". gdb/testsuite/ 2013-04-10 Yao Qi * gdb.trace/actions.exp: Save trace data to CTF. Change to ctf target if GDB supports, read CTF data in ctf target, and check the actions of tracepoints. * gdb.trace/while-stepping.exp: Likewise. * gdb.trace/report.exp: Test GDB saves trace data to CTF format and read CTF trace file if GDB supports. * gdb.trace/tstatus.exp: Save trace data to CTF. If ctf target is supported, change to ctf target, read trace data and check output of command "tstatus". * gdb.trace/tsv.exp: Save trace frame to CTF. If GDB supports, read CTF data by target ctf and call check_tsv. --- gdb/ChangeLog | 43 + gdb/Makefile.in | 6 +- gdb/NEWS | 3 + gdb/config.in | 3 + gdb/configure | 517 +++++++++ gdb/configure.ac | 41 + gdb/ctf.c | 1196 +++++++++++++++++++- gdb/doc/ChangeLog | 4 + gdb/doc/gdb.texinfo | 32 +- gdb/testsuite/ChangeLog | 14 + gdb/testsuite/gdb.trace/actions.exp | 24 + gdb/testsuite/gdb.trace/report.exp | 14 + gdb/testsuite/gdb.trace/tstatus.exp | 14 + gdb/testsuite/gdb.trace/tsv.exp | 13 + gdb/testsuite/gdb.trace/while-stepping.exp | 24 + gdb/tracepoint.c | 18 +- gdb/tracepoint.h | 15 + 17 files changed, 1953 insertions(+), 28 deletions(-) diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 75a0306a92..d37981db0a 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,46 @@ +2013-04-10 Hui Zhu + Yao Qi + + * configure.ac: Check libbabeltrace is installed. + * config.in: Regenerate. + * configure: Regenerate. + * Makefile.in (LIBBABELTRACE): New. + (CLIBS): Add LIBBABELTRACE. + * ctf.c: Include "exec.h". + (CTF_EVENT_ID_STATUS, CTF_EVENT_ID_TSV_DEF): New macros. + (CTF_EVENT_ID_TP_DEF, ctf_save_write_int32): New macros. + (ctf_save_metadata_header): Define new type aliases in + metadata. + (ctf_write_header): Define event type "tsv_def" and "tp_def" + in metadata. Start a new faked packet for trace status. + (ctf_write_status): Write trace status to CTF. + (ctf_write_uploaded_tsv): Write TSV to CTF. + (ctf_write_uploaded_tp): Write tracepoint definition to CTF. + (ctf_write_definition_end): End the faked packet. + + (ctx, ctf_iter, trace_dirname): New. + (start_pos): New variable. + (ctf_destroy, ctf_open_dir, ctf_open): New. + (SET_INT32_FIELD, SET_ARRAY_FIELD, SET_STRING_FIELD): New + macros. + (ctf_read_tsv, ctf_read_tp, ctf_close, ctf_files_info): New. + (ctf_fetch_registers, ctf_xfer_partial): New. + (ctf_get_trace_state_variable_value): New. + (ctf_get_tpnum_from_frame_event): New. + (ctf_get_traceframe_address): New. + (ctf_trace_find, ctf_has_stack): New. + (ctf_has_registers, ctf_traceframe_info, init_ctf_ops): New. + (ctf_get_trace_status, ctf_read_status): New. + (_initialize_ctf): New. + * tracepoint.c (get_tracepoint_number): New + (get_uploaded_tsv): Remove 'static'. + (struct traceframe_info, trace_regblock_size): Move it to ... + * tracepoint.h: ... here. + (get_tracepoint_number): Declare it. + (get_uploaded_tsv): Declare it. + + * NEWS: Mention new configure option. + 2013-04-10 Pedro Alves Hui Zhu diff --git a/gdb/Makefile.in b/gdb/Makefile.in index 498d42a917..0814bcb13f 100644 --- a/gdb/Makefile.in +++ b/gdb/Makefile.in @@ -154,6 +154,10 @@ LIBEXPAT = @LIBEXPAT@ # Where is lzma? This will be empty if lzma was not available. LIBLZMA = @LIBLZMA@ +# Where is libbabeltrace? This will be empty if lbabeltrace was not +# available. +LIBBABELTRACE = @LIBBABELTRACE@ + WARN_CFLAGS = @WARN_CFLAGS@ WERROR_CFLAGS = @WERROR_CFLAGS@ GDB_WARN_CFLAGS = $(WARN_CFLAGS) @@ -475,7 +479,7 @@ INTERNAL_LDFLAGS = $(CFLAGS) $(GLOBAL_CFLAGS) $(MH_LDFLAGS) $(LDFLAGS) $(CONFIG_ # LIBIBERTY appears twice on purpose. CLIBS = $(SIM) $(READLINE) $(OPCODES) $(BFD) $(INTL) $(LIBIBERTY) $(LIBDECNUMBER) \ $(XM_CLIBS) $(NAT_CLIBS) $(GDBTKLIBS) @LIBS@ @PYTHON_LIBS@ \ - $(LIBEXPAT) $(LIBLZMA) \ + $(LIBEXPAT) $(LIBLZMA) $(LIBBABELTRACE) \ $(LIBIBERTY) $(WIN32LIBS) $(LIBGNU) CDEPS = $(XM_CDEPS) $(NAT_CDEPS) $(SIM) $(BFD) $(READLINE_DEPS) \ $(OPCODES) $(INTL_DEPS) $(LIBIBERTY) $(CONFIG_DEPS) $(LIBGNU) diff --git a/gdb/NEWS b/gdb/NEWS index 6f202e2e8c..86716e0f86 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -135,6 +135,9 @@ Tilera TILE-Gx GNU/Linux tilegx*-*-linux Release versions, on the other hand, are built without -lmcheck by default. The --enable-libmcheck/--disable-libmcheck configure options allow the user to override that default. +--with-babeltrace/--with-babeltrace-include/--with-babeltrace-lib + This configure option allows the user to build GDB with + libbabeltrace using which GDB can read Common Trace Format data. * New commands (for set/show, see "New options" below) diff --git a/gdb/config.in b/gdb/config.in index 9e21325b1e..c4e8eaa6cd 100644 --- a/gdb/config.in +++ b/gdb/config.in @@ -180,6 +180,9 @@ /* Define if your file defines LC_MESSAGES. */ #undef HAVE_LC_MESSAGES +/* Define if libbabeltrace is available */ +#undef HAVE_LIBBABELTRACE + /* Define to 1 if you have the `dl' library (-ldl). */ #undef HAVE_LIBDL diff --git a/gdb/configure b/gdb/configure index 0dd67f0747..6588c72882 100755 --- a/gdb/configure +++ b/gdb/configure @@ -592,6 +592,9 @@ enable_option_checking=no ac_subst_vars='LTLIBOBJS LIBOBJS GDB_NM_FILE +LTLIBBABELTRACE +LIBBABELTRACE +HAVE_LIBBABELTRACE frags target_subdir CONFIG_UNINSTALL @@ -819,6 +822,8 @@ with_x enable_sim enable_multi_ice enable_gdbserver +with_babeltrace +with_libbabeltrace_prefix ' ac_precious_vars='build_alias host_alias @@ -1532,6 +1537,9 @@ Optional Packages: --with-tcl directory containing tcl configuration (tclConfig.sh) --with-tk directory containing tk configuration (tkConfig.sh) --with-x use the X Window System + --with-babeltrace include babeltrace support (auto/yes/no) + --with-libbabeltrace-prefix[=DIR] search for libbabeltrace in DIR/include and DIR/lib + --without-libbabeltrace-prefix don't search for libbabeltrace in includedir and libdir Some influential environment variables: CC C compiler command @@ -14093,6 +14101,515 @@ if test "$enable_gdbserver" = "yes" -a "$gdbserver_build_enabled" != "yes"; then as_fn_error "Automatic gdbserver build is not supported for this configuration" "$LINENO" 5 fi +# Check for babeltrace and babeltrace-ctf + +# Check whether --with-babeltrace was given. +if test "${with_babeltrace+set}" = set; then : + withval=$with_babeltrace; +else + with_babeltrace=auto +fi + +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether to use babeltrace" >&5 +$as_echo_n "checking whether to use babeltrace... " >&6; } +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $with_babeltrace" >&5 +$as_echo "$with_babeltrace" >&6; } + +if test "x$with_babeltrace" = "xno"; then + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: babletrace support disabled; GDB is unable to read CTF data." >&5 +$as_echo "$as_me: WARNING: babletrace support disabled; GDB is unable to read CTF data." >&2;} +else + # Append -Werror to CFLAGS so that configure can catch the warning + # "assignment from incompatible pointer type", which is related to + # the babeltrace change from 1.0.3 to 1.1.0. Babeltrace 1.1.0 works + # in GDB, while babeltrace 1.0.3 is broken. + # AC_LIB_HAVE_LINKFLAGS may modify CPPFLAGS in it, so it should be + # safe to save and restore CFLAGS here. + saved_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -Werror" + + + + + + + + + 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-libbabeltrace-prefix was given. +if test "${with_libbabeltrace_prefix+set}" = set; then : + withval=$with_libbabeltrace_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 + + LIBBABELTRACE= + LTLIBBABELTRACE= + INCBABELTRACE= + rpathdirs= + ltrpathdirs= + names_already_handled= + names_next_round='babeltrace babeltrace-ctf' + 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" || LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }$value" + eval value=\"\$LTLIB$uppername\" + test -z "$value" || LTLIBBABELTRACE="${LTLIBBABELTRACE}${LTLIBBABELTRACE:+ }$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"; 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 + else + 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 $LTLIBBABELTRACE; 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"; 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 + else + 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 + LTLIBBABELTRACE="${LTLIBBABELTRACE}${LTLIBBABELTRACE:+ }-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 + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }$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 + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }$found_so" + else + if test -n "$hardcode_libdir_flag_spec" && test "$hardcode_minus_L" = no; then + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }$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 $LIBBABELTRACE; 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 + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }-L$found_dir" + fi + if test "$hardcode_minus_L" != no; then + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }$found_so" + else + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }-l$name" + fi + fi + fi + fi + else + if test "X$found_a" != "X"; then + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }$found_a" + else + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }-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 $INCBABELTRACE; 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 + INCBABELTRACE="${INCBABELTRACE}${INCBABELTRACE:+ }-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 $LIBBABELTRACE; 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 + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }-L$additional_libdir" + fi + fi + haveit= + for x in $LDFLAGS $LTLIBBABELTRACE; 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 + LTLIBBABELTRACE="${LTLIBBABELTRACE}${LTLIBBABELTRACE:+ }-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$,,'` + ;; + *) + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }$dep" + LTLIBBABELTRACE="${LTLIBBABELTRACE}${LTLIBBABELTRACE:+ }$dep" + ;; + esac + done + fi + else + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }-l$name" + LTLIBBABELTRACE="${LTLIBBABELTRACE}${LTLIBBABELTRACE:+ }-l$name" + 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" + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }$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" + LIBBABELTRACE="${LIBBABELTRACE}${LIBBABELTRACE:+ }$flag" + done + fi + fi + if test "X$ltrpathdirs" != "X"; then + for found_dir in $ltrpathdirs; do + LTLIBBABELTRACE="${LTLIBBABELTRACE}${LTLIBBABELTRACE:+ }-R$found_dir" + done + fi + + + ac_save_CPPFLAGS="$CPPFLAGS" + + for element in $INCBABELTRACE; 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 libbabeltrace" >&5 +$as_echo_n "checking for libbabeltrace... " >&6; } +if test "${ac_cv_libbabeltrace+set}" = set; then : + $as_echo_n "(cached) " >&6 +else + + ac_save_LIBS="$LIBS" + LIBS="$LIBS $LIBBABELTRACE" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include + #include + #include +int +main () +{ +struct bt_iter_pos *pos = bt_iter_get_pos (bt_ctf_get_iter (NULL)); + struct bt_ctf_event *event = NULL; + const struct bt_definition *scope; + + scope = bt_ctf_get_top_level_scope (event, + BT_STREAM_EVENT_HEADER); + bt_ctf_get_uint64 (bt_ctf_get_field (event, scope, "id")); + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_libbabeltrace=yes +else + ac_cv_libbabeltrace=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_libbabeltrace" >&5 +$as_echo "$ac_cv_libbabeltrace" >&6; } + if test "$ac_cv_libbabeltrace" = yes; then + HAVE_LIBBABELTRACE=yes + +$as_echo "#define HAVE_LIBBABELTRACE 1" >>confdefs.h + + { $as_echo "$as_me:${as_lineno-$LINENO}: checking how to link with libbabeltrace" >&5 +$as_echo_n "checking how to link with libbabeltrace... " >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $LIBBABELTRACE" >&5 +$as_echo "$LIBBABELTRACE" >&6; } + else + HAVE_LIBBABELTRACE=no + CPPFLAGS="$ac_save_CPPFLAGS" + LIBBABELTRACE= + LTLIBBABELTRACE= + fi + + + + + + + CFLAGS=$saved_CFLAGS + + if test "$HAVE_LIBBABELTRACE" != yes; then + if test "$with_babeltrace" = yes; then + as_fn_error "babeltrace is missing or unusable" "$LINENO" 5 + else + { $as_echo "$as_me:${as_lineno-$LINENO}: WARNING: babeltrace is missing or unusable; GDB is unable to read CTF data." >&5 +$as_echo "$as_me: WARNING: babeltrace is missing or unusable; GDB is unable to read CTF data." >&2;} + fi + fi +fi + # If nativefile (NAT_FILE) is not set in config/*/*.m[ht] files, we link # to an empty version. diff --git a/gdb/configure.ac b/gdb/configure.ac index c17f5872ad..149ac37c83 100644 --- a/gdb/configure.ac +++ b/gdb/configure.ac @@ -2321,6 +2321,47 @@ if test "$enable_gdbserver" = "yes" -a "$gdbserver_build_enabled" != "yes"; then AC_MSG_ERROR(Automatic gdbserver build is not supported for this configuration) fi +# Check for babeltrace and babeltrace-ctf +AC_ARG_WITH(babeltrace, + AC_HELP_STRING([--with-babeltrace], [include babeltrace support (auto/yes/no)]), + [], [with_babeltrace=auto]) +AC_MSG_CHECKING([whether to use babeltrace]) +AC_MSG_RESULT([$with_babeltrace]) + +if test "x$with_babeltrace" = "xno"; then + AC_MSG_WARN([babletrace support disabled; GDB is unable to read CTF data.]) +else + # Append -Werror to CFLAGS so that configure can catch the warning + # "assignment from incompatible pointer type", which is related to + # the babeltrace change from 1.0.3 to 1.1.0. Babeltrace 1.1.0 works + # in GDB, while babeltrace 1.0.3 is broken. + # AC_LIB_HAVE_LINKFLAGS may modify CPPFLAGS in it, so it should be + # safe to save and restore CFLAGS here. + saved_CFLAGS=$CFLAGS + CFLAGS="$CFLAGS -Werror" + AC_LIB_HAVE_LINKFLAGS([babeltrace], [babeltrace-ctf], + [#include + #include + #include ], + [struct bt_iter_pos *pos = bt_iter_get_pos (bt_ctf_get_iter (NULL)); + struct bt_ctf_event *event = NULL; + const struct bt_definition *scope; + + scope = bt_ctf_get_top_level_scope (event, + BT_STREAM_EVENT_HEADER); + bt_ctf_get_uint64 (bt_ctf_get_field (event, scope, "id")); + ]) + CFLAGS=$saved_CFLAGS + + if test "$HAVE_LIBBABELTRACE" != yes; then + if test "$with_babeltrace" = yes; then + AC_MSG_ERROR([babeltrace is missing or unusable]) + else + AC_MSG_WARN([babeltrace is missing or unusable; GDB is unable to read CTF data.]) + fi + fi +fi + # If nativefile (NAT_FILE) is not set in config/*/*.m[ht] files, we link # to an empty version. diff --git a/gdb/ctf.c b/gdb/ctf.c index de54051e90..0985784ad4 100644 --- a/gdb/ctf.c +++ b/gdb/ctf.c @@ -24,6 +24,7 @@ #include "tracepoint.h" #include "regcache.h" #include "gdb_stat.h" +#include "exec.h" #include @@ -34,10 +35,15 @@ 1. The length (in bytes) of register cache. Event "register" will be defined in metadata, which includes the length. - 2. Trace status. Not implemented yet in CTF writer. + 2. Trace status. Event "status" is defined in metadata, which + includes all aspects of trace status. - 3. Uploaded trace variables and tracepoints. Not implemented yet - in CTF writer. + 3. Uploaded trace variables. Event "tsv_def" is defined in + metadata, which is about all aspects of a uploaded trace variable. + Uploaded tracepoints. Event "tp_def" is defined in meta, which + is about all aspects of an uploaded tracepoint. Note that the + "sequence" (a CTF type, which is a dynamically-sized array.) is + used for "actions" "step_actions" and "cmd_strings". 4. Trace frames. Each trace frame is composed by several blocks of different types ('R', 'M', 'V'). One trace frame is saved in @@ -65,6 +71,9 @@ #define CTF_EVENT_ID_TSV 1 #define CTF_EVENT_ID_MEMORY 2 #define CTF_EVENT_ID_FRAME 3 +#define CTF_EVENT_ID_STATUS 4 +#define CTF_EVENT_ID_TSV_DEF 5 +#define CTF_EVENT_ID_TP_DEF 6 /* The state kept while writing the CTF datastream file. */ @@ -119,6 +128,12 @@ ctf_save_write (struct trace_write_handler *handler, #define ctf_save_write_uint32(HANDLER, U32) \ ctf_save_write (HANDLER, (gdb_byte *) &U32, 4) +/* Write a signed 32-bit integer to datastream file represented by + HANDLER. */ + +#define ctf_save_write_int32(HANDLER, INT32) \ + ctf_save_write ((HANDLER), (gdb_byte *) &(INT32), 4) + /* Set datastream file position. Update HANDLER->content_size if WHENCE is SEEK_CUR. */ @@ -217,6 +232,15 @@ ctf_save_metadata_header (struct trace_write_handler *handler) "typealias integer { size = 64; align = 64;" "signed = false; base = hex;}" " := uint64_t;\n"); + ctf_save_write_metadata (handler, + "typealias integer { size = 32; align = 32;" + "signed = true; } := int32_t;\n"); + ctf_save_write_metadata (handler, + "typealias integer { size = 64; align = 64;" + "signed = true; } := int64_t;\n"); + ctf_save_write_metadata (handler, + "typealias string { encoding = ascii;" + " } := chars;\n"); ctf_save_write_metadata (handler, "\n"); /* Get the byte order of the host and write CTF data in this byte @@ -365,8 +389,50 @@ ctf_write_header (struct trace_file_writer *self) "\t};\n" "};\n", CTF_EVENT_ID_FRAME); + ctf_save_write_metadata (&writer->tcs, "\n"); + ctf_save_write_metadata (&writer->tcs, + "event {\n\tname = \"tsv_def\";\n" + "\tid = %u;\n\tfields := struct { \n" + "\t\tint64_t initial_value;\n" + "\t\tint32_t number;\n" + "\t\tint32_t builtin;\n" + "\t\tchars name;\n" + "\t};\n" + "};\n", CTF_EVENT_ID_TSV_DEF); + + ctf_save_write_metadata (&writer->tcs, "\n"); + ctf_save_write_metadata (&writer->tcs, + "event {\n\tname = \"tp_def\";\n" + "\tid = %u;\n\tfields := struct { \n" + "\t\tuint64_t addr;\n" + "\t\tuint64_t traceframe_usage;\n" + "\t\tint32_t number;\n" + "\t\tint32_t enabled;\n" + "\t\tint32_t step;\n" + "\t\tint32_t pass;\n" + "\t\tint32_t hit_count;\n" + "\t\tint32_t type;\n" + "\t\tchars cond;\n" + + "\t\tuint32_t action_num;\n" + "\t\tchars actions[action_num];\n" + + "\t\tuint32_t step_action_num;\n" + "\t\tchars step_actions[step_action_num];\n" + + "\t\tchars at_string;\n" + "\t\tchars cond_string;\n" + + "\t\tuint32_t cmd_num;\n" + "\t\tchars cmd_strings[cmd_num];\n" + "\t};\n" + "};\n", CTF_EVENT_ID_TP_DEF); + gdb_assert (writer->tcs.content_size == 0); gdb_assert (writer->tcs.packet_start == 0); + + /* Create a new packet to contain this event. */ + self->ops->frame_ops->start (self, 0); } /* This is the implementation of trace_file_write_ops method @@ -397,8 +463,39 @@ static void ctf_write_status (struct trace_file_writer *self, struct trace_status *ts) { - /* It is not supported yet to write trace status into CTF trace - data. */ + struct ctf_trace_file_writer *writer + = (struct ctf_trace_file_writer *) self; + uint32_t id; + int32_t int32; + + ctf_save_write_metadata (&writer->tcs, "\n"); + ctf_save_write_metadata (&writer->tcs, + "event {\n\tname = \"status\";\n\tid = %u;\n" + "\tfields := struct { \n" + "\t\tint32_t stop_reason;\n" + "\t\tint32_t stopping_tracepoint;\n" + "\t\tint32_t traceframe_count;\n" + "\t\tint32_t traceframes_created;\n" + "\t\tint32_t buffer_free;\n" + "\t\tint32_t buffer_size;\n" + "\t\tint32_t disconnected_tracing;\n" + "\t\tint32_t circular_buffer;\n" + "\t};\n" + "};\n", + CTF_EVENT_ID_STATUS); + + id = CTF_EVENT_ID_STATUS; + /* Event Id. */ + ctf_save_align_write (&writer->tcs, (gdb_byte *) &id, 4, 4); + + ctf_save_write_int32 (&writer->tcs, ts->stop_reason); + ctf_save_write_int32 (&writer->tcs, ts->stopping_tracepoint); + ctf_save_write_int32 (&writer->tcs, ts->traceframe_count); + ctf_save_write_int32 (&writer->tcs, ts->traceframes_created); + ctf_save_write_int32 (&writer->tcs, ts->buffer_free); + ctf_save_write_int32 (&writer->tcs, ts->buffer_size); + ctf_save_write_int32 (&writer->tcs, ts->disconnected_tracing); + ctf_save_write_int32 (&writer->tcs, ts->circular_buffer); } /* This is the implementation of trace_file_write_ops method @@ -408,8 +505,31 @@ static void ctf_write_uploaded_tsv (struct trace_file_writer *self, struct uploaded_tsv *tsv) { - /* It is not supported yet to write uploaded trace variables - into CTF trace data. */ + struct ctf_trace_file_writer *writer + = (struct ctf_trace_file_writer *) self; + int32_t int32; + int64_t int64; + unsigned int len; + const gdb_byte zero = 0; + + /* Event Id. */ + int32 = CTF_EVENT_ID_TSV_DEF; + ctf_save_align_write (&writer->tcs, (gdb_byte *) &int32, 4, 4); + + /* initial_value */ + int64 = tsv->initial_value; + ctf_save_align_write (&writer->tcs, (gdb_byte *) &int64, 8, 8); + + /* number */ + ctf_save_write_int32 (&writer->tcs, tsv->number); + + /* builtin */ + ctf_save_write_int32 (&writer->tcs, tsv->builtin); + + /* name */ + if (tsv->name != NULL) + ctf_save_write (&writer->tcs, tsv->name, strlen (tsv->name)); + ctf_save_write (&writer->tcs, &zero, 1); } /* This is the implementation of trace_file_write_ops method @@ -419,8 +539,80 @@ static void ctf_write_uploaded_tp (struct trace_file_writer *self, struct uploaded_tp *tp) { - /* It is not supported yet to write uploaded tracepoints - into CTF trace data. */ + struct ctf_trace_file_writer *writer + = (struct ctf_trace_file_writer *) self; + int32_t int32; + int64_t int64; + uint32_t u32; + const gdb_byte zero = 0; + int a; + char *act; + + /* Event Id. */ + int32 = CTF_EVENT_ID_TP_DEF; + ctf_save_align_write (&writer->tcs, (gdb_byte *) &int32, 4, 4); + + /* address */ + int64 = tp->addr; + ctf_save_align_write (&writer->tcs, (gdb_byte *) &int64, 8, 8); + + /* traceframe_usage */ + int64 = tp->traceframe_usage; + ctf_save_align_write (&writer->tcs, (gdb_byte *) &int64, 8, 8); + + /* number */ + ctf_save_write_int32 (&writer->tcs, tp->number); + + /* enabled */ + ctf_save_write_int32 (&writer->tcs, tp->enabled); + + /* step */ + ctf_save_write_int32 (&writer->tcs, tp->step); + + /* pass */ + ctf_save_write_int32 (&writer->tcs, tp->pass); + + /* hit_count */ + ctf_save_write_int32 (&writer->tcs, tp->hit_count); + + /* type */ + ctf_save_write_int32 (&writer->tcs, tp->type); + + /* condition */ + if (tp->cond != NULL) + ctf_save_write (&writer->tcs, tp->cond, strlen (tp->cond)); + ctf_save_write (&writer->tcs, &zero, 1); + + /* actions */ + u32 = VEC_length (char_ptr, tp->actions); + ctf_save_align_write (&writer->tcs, (gdb_byte *) &u32, 4, 4); + for (a = 0; VEC_iterate (char_ptr, tp->actions, a, act); ++a) + ctf_save_write (&writer->tcs, act, strlen (act) + 1); + + /* step_actions */ + u32 = VEC_length (char_ptr, tp->step_actions); + ctf_save_align_write (&writer->tcs, (gdb_byte *) &u32, 4, 4); + for (a = 0; VEC_iterate (char_ptr, tp->step_actions, a, act); ++a) + ctf_save_write (&writer->tcs, act, strlen (act) + 1); + + /* at_string */ + if (tp->at_string != NULL) + ctf_save_write (&writer->tcs, tp->at_string, + strlen (tp->at_string)); + ctf_save_write (&writer->tcs, &zero, 1); + + /* cond_string */ + if (tp->cond_string != NULL) + ctf_save_write (&writer->tcs, tp->cond_string, + strlen (tp->cond_string)); + ctf_save_write (&writer->tcs, &zero, 1); + + /* cmd_strings */ + u32 = VEC_length (char_ptr, tp->cmd_strings); + ctf_save_align_write (&writer->tcs, (gdb_byte *) &u32, 4, 4); + for (a = 0; VEC_iterate (char_ptr, tp->cmd_strings, a, act); ++a) + ctf_save_write (&writer->tcs, act, strlen (act) + 1); + } /* This is the implementation of trace_file_write_ops method @@ -429,7 +621,10 @@ ctf_write_uploaded_tp (struct trace_file_writer *self, static void ctf_write_definition_end (struct trace_file_writer *self) { - /* Nothing to do for CTF. */ + struct ctf_trace_file_writer *writer + = (struct ctf_trace_file_writer *) self; + + self->ops->frame_ops->end (self); } /* The minimal file size of data stream. It is required by @@ -670,3 +865,984 @@ ctf_trace_file_writer_new (void) return (struct trace_file_writer *) writer; } + +#if HAVE_LIBBABELTRACE +/* Use libbabeltrace to read CTF data. The libbabeltrace provides + iterator to iterate over each event in CTF data and APIs to get + details of event and packet, so it is very convenient to use + libbabeltrace to access events in CTF. */ + +#include +#include +#include + +/* The struct pointer for current CTF directory. */ +static struct bt_context *ctx = NULL; +static struct bt_ctf_iter *ctf_iter = NULL; +/* The position of the first packet containing trace frame. */ +static struct bt_iter_pos *start_pos; + +/* The name of CTF directory. */ +static char *trace_dirname; + +static struct target_ops ctf_ops; + +/* Destroy ctf iterator and context. */ + +static void +ctf_destroy (void) +{ + if (ctf_iter != NULL) + { + bt_ctf_iter_destroy (ctf_iter); + ctf_iter = NULL; + } + if (ctx != NULL) + { + bt_context_put (ctx); + ctx = NULL; + } +} + +/* Open CTF trace data in DIRNAME. */ + +static void +ctf_open_dir (char *dirname) +{ + int ret; + struct bt_iter_pos begin_pos; + struct bt_iter_pos *pos; + + ctx = bt_context_create (); + if (ctx == NULL) + error (_("Unable to create bt_context")); + ret = bt_context_add_trace (ctx, dirname, "ctf", NULL, NULL, NULL); + if (ret < 0) + { + ctf_destroy (); + error (_("Unable to use libbabeltrace on directory \"%s\""), + dirname); + } + + begin_pos.type = BT_SEEK_BEGIN; + ctf_iter = bt_ctf_iter_create (ctx, &begin_pos, NULL); + if (ctf_iter == NULL) + { + ctf_destroy (); + error (_("Unable to create bt_iterator")); + } + + /* Iterate over events, and look for an event for register block + to set trace_regblock_size. */ + + /* Save the current position. */ + pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter)); + gdb_assert (pos->type == BT_SEEK_RESTORE); + + while (1) + { + const char *name; + struct bt_ctf_event *event; + + event = bt_ctf_iter_read_event (ctf_iter); + + name = bt_ctf_event_name (event); + + if (name == NULL) + break; + else if (strcmp (name, "register") == 0) + { + const struct bt_definition *scope + = bt_ctf_get_top_level_scope (event, + BT_EVENT_FIELDS); + const struct bt_definition *array + = bt_ctf_get_field (event, scope, "contents"); + + trace_regblock_size + = bt_ctf_get_array_len (bt_ctf_get_decl_from_def (array)); + } + + if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0) + break; + } + + /* Restore the position. */ + bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos); +} + +#define SET_INT32_FIELD(EVENT, SCOPE, VAR, FIELD) \ + (VAR)->FIELD = (int) bt_ctf_get_int64 (bt_ctf_get_field ((EVENT), \ + (SCOPE), \ + #FIELD)) + +/* EVENT is the "status" event and TS is filled in. */ + +static void +ctf_read_status (struct bt_ctf_event *event, struct trace_status *ts) +{ + const struct bt_definition *scope + = bt_ctf_get_top_level_scope (event, BT_EVENT_FIELDS); + + SET_INT32_FIELD (event, scope, ts, stop_reason); + SET_INT32_FIELD (event, scope, ts, stopping_tracepoint); + SET_INT32_FIELD (event, scope, ts, traceframe_count); + SET_INT32_FIELD (event, scope, ts, traceframes_created); + SET_INT32_FIELD (event, scope, ts, buffer_free); + SET_INT32_FIELD (event, scope, ts, buffer_size); + SET_INT32_FIELD (event, scope, ts, disconnected_tracing); + SET_INT32_FIELD (event, scope, ts, circular_buffer); + + bt_iter_next (bt_ctf_get_iter (ctf_iter)); +} + +/* Read the events "tsv_def" one by one, extract its contents and fill + in the list UPLOADED_TSVS. */ + +static void +ctf_read_tsv (struct uploaded_tsv **uploaded_tsvs) +{ + gdb_assert (ctf_iter != NULL); + + while (1) + { + struct bt_ctf_event *event; + const struct bt_definition *scope; + const struct bt_definition *def; + uint32_t event_id; + struct uploaded_tsv *utsv = NULL; + + event = bt_ctf_iter_read_event (ctf_iter); + scope = bt_ctf_get_top_level_scope (event, + BT_STREAM_EVENT_HEADER); + event_id = bt_ctf_get_uint64 (bt_ctf_get_field (event, scope, + "id")); + if (event_id != CTF_EVENT_ID_TSV_DEF) + break; + + scope = bt_ctf_get_top_level_scope (event, + BT_EVENT_FIELDS); + + def = bt_ctf_get_field (event, scope, "number"); + utsv = get_uploaded_tsv ((int32_t) bt_ctf_get_int64 (def), + uploaded_tsvs); + + def = bt_ctf_get_field (event, scope, "builtin"); + utsv->builtin = (int32_t) bt_ctf_get_int64 (def); + def = bt_ctf_get_field (event, scope, "initial_value"); + utsv->initial_value = bt_ctf_get_int64 (def); + + def = bt_ctf_get_field (event, scope, "name"); + utsv->name = xstrdup (bt_ctf_get_string (def)); + + if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0) + break; + } + +} + +/* Read the value of element whose index is NUM from CTF and write it + to the corresponding VAR->ARRAY. */ + +#define SET_ARRAY_FIELD(EVENT, SCOPE, VAR, NUM, ARRAY) \ + do \ + { \ + uint32_t u32, i; \ + const struct bt_definition *def; \ + \ + u32 = (uint32_t) bt_ctf_get_uint64 (bt_ctf_get_field ((EVENT), \ + (SCOPE), \ + #NUM)); \ + def = bt_ctf_get_field ((EVENT), (SCOPE), #ARRAY); \ + for (i = 0; i < u32; i++) \ + { \ + const struct bt_definition *element \ + = bt_ctf_get_index ((EVENT), def, i); \ + \ + VEC_safe_push (char_ptr, (VAR)->ARRAY, \ + xstrdup (bt_ctf_get_string (element))); \ + } \ + } \ + while (0) + +/* Read a string from CTF and set VAR->FIELD. If the length of string + is zero, set VAR->FIELD to NULL. */ + +#define SET_STRING_FIELD(EVENT, SCOPE, VAR, FIELD) \ + do \ + { \ + const char *p = bt_ctf_get_string (bt_ctf_get_field ((EVENT), \ + (SCOPE), \ + #FIELD)); \ + \ + if (strlen (p) > 0) \ + (VAR)->FIELD = xstrdup (p); \ + else \ + (VAR)->FIELD = NULL; \ + } \ + while (0) + +/* Read the events "tp_def" one by one, extract its contents and fill + in the list UPLOADED_TPS. */ + +static void +ctf_read_tp (struct uploaded_tp **uploaded_tps) +{ + gdb_assert (ctf_iter != NULL); + + while (1) + { + struct bt_ctf_event *event; + const struct bt_definition *scope; + uint32_t u32; + int32_t int32; + uint64_t u64; + struct uploaded_tp *utp = NULL; + + event = bt_ctf_iter_read_event (ctf_iter); + scope = bt_ctf_get_top_level_scope (event, + BT_STREAM_EVENT_HEADER); + u32 = bt_ctf_get_uint64 (bt_ctf_get_field (event, scope, + "id")); + if (u32 != CTF_EVENT_ID_TP_DEF) + break; + + scope = bt_ctf_get_top_level_scope (event, + BT_EVENT_FIELDS); + int32 = (int32_t) bt_ctf_get_int64 (bt_ctf_get_field (event, + scope, + "number")); + u64 = bt_ctf_get_uint64 (bt_ctf_get_field (event, scope, + "addr")); + utp = get_uploaded_tp (int32, u64, uploaded_tps); + + SET_INT32_FIELD (event, scope, utp, enabled); + SET_INT32_FIELD (event, scope, utp, step); + SET_INT32_FIELD (event, scope, utp, pass); + SET_INT32_FIELD (event, scope, utp, hit_count); + SET_INT32_FIELD (event, scope, utp, type); + + /* Read 'cmd_strings'. */ + SET_ARRAY_FIELD (event, scope, utp, cmd_num, cmd_strings); + /* Read 'actions'. */ + SET_ARRAY_FIELD (event, scope, utp, action_num, actions); + /* Read 'step_actions'. */ + SET_ARRAY_FIELD (event, scope, utp, step_action_num, + step_actions); + + SET_STRING_FIELD(event, scope, utp, at_string); + SET_STRING_FIELD(event, scope, utp, cond_string); + + if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0) + break; + } +} + +/* This is the implementation of target_ops method to_open. Open CTF + trace data, read trace status, trace state variables and tracepoint + definitions from the first packet. Set the start position at the + second packet which contains events on trace blocks. */ + +static void +ctf_open (char *dirname, int from_tty) +{ + struct bt_ctf_event *event; + uint32_t event_id; + const struct bt_definition *scope; + struct uploaded_tsv *uploaded_tsvs = NULL; + struct uploaded_tp *uploaded_tps = NULL; + + if (!dirname) + error (_("No CTF directory specified.")); + + ctf_open_dir (dirname); + + target_preopen (from_tty); + + /* Skip the first packet which about the trace status. The first + event is "frame". */ + event = bt_ctf_iter_read_event (ctf_iter); + scope = bt_ctf_get_top_level_scope (event, BT_STREAM_EVENT_HEADER); + event_id = bt_ctf_get_uint64 (bt_ctf_get_field (event, scope, "id")); + if (event_id != CTF_EVENT_ID_FRAME) + error (_("Wrong event id of the first event")); + /* The second event is "status". */ + bt_iter_next (bt_ctf_get_iter (ctf_iter)); + event = bt_ctf_iter_read_event (ctf_iter); + scope = bt_ctf_get_top_level_scope (event, BT_STREAM_EVENT_HEADER); + event_id = bt_ctf_get_uint64 (bt_ctf_get_field (event, scope, "id")); + if (event_id != CTF_EVENT_ID_STATUS) + error (_("Wrong event id of the second event")); + ctf_read_status (event, current_trace_status ()); + + ctf_read_tsv (&uploaded_tsvs); + + ctf_read_tp (&uploaded_tps); + + event = bt_ctf_iter_read_event (ctf_iter); + /* EVENT can be NULL if we've already gone to the end of stream of + events. */ + if (event != NULL) + { + scope = bt_ctf_get_top_level_scope (event, + BT_STREAM_EVENT_HEADER); + event_id = bt_ctf_get_uint64 (bt_ctf_get_field (event, + scope, "id")); + if (event_id != CTF_EVENT_ID_FRAME) + error (_("Wrong event id of the first event of the second packet")); + } + + start_pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter)); + gdb_assert (start_pos->type == BT_SEEK_RESTORE); + + trace_dirname = xstrdup (dirname); + push_target (&ctf_ops); + + merge_uploaded_trace_state_variables (&uploaded_tsvs); + merge_uploaded_tracepoints (&uploaded_tps); +} + +/* This is the implementation of target_ops method to_close. Destroy + CTF iterator and context. */ + +static void +ctf_close (void) +{ + ctf_destroy (); + xfree (trace_dirname); + trace_dirname = NULL; +} + +/* This is the implementation of target_ops method to_files_info. + Print the directory name of CTF trace data. */ + +static void +ctf_files_info (struct target_ops *t) +{ + printf_filtered ("\t`%s'\n", trace_dirname); +} + +/* This is the implementation of target_ops method to_fetch_registers. + Iterate over events whose name is "register" in current frame, + extract contents from events, and set REGCACHE with the contents. + If no matched events are found, mark registers unavailable. */ + +static void +ctf_fetch_registers (struct target_ops *ops, + struct regcache *regcache, int regno) +{ + struct gdbarch *gdbarch = get_regcache_arch (regcache); + int offset, regn, regsize, pc_regno; + char *regs = NULL; + struct bt_ctf_event *event = NULL; + struct bt_iter_pos *pos; + + /* An uninitialized reg size says we're not going to be + successful at getting register blocks. */ + if (trace_regblock_size == 0) + return; + + gdb_assert (ctf_iter != NULL); + /* Save the current position. */ + pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter)); + gdb_assert (pos->type == BT_SEEK_RESTORE); + + while (1) + { + const char *name; + struct bt_ctf_event *event1; + + event1 = bt_ctf_iter_read_event (ctf_iter); + + name = bt_ctf_event_name (event1); + + if (name == NULL || strcmp (name, "frame") == 0) + break; + else if (strcmp (name, "register") == 0) + { + event = event1; + break; + } + + if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0) + break; + } + + /* Restore the position. */ + bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos); + + if (event != NULL) + { + const struct bt_definition *scope + = bt_ctf_get_top_level_scope (event, + BT_EVENT_FIELDS); + const struct bt_definition *array + = bt_ctf_get_field (event, scope, "contents"); + + regs = bt_ctf_get_char_array (array); + /* Assume the block is laid out in GDB register number order, + each register with the size that it has in GDB. */ + offset = 0; + for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++) + { + regsize = register_size (gdbarch, regn); + /* Make sure we stay within block bounds. */ + if (offset + regsize >= trace_regblock_size) + break; + if (regcache_register_status (regcache, regn) == REG_UNKNOWN) + { + if (regno == regn) + { + regcache_raw_supply (regcache, regno, regs + offset); + break; + } + else if (regno == -1) + { + regcache_raw_supply (regcache, regn, regs + offset); + } + } + offset += regsize; + } + return; + } + + regs = alloca (trace_regblock_size); + + /* We get here if no register data has been found. Mark registers + as unavailable. */ + for (regn = 0; regn < gdbarch_num_regs (gdbarch); regn++) + regcache_raw_supply (regcache, regn, NULL); + + /* We can often usefully guess that the PC is going to be the same + as the address of the tracepoint. */ + pc_regno = gdbarch_pc_regnum (gdbarch); + if (pc_regno >= 0 && (regno == -1 || regno == pc_regno)) + { + struct tracepoint *tp = get_tracepoint (get_tracepoint_number ()); + + if (tp != NULL && tp->base.loc) + { + /* But don't try to guess if tracepoint is multi-location... */ + if (tp->base.loc->next != NULL) + { + warning (_("Tracepoint %d has multiple " + "locations, cannot infer $pc"), + tp->base.number); + return; + } + /* ... or does while-stepping. */ + if (tp->step_count > 0) + { + warning (_("Tracepoint %d does while-stepping, " + "cannot infer $pc"), + tp->base.number); + return; + } + + store_unsigned_integer (regs, register_size (gdbarch, pc_regno), + gdbarch_byte_order (gdbarch), + tp->base.loc->address); + regcache_raw_supply (regcache, pc_regno, regs); + } + } +} + +/* This is the implementation of target_ops method to_xfer_partial. + Iterate over events whose name is "memory" in + current frame, extract the address and length from events. If + OFFSET is within the range, read the contents from events to + READBUF. */ + +static LONGEST +ctf_xfer_partial (struct target_ops *ops, enum target_object object, + const char *annex, gdb_byte *readbuf, + const gdb_byte *writebuf, ULONGEST offset, + LONGEST len) +{ + /* We're only doing regular memory for now. */ + if (object != TARGET_OBJECT_MEMORY) + return -1; + + if (readbuf == NULL) + error (_("ctf_xfer_partial: trace file is read-only")); + + if (get_traceframe_number () != -1) + { + struct bt_iter_pos *pos; + int i = 0; + + gdb_assert (ctf_iter != NULL); + /* Save the current position. */ + pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter)); + gdb_assert (pos->type == BT_SEEK_RESTORE); + + /* Iterate through the traceframe's blocks, looking for + memory. */ + while (1) + { + ULONGEST amt; + uint64_t maddr; + uint16_t mlen; + enum bfd_endian byte_order + = gdbarch_byte_order (target_gdbarch ()); + const struct bt_definition *scope; + const struct bt_definition *def; + struct bt_ctf_event *event + = bt_ctf_iter_read_event (ctf_iter); + const char *name = bt_ctf_event_name (event); + + if (strcmp (name, "frame") == 0) + break; + else if (strcmp (name, "memory") != 0) + { + if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0) + break; + + continue; + } + + scope = bt_ctf_get_top_level_scope (event, + BT_EVENT_FIELDS); + + def = bt_ctf_get_field (event, scope, "address"); + maddr = bt_ctf_get_uint64 (def); + def = bt_ctf_get_field (event, scope, "length"); + mlen = (uint16_t) bt_ctf_get_uint64 (def); + + /* If the block includes the first part of the desired + range, return as much it has; GDB will re-request the + remainder, which might be in a different block of this + trace frame. */ + if (maddr <= offset && offset < (maddr + mlen)) + { + const struct bt_definition *array + = bt_ctf_get_field (event, scope, "contents"); + const struct bt_declaration *decl + = bt_ctf_get_decl_from_def (array); + gdb_byte *contents; + int k; + + contents = xmalloc (mlen); + + for (k = 0; k < mlen; k++) + { + const struct bt_definition *element + = bt_ctf_get_index (event, array, k); + + contents[k] = (gdb_byte) bt_ctf_get_uint64 (element); + } + + amt = (maddr + mlen) - offset; + if (amt > len) + amt = len; + + memcpy (readbuf, &contents[offset - maddr], amt); + + xfree (contents); + + /* Restore the position. */ + bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos); + + return amt; + } + + if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0) + break; + } + + /* Restore the position. */ + bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos); + } + + /* It's unduly pedantic to refuse to look at the executable for + read-only pieces; so do the equivalent of readonly regions aka + QTro packet. */ + if (exec_bfd != NULL) + { + asection *s; + bfd_size_type size; + bfd_vma vma; + + for (s = exec_bfd->sections; s; s = s->next) + { + if ((s->flags & SEC_LOAD) == 0 + || (s->flags & SEC_READONLY) == 0) + continue; + + vma = s->vma; + size = bfd_get_section_size (s); + if (vma <= offset && offset < (vma + size)) + { + ULONGEST amt; + + amt = (vma + size) - offset; + if (amt > len) + amt = len; + + amt = bfd_get_section_contents (exec_bfd, s, + readbuf, offset - vma, amt); + return amt; + } + } + } + + /* Indicate failure to find the requested memory block. */ + return -1; +} + +/* This is the implementation of target_ops method + to_get_trace_state_variable_value. + Iterate over events whose name is "tsv" in current frame. When the + trace variable is found, set the value of it to *VAL and return + true, otherwise return false. */ + +static int +ctf_get_trace_state_variable_value (int tsvnum, LONGEST *val) +{ + struct bt_iter_pos *pos; + int found = 0; + + gdb_assert (ctf_iter != NULL); + /* Save the current position. */ + pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter)); + gdb_assert (pos->type == BT_SEEK_RESTORE); + + /* Iterate through the traceframe's blocks, looking for 'V' + block. */ + while (1) + { + struct bt_ctf_event *event + = bt_ctf_iter_read_event (ctf_iter); + const char *name = bt_ctf_event_name (event); + + if (name == NULL || strcmp (name, "frame") == 0) + break; + else if (strcmp (name, "tsv") == 0) + { + const struct bt_definition *scope; + const struct bt_definition *def; + + scope = bt_ctf_get_top_level_scope (event, + BT_EVENT_FIELDS); + + def = bt_ctf_get_field (event, scope, "num"); + if (tsvnum == (int32_t) bt_ctf_get_uint64 (def)) + { + def = bt_ctf_get_field (event, scope, "val"); + *val = bt_ctf_get_uint64 (def); + + found = 1; + } + } + + if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0) + break; + } + + /* Restore the position. */ + bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos); + + return found; +} + +/* Return the tracepoint number in "frame" event. */ + +static int +ctf_get_tpnum_from_frame_event (struct bt_ctf_event *event) +{ + /* The packet context of events has a field "tpnum". */ + const struct bt_definition *scope + = bt_ctf_get_top_level_scope (event, BT_STREAM_PACKET_CONTEXT); + uint64_t tpnum + = bt_ctf_get_uint64 (bt_ctf_get_field (event, scope, "tpnum")); + + return (int) tpnum; +} + +/* Return the address at which the current frame was collected. */ + +static CORE_ADDR +ctf_get_traceframe_address (void) +{ + struct bt_ctf_event *event = NULL; + struct bt_iter_pos *pos; + CORE_ADDR addr = 0; + + gdb_assert (ctf_iter != NULL); + pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter)); + gdb_assert (pos->type == BT_SEEK_RESTORE); + + while (1) + { + const char *name; + struct bt_ctf_event *event1; + + event1 = bt_ctf_iter_read_event (ctf_iter); + + name = bt_ctf_event_name (event1); + + if (name == NULL) + break; + else if (strcmp (name, "frame") == 0) + { + event = event1; + break; + } + + if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0) + break; + } + + if (event != NULL) + { + int tpnum = ctf_get_tpnum_from_frame_event (event); + struct tracepoint *tp + = get_tracepoint_by_number_on_target (tpnum); + + if (tp && tp->base.loc) + addr = tp->base.loc->address; + } + + /* Restore the position. */ + bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos); + + return addr; +} + +/* This is the implementation of target_ops method to_trace_find. + Iterate the events whose name is "frame", extract the tracepoint + number in it. Return traceframe number when matched. */ + +static int +ctf_trace_find (enum trace_find_type type, int num, + CORE_ADDR addr1, CORE_ADDR addr2, int *tpp) +{ + int ret = -1; + int tfnum = 0; + int found = 0; + struct bt_iter_pos pos; + + if (num == -1) + { + if (tpp != NULL) + *tpp = -1; + return -1; + } + + gdb_assert (ctf_iter != NULL); + /* Set iterator back to the start. */ + bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), start_pos); + + while (1) + { + int id; + struct bt_ctf_event *event; + const char *name; + + event = bt_ctf_iter_read_event (ctf_iter); + + name = bt_ctf_event_name (event); + + if (event == NULL || name == NULL) + break; + + if (strcmp (name, "frame") == 0) + { + CORE_ADDR tfaddr; + + if (type == tfind_number) + { + /* Looking for a specific trace frame. */ + if (tfnum == num) + found = 1; + } + else + { + /* Start from the _next_ trace frame. */ + if (tfnum > get_traceframe_number ()) + { + switch (type) + { + case tfind_tp: + { + struct tracepoint *tp = get_tracepoint (num); + + if (tp != NULL + && (tp->number_on_target + == ctf_get_tpnum_from_frame_event (event))) + found = 1; + break; + } + case tfind_pc: + tfaddr = ctf_get_traceframe_address (); + if (tfaddr == addr1) + found = 1; + break; + case tfind_range: + tfaddr = ctf_get_traceframe_address (); + if (addr1 <= tfaddr && tfaddr <= addr2) + found = 1; + break; + case tfind_outside: + tfaddr = ctf_get_traceframe_address (); + if (!(addr1 <= tfaddr && tfaddr <= addr2)) + found = 1; + break; + default: + internal_error (__FILE__, __LINE__, _("unknown tfind type")); + } + } + } + if (found) + { + if (tpp != NULL) + *tpp = ctf_get_tpnum_from_frame_event (event); + + /* Skip the event "frame". */ + bt_iter_next (bt_ctf_get_iter (ctf_iter)); + + return tfnum; + } + tfnum++; + } + + if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0) + break; + } + + return -1; +} + +/* This is the implementation of target_ops method to_has_stack. + The target has a stack when GDB has already selected one trace + frame. */ + +static int +ctf_has_stack (struct target_ops *ops) +{ + return get_traceframe_number () != -1; +} + +/* This is the implementation of target_ops method to_has_registers. + The target has registers when GDB has already selected one trace + frame. */ + +static int +ctf_has_registers (struct target_ops *ops) +{ + return get_traceframe_number () != -1; +} + +/* This is the implementation of target_ops method to_traceframe_info. + Iterate the events whose name is "memory", in current + frame, extract memory range information, and return them in + traceframe_info. */ + +static struct traceframe_info * +ctf_traceframe_info (void) +{ + struct traceframe_info *info = XCNEW (struct traceframe_info); + const char *name; + struct bt_iter_pos *pos; + + gdb_assert (ctf_iter != NULL); + /* Save the current position. */ + pos = bt_iter_get_pos (bt_ctf_get_iter (ctf_iter)); + gdb_assert (pos->type == BT_SEEK_RESTORE); + + do + { + struct bt_ctf_event *event + = bt_ctf_iter_read_event (ctf_iter); + + name = bt_ctf_event_name (event); + + if (name == NULL || strcmp (name, "register") == 0 + || strcmp (name, "frame") == 0) + ; + else if (strcmp (name, "memory") == 0) + { + const struct bt_definition *scope + = bt_ctf_get_top_level_scope (event, + BT_EVENT_FIELDS); + const struct bt_definition *def; + struct mem_range *r; + + r = VEC_safe_push (mem_range_s, info->memory, NULL); + def = bt_ctf_get_field (event, scope, "address"); + r->start = bt_ctf_get_uint64 (def); + + def = bt_ctf_get_field (event, scope, "length"); + r->length = (uint16_t) bt_ctf_get_uint64 (def); + } + else + { + warning (_("Unhandled trace block type (%s) " + "while building trace frame info."), + name); + } + + if (bt_iter_next (bt_ctf_get_iter (ctf_iter)) < 0) + break; + } + while (name != NULL && strcmp (name, "frame") != 0); + + /* Restore the position. */ + bt_iter_set_pos (bt_ctf_get_iter (ctf_iter), pos); + + return info; +} + +/* This is the implementation of target_ops method to_get_trace_status. + The trace status for a file is that tracing can never be run. */ + +static int +ctf_get_trace_status (struct trace_status *ts) +{ + /* Other bits of trace status were collected as part of opening the + trace files, so nothing to do here. */ + + return -1; +} + +static void +init_ctf_ops (void) +{ + memset (&ctf_ops, 0, sizeof (ctf_ops)); + + ctf_ops.to_shortname = "ctf"; + ctf_ops.to_longname = "CTF file"; + ctf_ops.to_doc = "Use a CTF directory as a target.\n\ +Specify the filename of the CTF directory."; + ctf_ops.to_open = ctf_open; + ctf_ops.to_close = ctf_close; + ctf_ops.to_fetch_registers = ctf_fetch_registers; + ctf_ops.to_xfer_partial = ctf_xfer_partial; + ctf_ops.to_files_info = ctf_files_info; + ctf_ops.to_get_trace_status = ctf_get_trace_status; + ctf_ops.to_trace_find = ctf_trace_find; + ctf_ops.to_get_trace_state_variable_value + = ctf_get_trace_state_variable_value; + ctf_ops.to_stratum = process_stratum; + ctf_ops.to_has_stack = ctf_has_stack; + ctf_ops.to_has_registers = ctf_has_registers; + ctf_ops.to_traceframe_info = ctf_traceframe_info; + ctf_ops.to_magic = OPS_MAGIC; +} + +#endif + +/* -Wmissing-prototypes */ + +extern initialize_file_ftype _initialize_ctf; + +/* module initialization */ + +void +_initialize_ctf (void) +{ +#if HAVE_LIBBABELTRACE + init_ctf_ops (); + + add_target (&ctf_ops); +#endif +} diff --git a/gdb/doc/ChangeLog b/gdb/doc/ChangeLog index eb543bdbf2..9e3a956d42 100644 --- a/gdb/doc/ChangeLog +++ b/gdb/doc/ChangeLog @@ -1,3 +1,7 @@ +2013-04-10 Yao Qi + + * gdb.texinfo (Trace Files): Add "target ctf". + 2013-04-06 Jan Kratochvil * Makefile.in (POD2MAN1, POD2MAN5): Replace $(VERSION) by ../version.in. diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo index bf7e25eabb..6974b5c79d 100644 --- a/gdb/doc/gdb.texinfo +++ b/gdb/doc/gdb.texinfo @@ -12307,13 +12307,33 @@ that can be shared by multiple debugging and tracing tools. Please go to @kindex target tfile @kindex tfile +@kindex target ctf +@kindex ctf @item target tfile @var{filename} -Use the file named @var{filename} as a source of trace data. Commands -that examine data work as they do with a live target, but it is not -possible to run any new trace experiments. @code{tstatus} will report -the state of the trace run at the moment the data was saved, as well -as the current trace frame you are examining. @var{filename} must be -on a filesystem accessible to the host. +@itemx target ctf @var{dirname} +Use the file named @var{filename} or directory named @var{dirname} as +a source of trace data. Commands that examine data work as they do with +a live target, but it is not possible to run any new trace experiments. +@code{tstatus} will report the state of the trace run at the moment +the data was saved, as well as the current trace frame you are examining. +@var{filename} or @var{dirname} must be on a filesystem accessible to +the host. + +@smallexample +(@value{GDBP}) target ctf ctf.ctf +(@value{GDBP}) tfind +Found trace frame 0, tracepoint 2 +39 ++a; /* set tracepoint 1 here */ +(@value{GDBP}) tdump +Data collected at tracepoint 2, trace frame 0: +i = 0 +a = 0 +b = 1 '\001' +c = @{"123", "456", "789", "123", "456", "789"@} +d = @{@{@{a = 1, b = 2@}, @{a = 3, b = 4@}@}, @{@{a = 5, b = 6@}, @{a = 7, b = 8@}@}@} +(@value{GDBP}) p b +$1 = 1 +@end smallexample @end table diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 574aff8b1b..92d8ff3a3a 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,17 @@ +2013-04-10 Yao Qi + + * gdb.trace/actions.exp: Save trace data to CTF. + Change to ctf target if GDB supports, read CTF data in ctf + target, and check the actions of tracepoints. + * gdb.trace/while-stepping.exp: Likewise. + * gdb.trace/report.exp: Test GDB saves trace data to CTF + format and read CTF trace file if GDB supports. + * gdb.trace/tstatus.exp: Save trace data to CTF. If ctf + target is supported, change to ctf target, read trace data and + check output of command "tstatus". + * gdb.trace/tsv.exp: Save trace frame to CTF. If GDB supports, + read CTF data by target ctf and call check_tsv. + 2013-04-10 Yao Qi * gdb.trace/actions.exp (check_tracepoint): New. diff --git a/gdb/testsuite/gdb.trace/actions.exp b/gdb/testsuite/gdb.trace/actions.exp index 5fa1b97a91..be2bb3ed0b 100644 --- a/gdb/testsuite/gdb.trace/actions.exp +++ b/gdb/testsuite/gdb.trace/actions.exp @@ -310,6 +310,9 @@ gdb_test_no_output "tstop" "" set tracefile [standard_output_file ${testfile}] gdb_test "tsave ${tracefile}.tf" \ "Trace data saved to file '${tracefile}.tf'\.\\r" +gdb_test "tsave -ctf ${tracefile}.ctf" \ + "Trace data saved to directory '${tracefile}.ctf'\.\\r" \ + "save ctf trace file" # Restart GDB and read the trace data in tfile target. gdb_exit @@ -319,3 +322,24 @@ gdb_file_cmd $binfile gdb_test "target tfile ${tracefile}.tf" ".*" \ "change to tfile target" check_tracepoint "tfile" + +# Try to read ctf data if GDB supports. +set gdb_can_read_ctf_data 0 +gdb_test_multiple "target ctf" "" { + -re "Undefined target command: \"ctf\"\. Try \"help target\"\.\r\n$gdb_prompt $" { + set gdb_can_read_ctf_data 0 + } + -re "No CTF directory specified.*\r\n$gdb_prompt $" { + set gdb_can_read_ctf_data 1 + } +} + +if { $gdb_can_read_ctf_data } { + gdb_exit + gdb_start + gdb_reinitialize_dir $srcdir/$subdir + gdb_file_cmd $binfile + gdb_test "target ctf ${tracefile}.ctf" ".*" \ + "change to ctf target" + check_tracepoint "ctf" +} diff --git a/gdb/testsuite/gdb.trace/report.exp b/gdb/testsuite/gdb.trace/report.exp index 3c3025d609..8bb71e4160 100644 --- a/gdb/testsuite/gdb.trace/report.exp +++ b/gdb/testsuite/gdb.trace/report.exp @@ -412,6 +412,11 @@ gdb_test "tsave ${tracefile}.tf" \ "Trace data saved to file '${tracefile}.tf'.*" \ "save tfile trace file" +# Save trace frames to ctf. +gdb_test "tsave -ctf ${tracefile}.ctf" \ + "Trace data saved to directory '${tracefile}.ctf'.*" \ + "save ctf trace file" + # Change target to tfile. set test "change to tfile target" gdb_test_multiple "target tfile ${tracefile}.tf" "$test" { @@ -425,3 +430,12 @@ gdb_test_multiple "target tfile ${tracefile}.tf" "$test" { } # Test the collected trace frames from tfile. use_collected_data "tfile" + +# Try to read ctf data if GDB supports. +gdb_test_multiple "target ctf ${tracefile}.ctf" "" { + -re "Undefined target command: \"ctf ${tracefile}.ctf\"\. Try \"help target\"\.\r\n$gdb_prompt $" { + } + -re ".*\r\n$gdb_prompt $" { + use_collected_data "ctf" + } +} diff --git a/gdb/testsuite/gdb.trace/tstatus.exp b/gdb/testsuite/gdb.trace/tstatus.exp index 74d0c2729d..c709ad83b1 100644 --- a/gdb/testsuite/gdb.trace/tstatus.exp +++ b/gdb/testsuite/gdb.trace/tstatus.exp @@ -142,6 +142,10 @@ set tracefile [standard_output_file ${testfile}] gdb_test "tsave ${tracefile}.tf" \ "Trace data saved to file '${tracefile}.tf'.*" \ "save tfile trace file" +# Save trace frames to CTF. +gdb_test "tsave -ctf ${tracefile}.ctf" \ + "Trace data saved to directory '${tracefile}.ctf'.*" \ + "save ctf trace file" # Change target to tfile. set test "change to tfile target" @@ -162,3 +166,13 @@ set tstatus_output [string map {\) \\)} $tstatus_output] # The status should be identical to the status of live inferior. gdb_test "tstatus" "Using a trace file\.\r\n${tstatus_output}.*" \ "tstatus on tfile target" + +# Change target to ctf if GDB supports. +gdb_test_multiple "target ctf ${tracefile}.ctf" "" { + -re "Undefined target command: \"ctf ${tracefile}.ctf\"\. Try \"help target\"\.\r\n$gdb_prompt $" { + } + -re ".*\r\n$gdb_prompt $" { + gdb_test "tstatus" "Using a trace file\.\r\n${tstatus_output}.*" \ + "tstatus on ctf target" + } +} diff --git a/gdb/testsuite/gdb.trace/tsv.exp b/gdb/testsuite/gdb.trace/tsv.exp index 6e88adc8a5..76b86455d4 100644 --- a/gdb/testsuite/gdb.trace/tsv.exp +++ b/gdb/testsuite/gdb.trace/tsv.exp @@ -147,6 +147,10 @@ set tracefile [standard_output_file ${testfile}] gdb_test "tsave ${tracefile}.tf" \ "Trace data saved to file '${tracefile}.tf'.*" \ "save tfile trace file" +# Save trace frames to ctf. +gdb_test "tsave -ctf ${tracefile}.ctf" \ + "Trace data saved to directory '${tracefile}.ctf'.*" \ + "save ctf trace file" proc check_tsv { data_source } { with_test_prefix "${data_source}" { @@ -174,4 +178,13 @@ gdb_test_multiple "target tfile ${tracefile}.tf" "$test" { } # Check the tsv from tfile. + check_tsv "tfile" +# Try to read ctf data if GDB supports. +gdb_test_multiple "target ctf ${tracefile}.ctf" "" { + -re "Undefined target command: \"ctf ${tracefile}.ctf\"\. Try \"help target\"\.\r\n$gdb_prompt $" { + } + -re ".*\r\n$gdb_prompt $" { + check_tsv "ctf" + } +} diff --git a/gdb/testsuite/gdb.trace/while-stepping.exp b/gdb/testsuite/gdb.trace/while-stepping.exp index 0b823a4e71..da9b14a65d 100644 --- a/gdb/testsuite/gdb.trace/while-stepping.exp +++ b/gdb/testsuite/gdb.trace/while-stepping.exp @@ -136,6 +136,9 @@ set tracefile [standard_output_file ${testfile}] gdb_test "tsave ${tracefile}.tf" \ "Trace data saved to file '${tracefile}.tf'\.\\r" \ "save tfile trace file" +gdb_test "tsave -ctf ${tracefile}.ctf" \ + "Trace data saved to directory '${tracefile}.ctf'\.\\r" \ + "save ctf trace file" # Restart GDB and read the trace data in tfile target. gdb_exit @@ -145,3 +148,24 @@ gdb_file_cmd $binfile gdb_test "target tfile ${tracefile}.tf" ".*" \ "change to tfile target" check_tracepoint "tfile" + +# Try to read ctf data if GDB supports. +set gdb_can_read_ctf_data 0 +gdb_test_multiple "target ctf" "" { + -re "Undefined target command: \"ctf\"\. Try \"help target\"\.\r\n$gdb_prompt $" { + set gdb_can_read_ctf_data 0 + } + -re "No CTF directory specified.*\r\n$gdb_prompt $" { + set gdb_can_read_ctf_data 1 + } +} + +if { $gdb_can_read_ctf_data } { + gdb_exit + gdb_start + gdb_reinitialize_dir $srcdir/$subdir + gdb_file_cmd $binfile + gdb_test "target ctf ${tracefile}.ctf" ".*" \ + "change to ctf target" + check_tracepoint "ctf" +} diff --git a/gdb/tracepoint.c b/gdb/tracepoint.c index 3d8b131f86..c9ffc77fd2 100644 --- a/gdb/tracepoint.c +++ b/gdb/tracepoint.c @@ -127,14 +127,6 @@ extern void (*deprecated_readline_end_hook) (void); typedef struct trace_state_variable tsv_s; DEF_VEC_O(tsv_s); -/* An object describing the contents of a traceframe. */ - -struct traceframe_info -{ - /* Collected memory. */ - VEC(mem_range_s) *memory; -}; - static VEC(tsv_s) *tvariables; /* The next integer to assign to a variable. */ @@ -3303,8 +3295,6 @@ static const struct trace_file_write_ops tfile_write_ops = #define TRACE_WRITE_V_BLOCK(writer, num, val) \ writer->ops->frame_ops->write_v_block ((writer), (num), (val)) -extern int trace_regblock_size; - /* Save tracepoint data to file named FILENAME through WRITER. WRITER determines the trace file format. If TARGET_DOES_SAVE is non-zero, the save is performed on the target, otherwise GDB obtains all trace @@ -3741,6 +3731,12 @@ get_traceframe_number (void) return traceframe_number; } +int +get_tracepoint_number (void) +{ + return tracepoint_number; +} + /* Make the traceframe NUM be the current trace frame. Does nothing if NUM is already current. */ @@ -3859,7 +3855,7 @@ free_uploaded_tps (struct uploaded_tp **utpp) /* Given a number and address, return an uploaded tracepoint with that number, creating if necessary. */ -static struct uploaded_tsv * +struct uploaded_tsv * get_uploaded_tsv (int num, struct uploaded_tsv **utsvp) { struct uploaded_tsv *utsv; diff --git a/gdb/tracepoint.h b/gdb/tracepoint.h index 8df906f114..f88394b8de 100644 --- a/gdb/tracepoint.h +++ b/gdb/tracepoint.h @@ -24,6 +24,14 @@ #include "memrange.h" #include "gdb_vecs.h" +/* An object describing the contents of a traceframe. */ + +struct traceframe_info +{ + /* Collected memory. */ + VEC(mem_range_s) *memory; +}; + /* A trace state variable is a value managed by a target being traced. A trace state variable (or tsv for short) can be accessed and assigned to by tracepoint actions and conditionals, but is not @@ -142,6 +150,8 @@ struct trace_status *current_trace_status (void); extern char *default_collect; +extern int trace_regblock_size; + /* Struct to collect random info about tracepoints on the target. */ struct uploaded_tp @@ -324,6 +334,9 @@ extern void (*deprecated_trace_start_stop_hook) (int start, int from_tty); /* Returns the current traceframe number. */ extern int get_traceframe_number (void); +/* Returns the tracepoint number for current traceframe. */ +extern int get_tracepoint_number (void); + /* Make the traceframe NUM be the current GDB trace frame number, and do nothing more. In particular, this does not flush the register/frame caches or notify the target about the trace frame @@ -368,6 +381,8 @@ extern void parse_tsv_definition (char *line, struct uploaded_tsv **utsvp); extern struct uploaded_tp *get_uploaded_tp (int num, ULONGEST addr, struct uploaded_tp **utpp); +extern struct uploaded_tsv *get_uploaded_tsv (int num, + struct uploaded_tsv **utsvp); extern struct tracepoint *create_tracepoint_from_upload (struct uploaded_tp *utp); extern void merge_uploaded_tracepoints (struct uploaded_tp **utpp); extern void merge_uploaded_trace_state_variables (struct uploaded_tsv **utsvp); -- 2.34.1