Python: Use correct ptid in btrace recording
authorTim Wiederhake <tim.wiederhake@intel.com>
Tue, 2 May 2017 09:35:54 +0000 (11:35 +0200)
committerTim Wiederhake <tim.wiederhake@intel.com>
Tue, 2 May 2017 09:35:54 +0000 (11:35 +0200)
The user would always get the instruction_history and function_call_history
objects of the current thread, not the thread for which the gdb.Record object
was created.

The attached testcase fails without this patch and passes with the patch.

gdb/ChangeLog
gdb/btrace.c
gdb/python/py-record-btrace.c
gdb/python/py-record.c
gdb/python/py-record.h [new file with mode: 0644]
gdb/testsuite/ChangeLog
gdb/testsuite/gdb.python/py-record-btrace-threads.c [new file with mode: 0644]
gdb/testsuite/gdb.python/py-record-btrace-threads.exp [new file with mode: 0644]

index 78e4469e12171fd31e7735fedeb6be678de9ac33..d4741b043c0cc685f2dce9c3a49a21bf49a35ba3 100644 (file)
@@ -1,3 +1,15 @@
+2017-05-01  Tim Wiederhake  <tim.wiederhake@intel.com>
+
+       * btrace.c (btrace_fetch): Set inferior_ptid.
+       * python/py-record-btrace.c: Add "py-record.h" include.
+       (recpy_bt_format, recpy_bt_replay_position, recpy_bt_begin,
+       recpy_bt_end, recpy_bt_instruction_history,
+       recpy_bt_function_call_history, recpy_bt_goto): Use ptid stored
+       in gdb.Record object instead of current ptid.
+       * python/py-record.c: Include new "py-record.h" file.
+       (recpy_record_object): Moved to py-record.h.
+       * python/py-record.h: New file.
+
 2017-05-01  Tim Wiederhake  <tim.wiederhake@intel.com>
 
        * python/py-record-btrace.c (BTPY_REQUIRE_VALID_INSN,
index 238df0a123b4b805ca01ab51829e287033df53c9..380108682ef208b2da6e678f1af2922822c95dfd 100644 (file)
@@ -1802,11 +1802,17 @@ btrace_fetch (struct thread_info *tp)
   if (btinfo->replay != NULL)
     return;
 
+  /* With CLI usage, TP->PTID always equals INFERIOR_PTID here.  Now that we
+     can store a gdb.Record object in Python referring to a different thread
+     than the current one, temporarily set INFERIOR_PTID.  */
+  cleanup = save_inferior_ptid ();
+  inferior_ptid = tp->ptid;
+
   /* We should not be called on running or exited threads.  */
   gdb_assert (can_access_registers_ptid (tp->ptid));
 
   btrace_data_init (&btrace);
-  cleanup = make_cleanup_btrace_data (&btrace);
+  make_cleanup_btrace_data (&btrace);
 
   /* Let's first try to extend the trace we already have.  */
   if (btinfo->end != NULL)
index 6ba9d7e25b026217e1dc359a982badd1ca90265c..5f9264d4e89990f32a30f65fc924f2a06fe6e741 100644 (file)
@@ -22,6 +22,7 @@
 #include "gdbcmd.h"
 #include "gdbthread.h"
 #include "btrace.h"
+#include "py-record.h"
 #include "py-record-btrace.h"
 #include "disasm.h"
 
@@ -734,7 +735,8 @@ recpy_bt_method (PyObject *self, void *closure)
 PyObject *
 recpy_bt_format (PyObject *self, void *closure)
 {
-  const struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+  const recpy_record_object * const record = (recpy_record_object *) self;
+  const struct thread_info * const tinfo = find_thread_ptid (record->ptid);
   const struct btrace_config * config;
 
   if (tinfo == NULL)
@@ -754,7 +756,8 @@ recpy_bt_format (PyObject *self, void *closure)
 PyObject *
 recpy_bt_replay_position (PyObject *self, void *closure)
 {
-  const struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+  const recpy_record_object * const record = (recpy_record_object *) self;
+  const struct thread_info * const tinfo = find_thread_ptid (record->ptid);
 
   if (tinfo == NULL)
     Py_RETURN_NONE;
@@ -762,7 +765,7 @@ recpy_bt_replay_position (PyObject *self, void *closure)
   if (tinfo->btrace.replay == NULL)
     Py_RETURN_NONE;
 
-  return btpy_insn_new (inferior_ptid,
+  return btpy_insn_new (record->ptid,
                        btrace_insn_number (tinfo->btrace.replay));
 }
 
@@ -772,7 +775,8 @@ recpy_bt_replay_position (PyObject *self, void *closure)
 PyObject *
 recpy_bt_begin (PyObject *self, void *closure)
 {
-  struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+  const recpy_record_object * const record = (recpy_record_object *) self;
+  struct thread_info * const tinfo = find_thread_ptid (record->ptid);
   struct btrace_insn_iterator iterator;
 
   if (tinfo == NULL)
@@ -784,7 +788,7 @@ recpy_bt_begin (PyObject *self, void *closure)
     Py_RETURN_NONE;
 
   btrace_insn_begin (&iterator, &tinfo->btrace);
-  return btpy_insn_new (inferior_ptid, btrace_insn_number (&iterator));
+  return btpy_insn_new (record->ptid, btrace_insn_number (&iterator));
 }
 
 /* Implementation of
@@ -793,7 +797,8 @@ recpy_bt_begin (PyObject *self, void *closure)
 PyObject *
 recpy_bt_end (PyObject *self, void *closure)
 {
-  struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+  const recpy_record_object * const record = (recpy_record_object *) self;
+  struct thread_info * const tinfo = find_thread_ptid (record->ptid);
   struct btrace_insn_iterator iterator;
 
   if (tinfo == NULL)
@@ -805,7 +810,7 @@ recpy_bt_end (PyObject *self, void *closure)
     Py_RETURN_NONE;
 
   btrace_insn_end (&iterator, &tinfo->btrace);
-  return btpy_insn_new (inferior_ptid, btrace_insn_number (&iterator));
+  return btpy_insn_new (record->ptid, btrace_insn_number (&iterator));
 }
 
 /* Implementation of
@@ -814,7 +819,8 @@ recpy_bt_end (PyObject *self, void *closure)
 PyObject *
 recpy_bt_instruction_history (PyObject *self, void *closure)
 {
-  struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+  const recpy_record_object * const record = (recpy_record_object *) self;
+  struct thread_info * const tinfo = find_thread_ptid (record->ptid);
   struct btrace_insn_iterator iterator;
   unsigned long first = 0;
   unsigned long last = 0;
@@ -833,7 +839,7 @@ recpy_bt_instruction_history (PyObject *self, void *closure)
    btrace_insn_end (&iterator, &tinfo->btrace);
    last = btrace_insn_number (&iterator);
 
-   return btpy_list_new (inferior_ptid, first, last, 1, &btpy_insn_type);
+   return btpy_list_new (record->ptid, first, last, 1, &btpy_insn_type);
 }
 
 /* Implementation of
@@ -842,7 +848,8 @@ recpy_bt_instruction_history (PyObject *self, void *closure)
 PyObject *
 recpy_bt_function_call_history (PyObject *self, void *closure)
 {
-  struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+  const recpy_record_object * const record = (recpy_record_object *) self;
+  struct thread_info * const tinfo = find_thread_ptid (record->ptid);
   struct btrace_call_iterator iterator;
   unsigned long first = 0;
   unsigned long last = 0;
@@ -861,7 +868,7 @@ recpy_bt_function_call_history (PyObject *self, void *closure)
   btrace_call_end (&iterator, &tinfo->btrace);
   last = btrace_call_number (&iterator);
 
-  return btpy_list_new (inferior_ptid, first, last, 1, &btpy_call_type);
+  return btpy_list_new (record->ptid, first, last, 1, &btpy_call_type);
 }
 
 /* Implementation of BtraceRecord.goto (self, BtraceInstruction) -> None.  */
@@ -869,7 +876,8 @@ recpy_bt_function_call_history (PyObject *self, void *closure)
 PyObject *
 recpy_bt_goto (PyObject *self, PyObject *args)
 {
-  struct thread_info * const tinfo = find_thread_ptid (inferior_ptid);
+  const recpy_record_object * const record = (recpy_record_object *) self;
+  struct thread_info * const tinfo = find_thread_ptid (record->ptid);
   const btpy_object *obj;
 
   if (tinfo == NULL || btrace_is_empty (tinfo))
index 60c0a7ce92bcdea49ad748a96db4442ce44f24a6..63cd293c5d37a69e4697d9d0aa98b1af208fa502 100644 (file)
    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
 
 #include "defs.h"
-#include "inferior.h"
-#include "record.h"
-#include "python-internal.h"
+#include "py-record.h"
 #include "py-record-btrace.h"
 #include "py-record-full.h"
 #include "target.h"
 
-/* Python Record object.  */
-
-typedef struct
-{
-  PyObject_HEAD
-
-  /* The ptid this object refers to.  */
-  ptid_t ptid;
-
-  /* The current recording method.  */
-  enum record_method method;
-} recpy_record_object;
-
 /* Python Record type.  */
 
 static PyTypeObject recpy_record_type = {
diff --git a/gdb/python/py-record.h b/gdb/python/py-record.h
new file mode 100644 (file)
index 0000000..c386ba2
--- /dev/null
@@ -0,0 +1,40 @@
+/* Python interface to record targets.
+
+   Copyright 2017 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   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/>.  */
+
+#ifndef GDB_PY_RECORD_H
+#define GDB_PY_RECORD_H
+
+#include "inferior.h"
+#include "python-internal.h"
+#include "record.h"
+
+/* Python Record object.  */
+
+typedef struct
+{
+  PyObject_HEAD
+
+  /* The ptid this object refers to.  */
+  ptid_t ptid;
+
+  /* The current recording method.  */
+  enum record_method method;
+} recpy_record_object;
+
+#endif /* GDB_PY_RECORD_H */
index 2f24d4795859b582a630ef2dbf17a87804e098c0..dac5b2e399fea8ff31acb1d7b9e591db17747970 100644 (file)
@@ -1,3 +1,8 @@
+2017-05-01  Tim Wiederhake  <tim.wiederhake@intel.com>
+
+       * gdb.python/py-record-btrace-threads.c: New file.
+       * gdb.python/py-record-btrace-threads.exp: New file.
+
 2017-04-28  Sergio Durigan Junior  <sergiodj@redhat.com>
 
        PR testsuite/8595
diff --git a/gdb/testsuite/gdb.python/py-record-btrace-threads.c b/gdb/testsuite/gdb.python/py-record-btrace-threads.c
new file mode 100644 (file)
index 0000000..3dc58ca
--- /dev/null
@@ -0,0 +1,58 @@
+/* This testcase is part of GDB, the GNU debugger.
+
+   Copyright 2017 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/>.  */
+
+#include <pthread.h>
+
+static pthread_barrier_t barrier;
+static int dummy;
+
+static void *
+func1 (void *arg)
+{
+  pthread_barrier_wait (&barrier);
+  dummy = 1; /* bp1 */
+  pthread_barrier_wait (&barrier);
+  dummy = 1;
+  pthread_barrier_wait (&barrier);
+  return arg;
+}
+
+static void *
+func2 (void *arg)
+{
+  pthread_barrier_wait (&barrier);
+  dummy = 2;
+  pthread_barrier_wait (&barrier);
+  dummy = 2;
+  pthread_barrier_wait (&barrier); /* bp2 */
+  return arg;
+}
+
+int
+main (void)
+{
+  pthread_t thread;
+
+  pthread_barrier_init (&barrier, NULL, 2);
+
+  pthread_create (&thread, NULL, func2, NULL);
+  func1 (NULL);
+
+  pthread_join (thread, NULL);
+  pthread_barrier_destroy (&barrier);
+  return 0;
+}
diff --git a/gdb/testsuite/gdb.python/py-record-btrace-threads.exp b/gdb/testsuite/gdb.python/py-record-btrace-threads.exp
new file mode 100644 (file)
index 0000000..17fb5d0
--- /dev/null
@@ -0,0 +1,81 @@
+# This testcase is part of GDB, the GNU debugger.
+#
+# Copyright 2017 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/>.
+
+# Skip this test if btrace is disabled.
+
+if { [skip_btrace_tests] } {
+    untested "skipping btrace tests"
+    return -1
+}
+
+standard_testfile
+
+if { [gdb_compile_pthreads "$srcdir/$subdir/$srcfile" "$binfile" executable {debug} ] != "" } {
+    untested "failed to prepare"
+    return -1
+}
+clean_restart $testfile
+
+# Skip this test if python is disabled.
+
+load_lib gdb-python.exp
+if { [skip_python_tests] } {
+    untested "skipping python tests"
+    return -1
+}
+
+if { ![runto_main] } {
+    untested "failed to run to main"
+    return -1
+}
+
+# set up breakpoints
+gdb_breakpoint $srcfile:[gdb_get_line_number "bp1" $srcfile]
+gdb_breakpoint $srcfile:[gdb_get_line_number "bp2" $srcfile]
+
+# record data
+gdb_continue_to_breakpoint "cont to bp.1" ".*bp1.*"
+gdb_test_no_output "record btrace"
+gdb_continue_to_breakpoint "cont to bp.2" ".*bp2.*"
+
+# acquire the record objects for thread 1 and thread 2
+gdb_test "thread 1" ".*"
+gdb_test "record function-call-history" ".*" "fch thread 1"
+gdb_test_no_output "python rec1 = gdb.current_recording()"
+gdb_test "thread 2" ".*"
+gdb_test "record function-call-history" ".*" "fch thread 2"
+gdb_test_no_output "python rec2 = gdb.current_recording()"
+
+# Thread 1 is supposed to call func1 (), thread 2 is supposed to call func2 ().
+# Check that the function call history for the current thread contains a call
+# to the right function and does not contain a call to the wrong function.
+proc check_insn_for_thread { self other } {
+  with_test_prefix "checking thread $self" {
+    gdb_test_no_output "python fch = rec$self.function_call_history"
+    gdb_test_no_output "python f1calls = \{x for x in fch if x.symbol and x.symbol.name == \"func1\"\}"
+    gdb_test_no_output "python f2calls = \{x for x in fch if x.symbol and x.symbol.name == \"func2\"\}"
+
+    gdb_test "python print not f${self}calls" "False"
+    gdb_test "python print not f${other}calls" "True"
+  }
+}
+
+foreach_with_prefix thread { 1 2 } {
+  gdb_test "thread $thread"
+  check_insn_for_thread 1 2
+  check_insn_for_thread 2 1
+}