From ae20e79ae852fee8f7d42701a54a95de3b79ecea Mon Sep 17 00:00:00 2001 From: Tim Wiederhake Date: Tue, 2 May 2017 11:35:54 +0200 Subject: [PATCH] Python: Use correct ptid in btrace recording 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 | 12 +++ gdb/btrace.c | 8 +- gdb/python/py-record-btrace.c | 32 +++++--- gdb/python/py-record.c | 17 +--- gdb/python/py-record.h | 40 +++++++++ gdb/testsuite/ChangeLog | 5 ++ .../gdb.python/py-record-btrace-threads.c | 58 +++++++++++++ .../gdb.python/py-record-btrace-threads.exp | 81 +++++++++++++++++++ 8 files changed, 224 insertions(+), 29 deletions(-) create mode 100644 gdb/python/py-record.h create mode 100644 gdb/testsuite/gdb.python/py-record-btrace-threads.c create mode 100644 gdb/testsuite/gdb.python/py-record-btrace-threads.exp diff --git a/gdb/ChangeLog b/gdb/ChangeLog index 78e4469e12..d4741b043c 100644 --- a/gdb/ChangeLog +++ b/gdb/ChangeLog @@ -1,3 +1,15 @@ +2017-05-01 Tim Wiederhake + + * 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 * python/py-record-btrace.c (BTPY_REQUIRE_VALID_INSN, diff --git a/gdb/btrace.c b/gdb/btrace.c index 238df0a123..380108682e 100644 --- a/gdb/btrace.c +++ b/gdb/btrace.c @@ -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) diff --git a/gdb/python/py-record-btrace.c b/gdb/python/py-record-btrace.c index 6ba9d7e25b..5f9264d4e8 100644 --- a/gdb/python/py-record-btrace.c +++ b/gdb/python/py-record-btrace.c @@ -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)) diff --git a/gdb/python/py-record.c b/gdb/python/py-record.c index 60c0a7ce92..63cd293c5d 100644 --- a/gdb/python/py-record.c +++ b/gdb/python/py-record.c @@ -18,26 +18,11 @@ along with this program. If not, see . */ #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 index 0000000000..c386ba23b0 --- /dev/null +++ b/gdb/python/py-record.h @@ -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 . */ + +#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 */ diff --git a/gdb/testsuite/ChangeLog b/gdb/testsuite/ChangeLog index 2f24d47958..dac5b2e399 100644 --- a/gdb/testsuite/ChangeLog +++ b/gdb/testsuite/ChangeLog @@ -1,3 +1,8 @@ +2017-05-01 Tim Wiederhake + + * gdb.python/py-record-btrace-threads.c: New file. + * gdb.python/py-record-btrace-threads.exp: New file. + 2017-04-28 Sergio Durigan Junior 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 index 0000000000..3dc58cac96 --- /dev/null +++ b/gdb/testsuite/gdb.python/py-record-btrace-threads.c @@ -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 . */ + +#include + +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 index 0000000000..17fb5d019c --- /dev/null +++ b/gdb/testsuite/gdb.python/py-record-btrace-threads.exp @@ -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 . + +# 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 +}