4ee4cc5016
- Maintenance scripts: * Add KFAILs in qa.sh. * Add missing '-a' in gdb.log greps in qa.sh. - Disable big endian powerpc. - Maintenance scripts: * Fix patch filtering in clean.sh. * Add KFAIL in qa.sh. - Patches added: * gdb-testsuite-add-checks-to-gdb.arch-i386-sse.exp.patch * gdb-testsuite-add-gdb.testsuite-dump-system-info.exp.patch * gdb-testsuite-add-kfail-in-gdb.threads-fork-plus-threads.exp.patch * gdb-testsuite-factor-out-dump_info-in-gdb.testsuite-dump-system-info.exp.patch * gdb-testsuite-fix-fail-in-gdb.threads-fork-and-threads.exp.patch * gdb-testsuite-fix-gdb.threads-linux-dp.exp.patch * gdb-testsuite-fix-port-detection-in-gdb.debuginfod-fetch_src_and_symbols.exp.patch * gdb-testsuite-handle-recursive-internal-problem-in-gdb_internal_error_resync.patch * gdb-testsuite-handle-runto-fail-in-gdb.mi-mi-var-cp.exp.patch * gdb-tui-fix-breakpoint-display-functionality.patch * gdb-testsuite-Fix-gdb.threads-thread-specific-bp.exp.patch - Patches dropped: * fix-gdb.threads-linux-dp.exp.patch - Added maintenance script: * qa.sh - Drop 32bit packages for s390x. - Fix SLE-15 s390x: unresolvable, nothing provides glibc-devel-static-32bit. - Disable test-case gdb.base/break-interp.exp for SLE-11. It causes a "glibc detected *** expect: double free or corruption (out)" in expect and a subsequent runtest abort. This might be the cause of the package build failure due to "Job seems to be stuck here, killed. (after 8hrs of inactivity)" - Patches added: * gdb-testsuite-fix-gdb.base-step-over-syscall.exp-with-m32-amd-case.patch * gdb-testsuite-fix-gdb.ada-big_packed_array.exp-xfail-for-m32.patch * gdb-testsuite-fix-race-in-gdb.threads-detach-step-over.exp.patch * fix-gdb.multi-multi-term-settings.exp-race.patch * gdb-testsuite-update-test-gdb.base-step-over-syscall.exp.patch - Rebase to 11.1 release (as in fedora 35 @ 9cd9368): * GDB now supports general memory tagging functionality if the underlying architecture supports the proper primitives and hooks. Currently this is enabled only for AArch64 MTE. * GDB will now look for the .gdbinit file in a config directory before looking for ~/.gdbinit. The file is searched for in the following locations: $XDG_CONFIG_HOME/gdb/gdbinit, $HOME/.config/gdb/gdbinit, $HOME/.gdbinit. * GDB will now load and process commands from ~/.config/gdb/gdbearlyinit or ~/.gdbearlyinit if these files are present. These files are processed earlier than any of the other initialization files and can affect parts of GDB's startup that previously had already been completed before the initialization files were read, for example styling of the initial GDB greeting. * GDB now has two new options "--early-init-command" and "--early-init-eval-command" with corresponding short options "-eix" and "-eiex" that allow options (that would normally appear in a gdbearlyinit file) to be passed on the command line. * set startup-quietly on|off show startup-quietly When 'on', this causes GDB to act as if "-silent" were passed on the command line. This command needs to be added to an early initialization file (e.g. ~/.config/gdb/gdbearlyinit) in order to affect GDB. * For RISC-V targets, the target feature "org.gnu.gdb.riscv.vector" is now understood by GDB, and can be used to describe the vector registers of a target. * TUI windows now support mouse actions. The mouse wheel scrolls the appropriate window. * Key combinations that do not have a specific action on the focused window are passed to GDB. For example, you now can use Ctrl-Left/Ctrl-Right to move between words in the command window regardless of which window is in focus. Previously you would need to focus on the command window for such key combinations to work. * set python ignore-environment on|off show python ignore-environment When 'on', this causes GDB's builtin Python to ignore any environment variables that would otherwise affect how Python behaves. This command needs to be added to an early initialization file (e.g. ~/.config/gdb/gdbearlyinit) in order to affect GDB. * set python dont-write-bytecode auto|on|off show python dont-write-bytecode When 'on', this causes GDB's builtin Python to not write any byte-code (.pyc files) to disk. This command needs to be added to an early initialization file (e.g. ~/.config/gdb/gdbearlyinit) in order to affect GDB. When 'off' byte-code will always be written. When set to 'auto' (the default) Python will check the PYTHONDONTWRITEBYTECODE environment variable. * break [PROBE_MODIFIER] [LOCATION] [thread THREADNUM] [-force-condition] [if CONDITION] This command would previously refuse setting a breakpoint if the CONDITION expression is invalid at a location. It now accepts and defines the breakpoint if there is at least one location at which the CONDITION is valid. The locations for which the CONDITION is invalid, are automatically disabled. If CONDITION is invalid at all of the locations, setting the breakpoint is still rejected. However, the '-force-condition' flag can be used in this case for forcing GDB to define the breakpoint, making all the current locations automatically disabled. This may be useful if the user knows the condition will become meaningful at a future location, e.g. due to a shared library load. - Update libipt to v2.0.4. Dropped obsoleted patch: * v1.5-libipt-static.patch - Obsoleted fedora patches dropped: * gdb-moribund-utrace-workaround.patch * gdb-save-restore-file-offset-while-reading-notes-in-core-file.patch * gdb-vla-intel-fix-print-char-array.patch * gdb-vla-intel-fortran-strides.patch * gdb-vla-intel-fortran-vla-strings.patch * gdb-vla-intel-stringbt-fix.patch * gdb-vla-intel-tests.patch - Obsoleted fedora fixup patches dropped: * fixup-2-gdb-archer-vla-tests.patch * fixup-2-gdb-rhbz1156192-recursive-dlopen-test.patch * fixup-3-gdb-archer-vla-tests.patch * fixup-gdb-6.3-test-pie-20050107.patch * fixup-gdb-6.3-threaded-watchpoints2-20050225.patch * fixup-gdb-6.5-sharedlibrary-path.patch * fixup-gdb-6.8-bz442765-threaded-exec-test.patch * fixup-gdb-archer-vla-tests.patch * fixup-gdb-base-gnu-ifunc-strstr-workaround-exp.patch * fixup-gdb-btrobust.patch * fixup-gdb-bz634108-solib_address.patch * fixup-gdb-dts-rhel6-python-compat.patch * fixup-gdb-gnat-dwarf-crash-3of3.patch * fixup-gdb-rhbz1156192-recursive-dlopen-test.patch * fixup-gdb-test-ivy-bridge.patch * fixup-gdb-vla-intel-fortran-vla-strings.patch * fixup-gdb-vla-intel-tests.patch - Obsoleted patches dropped: * amd64-linux-siginfo-include-order.patch * gdb-powerpc-remove-512-bytes-region-limit-if-2nd-dawr-is-avaliable.patch * gdb-support-dw-lle-start-end.patch * gdb-symtab-fix-infinite-recursion-in-dwarf2_cu-get_builder-again.patch * gdb-symtab-fix-language-of-frame-without-debug-info.patch * gdb-symtab-read-cu-base-address-for-enqueued-cu.patch * gdb-symtab-use-early-continue-in-find_pc_sect_compunit.patch * gdb-testsuite-catch-condition-evaluation-errors-in-gdb-assert.patch * gdb-testsuite-detect-gdb-prompt-after-monitor-exit.patch * gdb-testsuite-disable-selftests-for-factory.patch * gdb-testsuite-fix-control-flow-in-gdb-reverse-insn-reverse-exp.patch * gdb-testsuite-fix-failure-in-gdb-base-step-over-no-symbols-exp.patch * gdb-testsuite-fix-gdb-dlang-watch-loc-exp-on-ppc64.patch * gdb-testsuite-fix-gdb-reverse-insn-reverse-x86-c-for-m32.patch * gdb-testsuite-fix-gdb.arch-amd64-stap-three-arg-disp.s.patch * gdb-testsuite-fix-unset-of-debuginfod_urls-in-default_gdb_init.patch * gdb-testsuite-fix-xfail-handling-in-gdb.threads-gcore-thread.exp.patch * gdb-testsuite-gdb-base-morestack-exp-no-clang.patch * gdb-testsuite-gdb-tui-new-layout-exp-partly-require-tcl86.patch * gdb-testsuite-handle-sigill-in-gdb-reverse-insn-reverse-exp.patch * gdb-testsuite-ignore-debuginfod_urls.patch * gdb-testsuite-prevent-pagination-in-gdb-internalflags.patch * gdb-threads-fix-lin_thread_get_thread_signals-for-glibc-2.28.patch * gdb-try-to-load-libthread_db-only-after-reading-all-shared-libraries-when-attaching.patch * gdb-tui-fix-len_without_escapes-in-tui-disasm.c.patch - Fedora patches updated: * gdb-6.3-bz140532-ppc-unwinding-test.patch * gdb-6.3-bz202689-exec-from-pthread-test.patch * gdb-6.3-gstack-20050411.patch * gdb-6.3-inheritancetest-20050726.patch * gdb-6.3-mapping-zero-inode-test.patch * gdb-6.3-test-dtorfix-20050121.patch * gdb-6.3-test-movedir-20050125.patch * gdb-6.3-threaded-watchpoints2-20050225.patch * gdb-6.5-bz109921-DW_AT_decl_file-test.patch * gdb-6.5-bz185337-resolve-tls-without-debuginfo-v2.patch * gdb-6.5-bz218379-ppc-solib-trampoline-test.patch * gdb-6.5-bz243845-stale-testing-zombie-test.patch * gdb-6.5-ia64-libunwind-leak-test.patch * gdb-6.5-last-address-space-byte-test.patch * gdb-6.5-readline-long-line-crash-test.patch * gdb-6.5-section-num-fixup-test.patch * gdb-6.5-sharedlibrary-path.patch * gdb-6.6-buildid-locate-rpm-scl.patch * gdb-6.6-buildid-locate-rpm.patch * gdb-6.6-buildid-locate-solib-missing-ids.patch * gdb-6.6-buildid-locate.patch * gdb-6.6-bz229517-gcore-without-terminal.patch * gdb-6.6-bz230000-power6-disassembly-test.patch * gdb-6.6-bz237572-ppc-atomic-sequence-test.patch * gdb-6.8-bz442765-threaded-exec-test.patch * gdb-bz601887-dwarf4-rh-test.patch * gdb-bz634108-solib_address.patch * gdb-ccache-workaround.patch * gdb-container-rh-pkg.patch * gdb-fedora-libncursesw.patch * gdb-fortran-frame-string.patch * gdb-glibc-strstr-workaround.patch * gdb-lineno-makeup-test.patch * gdb-linux_perf-bundle.patch * gdb-physname-pr11734-test.patch * gdb-ppc-power7-test.patch * gdb-rhbz1156192-recursive-dlopen-test.patch * gdb-rhbz1261564-aarch64-hw-watchpoint-test.patch * gdb-rhbz1350436-type-printers-error.patch * gdb-rhbz1398387-tab-crash-test.patch * gdb-test-dw2-aranges.patch * gdb-test-ivy-bridge.patch * gdb-test-pid0-core.patch - Patches updated: * gdb-fix-selftest-fails-with-gdb-build-with-O2-flto.patch * gdb-gcore-bash.patch - Fedora patches added: * gdb-6.3-attach-see-vdso-test.patch * gdb-6.3-inferior-notification-20050721.patch * gdb-6.5-gcore-buffer-limit-test.patch * gdb-6.5-missed-trap-on-step-test.patch * gdb-rhbz1976887-field-location-kind.patch * gdb-rhbz2012976-paper-over-fortran-lex-problems.patch * gdb-test-for-rhbz1976887.patch - Fedora fixup patches added: * fixup-2-gdb-6.6-buildid-locate.patch * fixup-gdb-glibc-strstr-workaround.patch * fixup-gdb-linux_perf-bundle.patch * fixup-gdb-rhbz1325795-framefilters-test.patch * fixup-gdb-rhbz1553104-s390x-arch12-test.patch - Patches added: * fix-gdb.mi-new-ui-mi-sync.exp.patch * fix-gdb.threads-linux-dp.exp.patch * gdb-add-index.sh-fix-bashism.patch * gdb-build-add-cxx_dialect-to-cxx.patch * gdb-build-make-c-exp.y-work-with-bison-3.8.patch * gdb-python-finishbreakpoint-update.patch * gdb-symtab-add-call_site_eq-and-call_site_hash.patch * gdb-symtab-c-ify-call_site.patch * gdb-symtab-fix-htab_find_slot-call-in-read_call_site_scope.patch * gdb-symtab-remove-compunit_call_site_htab.patch * gdb-symtab-use-unrelocated-addresses-in-call_site.patch * gdb-testsuite-add-nopie-in-two-test-cases.patch * gdb-testsuite-fix-fail-in-gdb.base-annota1.exp.patch * gdb-testsuite-fix-fail-in-gdb.tui-corefile-run.exp.patch * gdb-testsuite-fix-gdb.base-dcache-flush.exp.patch * gdb-testsuite-fix-gdb.gdb-selftest.exp.patch * gdb-testsuite-fix-gdb.python-py-events.exp.patch * gdb-testsuite-fix-gdb.server-server-kill.exp-with-m32.patch * gdb-testsuite-fix-gdb.threads-check-libthread-db.exp-with-glibc-2.34.patch * gdb-testsuite-handle-supports_memtag-in-gdb.base-gdb-caching-proc.exp.patch * gdb-testsuite-prevent-compilation-fails-with-unix-fpie-pie.patch * gdb-testsuite-refactor-regexp-in-gdb.base-annota1.exp.patch * gdb-testsuite-support-fpie-fno-pie-pie-no-pie-in-gdb_compile_rust.patch * gdb-testsuite-use-compiler-generated-instead-of-gas-generated-stabs.patch - Fedora patches replaced: * fixup-gdb-6.6-buildid-locate.patch - Added maintenance scripts: * import-patches.sh * clean.sh OBS-URL: https://build.opensuse.org/request/show/927907 OBS-URL: https://build.opensuse.org/package/show/devel:gcc/gdb?expand=0&rev=289
454 lines
15 KiB
Diff
454 lines
15 KiB
Diff
[gdb/python] FinishBreakPoint update
|
|
|
|
I.
|
|
|
|
Consider the python gdb.FinishBreakpoint class, an extension of the
|
|
gdb.Breakpoint class.
|
|
|
|
It sets a temporary breakpoint on the return address of a frame.
|
|
|
|
This type of breakpoints is thread-specific.
|
|
|
|
II.
|
|
|
|
If the FinishBreakpoint is hit, it is deleted, and the method return_value
|
|
can be used to get the value returned by the function.
|
|
|
|
Let's demonstrate this:
|
|
...
|
|
$ cat -n test.c
|
|
1 int foo (int a) { return a + 2; }
|
|
2 int main () { return foo (1); }
|
|
$ gcc -g test.c
|
|
$ gdb -q -batch a.out -ex "set trace-commands on" -ex "tbreak foo" -ex run \
|
|
-ex "python bp = gdb.FinishBreakpoint()" -ex "info breakpoints" \
|
|
-ex continue -ex "info breakpoints" -ex "python print (bp.return_value)"
|
|
+tbreak foo
|
|
Temporary breakpoint 1 at 0x40049e: file test.c, line 1.
|
|
+run
|
|
|
|
Temporary breakpoint 1, foo (a=1) at test.c:1
|
|
1 int foo (int a) { return a + 2; }
|
|
+python bp = gdb.FinishBreakpoint()
|
|
Temporary breakpoint 2 at 0x4004b4: file test.c, line 2.
|
|
+info breakpoints
|
|
Num Type Disp Enb Address What
|
|
2 breakpoint del y 0x004004b4 in main at test.c:2 thread 1
|
|
stop only in thread 1
|
|
+continue
|
|
|
|
Temporary breakpoint 2, 0x004004b4 in main () at test.c:2
|
|
2 int main () { return foo (1); }
|
|
+info breakpoints
|
|
No breakpoints or watchpoints.
|
|
+python print (bp.return_value)
|
|
3
|
|
...
|
|
|
|
III.
|
|
|
|
Another possibility is that the FinishBreakpoint is not hit, because the
|
|
function did not terminate, f.i. because of longjmp, C++ exceptions, or GDB
|
|
return command.
|
|
|
|
Let's demonstrate this, using C++ exceptions:
|
|
...
|
|
$ cat -n test.c
|
|
1 int foo (int a) { throw 1; return a + 2; }
|
|
2 int main () {
|
|
3 try { return foo (1); } catch (...) {};
|
|
4 return 1;
|
|
5 }
|
|
$ g++ -g test.c
|
|
$ gdb -q -batch a.out -ex "set trace-commands on" -ex "tbreak foo" -ex run \
|
|
-ex "python bp = gdb.FinishBreakpoint()" -ex "info breakpoints" \
|
|
-ex continue -ex "info breakpoints" -ex "python print (bp.return_value)"
|
|
+tbreak foo
|
|
Temporary breakpoint 1 at 0x400712: file test.c, line 1.
|
|
+run
|
|
|
|
Temporary breakpoint 1, foo (a=1) at test.c:1
|
|
1 int foo (int a) { throw 1; return a + 2; }
|
|
+python bp = gdb.FinishBreakpoint()
|
|
Temporary breakpoint 2 at 0x400742: file test.c, line 3.
|
|
+info breakpoints
|
|
Num Type Disp Enb Address What
|
|
2 breakpoint del y 0x00400742 in main() at test.c:3 thread 1
|
|
stop only in thread 1
|
|
+continue
|
|
[Inferior 1 (process 25269) exited with code 01]
|
|
Thread-specific breakpoint 2 deleted - thread 1 no longer in the thread list.
|
|
+info breakpoints
|
|
No breakpoints or watchpoints.
|
|
+python print (bp.return_value)
|
|
None
|
|
...
|
|
|
|
Indeed, we do not hit the FinishBreakpoint. Instead, it's deleted when the
|
|
thread disappears, like any other thread-specific breakpoint.
|
|
|
|
I think this is a bug: the deletion is meant to be handled by FinishBreakpoint
|
|
itself.
|
|
|
|
IV.
|
|
|
|
Fix aforementioned bug by:
|
|
- adding an observer of the thread_exit event in FinishBreakpoint
|
|
- making sure that this observer is called before the
|
|
remove_threaded_breakpoints observer of that same event.
|
|
|
|
This changes the behaviour to:
|
|
...
|
|
+continue
|
|
[Inferior 1 (process 30256) exited with code 01]
|
|
+info breakpoints
|
|
No breakpoints or watchpoints.
|
|
+python print (bp.return_value)
|
|
None
|
|
...
|
|
|
|
V.
|
|
|
|
An out_of_scope callback can be defined to make this more verbose:
|
|
...
|
|
$ cat fbp.py
|
|
class FBP(gdb.FinishBreakpoint):
|
|
def __init__(self):
|
|
gdb.FinishBreakpoint.__init__(self)
|
|
|
|
def out_of_scope(self):
|
|
try:
|
|
frame = gdb.selected_frame ()
|
|
sal = frame.find_sal ()
|
|
print ("out_of_scope triggered at %s:%s"
|
|
% (sal.symtab.fullname(), sal.line))
|
|
except gdb.error as e:
|
|
print ("out_of_scope triggered at thread/inferior exit")
|
|
...
|
|
and using that gets us:
|
|
...
|
|
+continue
|
|
[Inferior 1 (process 30742) exited with code 01]
|
|
out_of_scope triggered at thread/inferior exit
|
|
+info breakpoints
|
|
No breakpoints or watchpoints.
|
|
+python print (bp.return_value)
|
|
None
|
|
...
|
|
|
|
VI.
|
|
|
|
This out_of_scope event can be triggered earlier than inferior/thread exit.
|
|
|
|
Let's demonstrate this:
|
|
...
|
|
$ cat -n test.c
|
|
1 int bar (int a) { throw 1; return a + 2; }
|
|
2 int foo (int a) {
|
|
3 int res = a;
|
|
4 try
|
|
5 {
|
|
6 res += bar (1);
|
|
7 }
|
|
8 catch (...)
|
|
9 {
|
|
10 }
|
|
11 return res;
|
|
12 }
|
|
13 int main () {
|
|
14 int res = 0;
|
|
15 res += foo (1);
|
|
16 res += 2;
|
|
17 return res * 2;
|
|
18 }
|
|
$ g++ -g test.c
|
|
$ gdb -q -batch -ex "source fbp.py" a.out -ex "set trace-commands on" \
|
|
-ex "tbreak bar" -ex run -ex "python bp = FBP()" -ex "info breakpoints" \
|
|
-ex "tbreak 16" -ex continue -ex "info breakpoints" \
|
|
-ex "python print (bp.return_value)"
|
|
+tbreak bar
|
|
Temporary breakpoint 1 at 0x400712: file test.c, line 1.
|
|
+run
|
|
|
|
Temporary breakpoint 1, bar (a=1) at test.c:1
|
|
1 int bar (int a) { throw 1; return a + 2; }
|
|
+python bp = FBP()
|
|
Temporary breakpoint 2 at 0x40074f: file test.c, line 6.
|
|
+info breakpoints
|
|
Num Type Disp Enb Address What
|
|
2 breakpoint del y 0x0040074f in foo(int) at test.c:6 thread 1
|
|
stop only in thread 1
|
|
+tbreak 16
|
|
Temporary breakpoint 3 at 0x400784: file test.c, line 16.
|
|
+continue
|
|
|
|
Temporary breakpoint 3, main () at test.c:16
|
|
16 res += 2;
|
|
out_of_scope triggered at /home/vries/gdb_versions/devel/test.c:16
|
|
+info breakpoints
|
|
No breakpoints or watchpoints.
|
|
+python print (bp.return_value)
|
|
None
|
|
...
|
|
|
|
Note that the out_of_scope event triggers at the breakpoint we set at
|
|
test.c:16. If we'd set that breakpoint at line 17, the out_of_scope event
|
|
would trigger at line 17 instead.
|
|
|
|
Also note that it can't be triggered earlier, say by setting the breakpoint in
|
|
foo at line 11. We would get instead "out_of_scope triggered at
|
|
thread/inferior exit".
|
|
|
|
VII.
|
|
|
|
Now consider a reduced version of
|
|
src/gdb/testsuite/gdb.python/py-finish-breakpoint2.cc:
|
|
...
|
|
$ cat -n test.c
|
|
1 #include <iostream>
|
|
2
|
|
3 void
|
|
4 throw_exception_1 (int e)
|
|
5 {
|
|
6 throw new int (e);
|
|
7 }
|
|
8
|
|
9 int
|
|
10 main (void)
|
|
11 {
|
|
12 int i;
|
|
13 try
|
|
14 {
|
|
15 throw_exception_1 (10);
|
|
16 }
|
|
17 catch (const int *e)
|
|
18 {
|
|
19 std::cerr << "Exception #" << *e << std::endl;
|
|
20 }
|
|
21 i += 1;
|
|
22
|
|
23 return i;
|
|
24 }
|
|
$ g++ -g test.c
|
|
...
|
|
|
|
Now let's try to see if the FinishBreakPoint triggers:
|
|
...
|
|
$ gdb -q -batch -ex "source fbp.py" a.out -ex "set trace-commands on" \
|
|
-ex "tbreak throw_exception_1" -ex run -ex "python bp = FBP()" \
|
|
-ex "info breakpoints" -ex continue -ex "info breakpoints" \
|
|
-ex "python print (bp.return_value)"
|
|
+tbreak throw_exception_1
|
|
Temporary breakpoint 1 at 0x400bd5: file test.c, line 6.
|
|
+run
|
|
|
|
Temporary breakpoint 1, throw_exception_1 (e=10) at test.c:6
|
|
6 throw new int (e);
|
|
+python bp = FBP()
|
|
Temporary breakpoint 2 at 0x400c2f: file test.c, line 21.
|
|
+info breakpoints
|
|
Num Type Disp Enb Address What
|
|
2 breakpoint del y 0x00400c2f in main() at test.c:21 thread 1
|
|
stop only in thread 1
|
|
+continue
|
|
Exception #10
|
|
|
|
Temporary breakpoint 2, main () at test.c:21
|
|
21 i += 1;
|
|
+info breakpoints
|
|
No breakpoints or watchpoints.
|
|
+python print (bp.return_value)
|
|
None
|
|
...
|
|
|
|
Surprisingly, it did. The explanation is that FinishBreakPoint is really a
|
|
frame-return-address breakpoint, and that address happens to be at line 21,
|
|
which is still executed after the throw in throw_exception_1.
|
|
|
|
Interestingly, with -m32 the FinishBreakPoint doesn't trigger, because the
|
|
frame-return-address happens to be an instruction which is part of line 15.
|
|
|
|
VIII.
|
|
|
|
In conclusion, the FinishBreakpoint is a frame-return-address breakpoint.
|
|
|
|
After being set, either:
|
|
- it triggers, or
|
|
- an out-of-scope event will be generated.
|
|
|
|
If an out-of-scope event is generated, it will be due to incomplete function
|
|
termination.
|
|
|
|
OTOH, incomplete function termination does not guarantee an out-of-scope event
|
|
instead of hitting the breakpoint.
|
|
|
|
IX.
|
|
|
|
The documentation states that 'A finish breakpoint is a temporary breakpoint
|
|
set at the return address of a frame, based on the finish command'.
|
|
|
|
It's indeed somewhat similar to the finish command, at least in the sense that
|
|
both may stop at the frame-return-address.
|
|
|
|
But the finish command can accurately detect function termination.
|
|
|
|
And the finish command will stop at any other address that is the first
|
|
address not in the original function.
|
|
|
|
The documentation needs updating to accurately describe what it does.
|
|
|
|
X.
|
|
|
|
A better implementation of a finish breakpoint would be one that borrows from
|
|
the finish command implementation. That one:
|
|
- installs a thread_fsm, and
|
|
- continues
|
|
|
|
A finish breakpoint would do the same minus the continue, but it requires gdb
|
|
to handle multiple thread_fsms at a time (because other commands may wish to
|
|
install their own thread_fsm), which AFAICT is not supported yet.
|
|
|
|
XI.
|
|
|
|
This patch repairs a minor part of the functionality, and updates
|
|
documentation and test-cases to match actual behaviour.
|
|
|
|
The question remains how useful the functionality is, as it is now
|
|
( see f.i. discussion at
|
|
https://sourceware.org/pipermail/gdb-patches/2021-January/175290.html ).
|
|
Perhaps it would be better to deprecate this in a follow-up patch in some form
|
|
or another, say by disabling it by default and introducing a maintenance
|
|
command that switches it on, with the warning that it is deprecated.
|
|
|
|
Tested on x86_64-linux with native and target board unix/-m32, by rebuilding
|
|
and running the test-cases:
|
|
- gdb.python/py-finish-breakpoint.exp
|
|
- gdb.python/py-finish-breakpoint2.exp
|
|
|
|
---
|
|
gdb/breakpoint.c | 10 ++++++++++
|
|
gdb/doc/python.texi | 6 ++++--
|
|
gdb/python/py-finishbreakpoint.c | 21 +++++++++++++++++++++
|
|
gdb/testsuite/gdb.python/py-finish-breakpoint2.exp | 16 +++++++++++++---
|
|
4 files changed, 48 insertions(+), 5 deletions(-)
|
|
|
|
diff --git a/gdb/breakpoint.c b/gdb/breakpoint.c
|
|
index dbbea6b8bff..64a9a3d394f 100644
|
|
--- a/gdb/breakpoint.c
|
|
+++ b/gdb/breakpoint.c
|
|
@@ -15452,6 +15452,10 @@ initialize_breakpoint_ops (void)
|
|
|
|
static struct cmd_list_element *enablebreaklist = NULL;
|
|
|
|
+#if HAVE_PYTHON
|
|
+extern gdb::observers::token bpfinishpy_handle_thread_exit_observer_token;
|
|
+#endif
|
|
+
|
|
/* See breakpoint.h. */
|
|
|
|
cmd_list_element *commands_cmd_element = nullptr;
|
|
@@ -16065,6 +16069,12 @@ This is useful for formatted output in user-defined commands."));
|
|
|
|
gdb::observers::about_to_proceed.attach (breakpoint_about_to_proceed,
|
|
"breakpoint");
|
|
+#if HAVE_PYTHON
|
|
+ gdb::observers::thread_exit.attach
|
|
+ (remove_threaded_breakpoints, "breakpoint",
|
|
+ { &bpfinishpy_handle_thread_exit_observer_token });
|
|
+#else
|
|
gdb::observers::thread_exit.attach (remove_threaded_breakpoints,
|
|
"breakpoint");
|
|
+#endif
|
|
}
|
|
diff --git a/gdb/doc/python.texi b/gdb/doc/python.texi
|
|
index f4865b3d6a6..17a67800ba2 100644
|
|
--- a/gdb/doc/python.texi
|
|
+++ b/gdb/doc/python.texi
|
|
@@ -5698,7 +5698,7 @@ attribute is @code{None}. This attribute is writable.
|
|
@tindex gdb.FinishBreakpoint
|
|
|
|
A finish breakpoint is a temporary breakpoint set at the return address of
|
|
-a frame, based on the @code{finish} command. @code{gdb.FinishBreakpoint}
|
|
+a frame. @code{gdb.FinishBreakpoint}
|
|
extends @code{gdb.Breakpoint}. The underlying breakpoint will be disabled
|
|
and deleted when the execution will run out of the breakpoint scope (i.e.@:
|
|
@code{Breakpoint.stop} or @code{FinishBreakpoint.out_of_scope} triggered).
|
|
@@ -5717,7 +5717,9 @@ details about this argument.
|
|
In some circumstances (e.g.@: @code{longjmp}, C@t{++} exceptions, @value{GDBN}
|
|
@code{return} command, @dots{}), a function may not properly terminate, and
|
|
thus never hit the finish breakpoint. When @value{GDBN} notices such a
|
|
-situation, the @code{out_of_scope} callback will be triggered.
|
|
+situation, the @code{out_of_scope} callback will be triggered. Note
|
|
+though that improper function termination does not guarantee that the
|
|
+finish breakpoint is not hit.
|
|
|
|
You may want to sub-class @code{gdb.FinishBreakpoint} and override this
|
|
method:
|
|
diff --git a/gdb/python/py-finishbreakpoint.c b/gdb/python/py-finishbreakpoint.c
|
|
index 1d8373d807e..a881103fdad 100644
|
|
--- a/gdb/python/py-finishbreakpoint.c
|
|
+++ b/gdb/python/py-finishbreakpoint.c
|
|
@@ -398,6 +398,24 @@ bpfinishpy_handle_exit (struct inferior *inf)
|
|
bpfinishpy_detect_out_scope_cb (bp, nullptr);
|
|
}
|
|
|
|
+/* Attached to `thread_exit' notifications, triggers all the necessary out of
|
|
+ scope notifications. */
|
|
+
|
|
+static void
|
|
+bpfinishpy_handle_thread_exit (struct thread_info *tp, int ignore)
|
|
+{
|
|
+ gdbpy_enter enter_py (target_gdbarch (), current_language);
|
|
+
|
|
+ for (breakpoint *bp : all_breakpoints_safe ())
|
|
+ {
|
|
+ if (tp->global_num == bp->thread)
|
|
+ bpfinishpy_detect_out_scope_cb (bp, nullptr);
|
|
+ }
|
|
+}
|
|
+
|
|
+extern gdb::observers::token bpfinishpy_handle_thread_exit_observer_token;
|
|
+gdb::observers::token bpfinishpy_handle_thread_exit_observer_token;
|
|
+
|
|
/* Initialize the Python finish breakpoint code. */
|
|
|
|
int
|
|
@@ -414,6 +432,9 @@ gdbpy_initialize_finishbreakpoints (void)
|
|
"py-finishbreakpoint");
|
|
gdb::observers::inferior_exit.attach (bpfinishpy_handle_exit,
|
|
"py-finishbreakpoint");
|
|
+ gdb::observers::thread_exit.attach
|
|
+ (bpfinishpy_handle_thread_exit,
|
|
+ bpfinishpy_handle_thread_exit_observer_token, "py-finishbreakpoint");
|
|
|
|
return 0;
|
|
}
|
|
diff --git a/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
|
|
index 58e086ad3b4..46c39d0d108 100644
|
|
--- a/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
|
|
+++ b/gdb/testsuite/gdb.python/py-finish-breakpoint2.exp
|
|
@@ -50,10 +50,20 @@ gdb_test "continue" "Breakpoint .*throw_exception_1.*" "run to exception 1"
|
|
gdb_test "python print (len(gdb.breakpoints()))" "3" "check BP count"
|
|
gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" \
|
|
"init ExceptionFinishBreakpoint" "set FinishBP after the exception"
|
|
-gdb_test "continue" ".*stopped at ExceptionFinishBreakpoint.*" "check FinishBreakpoint in catch()"
|
|
-gdb_test "python print (len(gdb.breakpoints()))" "3" "check finish BP removal"
|
|
|
|
-gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" "continue to second exception"
|
|
+gdb_test_multiple "continue" "continue after setting FinishBreakpoint" {
|
|
+ -re -wrap ".*stopped at ExceptionFinishBreakpoint.*" {
|
|
+ pass "$gdb_test_name (scenario 1, triggered)"
|
|
+ gdb_test "python print (len(gdb.breakpoints()))" "3" \
|
|
+ "check finish BP removal"
|
|
+ gdb_test "continue" ".*Breakpoint.* throw_exception_1.*" \
|
|
+ "continue to second exception"
|
|
+ }
|
|
+ -re -wrap ".*Breakpoint.* throw_exception_1.*" {
|
|
+ pass "$gdb_test_name (scenario 2, not triggered)"
|
|
+ }
|
|
+}
|
|
+
|
|
gdb_test "python ExceptionFinishBreakpoint(gdb.newest_frame())" \
|
|
"init ExceptionFinishBreakpoint" "set FinishBP after the exception again"
|
|
gdb_test "continue" ".*exception did not finish.*" "FinishBreakpoint with exception thrown not caught"
|