# support in Python.
require isnative
+require {!is_remote host}
load_lib gdb-python.exp
return
}
+set remote_python_file \
+ [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
+
set corefile [core_find $binfile]
if {$corefile == ""} {
unsupported "couldn't create or find corefile"
return
}
+# Helper proc to run the 'core-file' command. Takes optional arguments:
+#
+# -corefile FILENAME : Load FILENAME as the new core file. If this
+# argument is not given then the current core
+# file will be unloaded.
+#
+# -inferior NUM : The inferior in which the corefile is being changed.
+# This is used to match the corefile_changed events
+# that will be emitted.
+#
+# -prefix STRING : A test prefix, to make test names unique.
+#
+# -replacement : There's already a core file loaded when this command
+# is being run.
+proc core_file_cmd { args } {
+ parse_some_args {
+ {corefile ""}
+ {inferior 1}
+ {prefix ""}
+ {replacement}
+ }
+
+ if { $prefix eq "" } {
+ if { $corefile eq "" } {
+ set prefix "unload corefile"
+ } else {
+ set prefix "load corefile"
+ }
+ }
+
+ with_test_prefix $prefix {
+ gdb_test "events corefile_changed check" \
+ "^No corefile_changed event has been seen\\." \
+ "no corefile event has been seen"
+
+ gdb_test "events exited check" \
+ "^No exited event has been seen\\." \
+ "no exited event has been seen"
+
+ if { $corefile eq "" } {
+ gdb_test "core-file" "^No core file now\\." "unload current core file"
+
+ gdb_test "events corefile_changed check" \
+ "Event 1/1, Inferior $inferior, Corefile None" \
+ "expected corefile event has been seen"
+
+ gdb_test "events exited check" \
+ "Event 1/1, Inferior $inferior, Exit Code None" \
+ "expected exited event has been seen"
+ } else {
+ gdb_test "core-file $corefile" ".*" "load core file"
+
+ if { $replacement } {
+ gdb_test "events corefile_changed check" \
+ [multi_line \
+ "Event 1/2, Inferior $inferior, Corefile None" \
+ "Event 2/2, Inferior $inferior, Corefile [string_to_regexp $corefile]"] \
+ "expected corefile event has been seen"
+
+ gdb_test "events exited check" \
+ "Event 1/1, Inferior $inferior, Exit Code None" \
+ "expected exited event has been seen"
+ } else {
+ gdb_test "events corefile_changed check" \
+ "Event 1/1, Inferior $inferior, Corefile [string_to_regexp $corefile]" \
+ "expected corefile event has been seen"
+
+ gdb_test "events exited check" \
+ "^No exited event has been seen\\." \
+ "no exited event was emitted"
+ }
+ }
+ }
+
+ gdb_test_no_output -nopass "events corefile_changed reset"
+ gdb_test_no_output -nopass "events exited reset"
+}
+
+# A helper proc runs clean_restart passing through ARGS, and then loads the
+# test's Python script.
+proc clean_restart_and_load_py_script { args } {
+ clean_restart {*}$args
+
+ # Load the Python script into GDB.
+ gdb_test "source $::remote_python_file" "^Success" \
+ "source python script"
+}
+
# Create a copy of the corefile.
set other_corefile [standard_output_file ${testfile}-other.core]
remote_exec build "cp $corefile $other_corefile"
-clean_restart
+clean_restart_and_load_py_script
gdb_test_no_output "python inf = gdb.selected_inferior()" \
"capture current inferior"
gdb_test "python print(inf.corefile)" "^None" \
"Inferior.corefile is None before loading a core file"
-gdb_test "core-file $corefile" ".*" \
- "load core file"
+core_file_cmd -corefile $corefile
set file_re [string_to_regexp $corefile]
gdb_test "python print(inf.corefile)" "^<gdb\\.Corefile inferior=1 filename='$file_re'>" \
gdb_test "python print(core1.is_valid())" "^True" \
"Corefile.is_valid() is True while corefile is loaded"
-gdb_test "core-file" "^No core file now\\." "unload current core file"
+core_file_cmd
gdb_test "python print(core1.is_valid())" "^False" \
"Corefile.is_valid() is False after corefile is unloaded"
gdb_test "inferior 2"
with_test_prefix "in second inferior" {
- gdb_test "core-file $corefile" ".*" \
- "load core file"
+ core_file_cmd -corefile $corefile -inferior 2
gdb_test "python print(inf.corefile)" "^None" \
"first inferior still has no core file"
"Corefile.filename attribute works from different progspace"
# Load the other corefile into the first inferior.
-gdb_test "core $other_corefile" ".*" \
- "load other corefile into inferior 1"
+core_file_cmd -corefile $other_corefile \
+ -prefix "load other corefile into inferior 1"
# Delete the second inferior. We need to switch to the second
# inferior and unload its corefile before we can do that. Then,
"AttributeError.*: 'gdb\\.Corefile' object has no attribute '_my_attribute'" \
"try to read attribute that doesn't exist"
- gdb_test "core-file"
+ core_file_cmd -inferior 2
gdb_test "python print(core2.filename)" \
[multi_line \
# mapped_files API. The output from the built-in command, and the
# Python command should be identical.
with_test_prefix "test mapped files data" {
- clean_restart
-
- set remote_python_file \
- [gdb_remote_download host ${srcdir}/${subdir}/${testfile}.py]
-
- # Load the Python script into GDB.
- gdb_test "source $remote_python_file" "^Success" \
- "source python script"
+ clean_restart_and_load_py_script
# Load the core file.
- gdb_test "core-file $corefile" ".*" \
- "load core file"
+ core_file_cmd -corefile $corefile
# Two files to write the output to.
set out_1 [standard_output_file ${gdb_test_file_name}-out-1.txt]
gdb_test "python regions\[0\] = None" \
"'tuple' object does not support item assignment"
}
+
+# Load a core file. GDB should figure out which file is being debugged.
+# Then use 'start' to run this executable, this will replace the core file
+# target. At least on Linux, this replacement is done without calling
+# target_detach. This test checks that the expected core file changed and
+# inferior exited events are still seen.
+with_test_prefix "start from corefile" {
+ if { [gdb_protocol_is_native] } {
+ clean_restart_and_load_py_script
+
+ # Load the core file.
+ core_file_cmd -corefile $corefile
+
+ # Check GDB figured out the executable.
+ gdb_test "info inferiors 1" \
+ [multi_line \
+ "\[^\r\n\]+[string_to_regexp $binfile]\\s*" \
+ "\[^\r\n\]+[string_to_regexp $corefile]\\s*"] \
+ "check executable was detected correctly"
+
+ gdb_test "start" \
+ "Temporary breakpoint $::decimal, main \\(\\).*" \
+
+ gdb_test "events corefile_changed check" \
+ "Event 1/1, Inferior 1, Corefile None" \
+ "expected corefile event has been seen"
+
+ gdb_test "events exited check" \
+ "Event 1/1, Inferior 1, Exit Code None" \
+ "expected exited event has been seen"
+
+ gdb_test_no_output -nopass "events corefile_changed reset"
+ gdb_test_no_output -nopass "events exited reset"
+ }
+}
+
+# Load a core file, then load a different core file to replace it.
+# Check that the events that are emitted are as expected.
+with_test_prefix "load one core file over another" {
+ clean_restart_and_load_py_script
+
+ # Load the core file.
+ core_file_cmd -corefile $corefile \
+ -prefix "load first corefile"
+
+ core_file_cmd -corefile $other_corefile \
+ -prefix "load second corefile" \
+ -replacement
+}
CheckMainExec()
+# An 'events' prefix command.
+class events_cmd(gdb.Command):
+ """Information about recent Python events."""
+
+ def __init__(self):
+ gdb.Command.__init__(self, "events", gdb.COMMAND_USER, prefix=True)
+
+
+# An 'events corefile_changed' sub-command.
+class events_corefile_changed_cmd(gdb.Command):
+ """Check recent corefile_changed events.
+
+ Requires a single argument either 'check' or 'reset'. With
+ 'check', print details of every recent corefile_changed event.
+ With 'reset' clear the list of recent corefile_changed events."""
+
+ def __init__(self):
+ gdb.Command.__init__(self, "events corefile_changed", gdb.COMMAND_USER)
+ self._events = []
+ gdb.events.corefile_changed.connect(lambda e: self._corefile_changed_handler(e))
+
+ def _corefile_changed_handler(self, event):
+ assert isinstance(event, gdb.CorefileChangedEvent)
+ inf = event.inferior
+ assert isinstance(inf, gdb.Inferior)
+
+ corefile = inf.corefile
+ if corefile is not None:
+ assert corefile.is_valid()
+ corefile = corefile.filename
+
+ obj = {"inferior": inf.num, "corefile": corefile}
+ self._events.append(obj)
+
+ def invoke(self, args, from_tty):
+ if args == "check":
+ if len(self._events) == 0:
+ print("No corefile_changed event has been seen.")
+ else:
+ total = len(self._events)
+ for idx, obj in enumerate(self._events, start=1):
+ inf_num = obj["inferior"]
+ corefile = obj["corefile"]
+
+ if corefile is None:
+ msg = "None"
+ else:
+ msg = corefile
+
+ print(
+ "Event {}/{}, Inferior {}, Corefile {}".format(
+ idx, total, inf_num, msg
+ )
+ )
+ elif args == "reset":
+ self._events = []
+ else:
+ raise gdb.GdbError("Unknown command args: {}".format(args))
+
+
+# An 'events exited' sub-command.
+class events_exited_cmd(gdb.Command):
+ """Check recent exited events.
+
+ Requires a single argument either 'check' or 'reset'. With
+ 'check', print details of every recent exited event. With 'reset'
+ clear the list of recent exited events."""
+
+ def __init__(self):
+ gdb.Command.__init__(self, "events exited", gdb.COMMAND_USER)
+ self._events = []
+ gdb.events.exited.connect(lambda e: self._exited_handler(e))
+
+ def _exited_handler(self, event):
+ assert isinstance(event, gdb.ExitedEvent)
+ inf = event.inferior
+ assert isinstance(inf, gdb.Inferior)
+
+ if hasattr(event, "exit_code"):
+ assert isinstance(event.exit_code, int)
+ exit_code = event.exit_code
+ else:
+ exit_code = None
+
+ obj = {"inferior": inf.num, "exit_code": exit_code}
+ self._events.append(obj)
+
+ def invoke(self, args, from_tty):
+ if args == "check":
+ if len(self._events) == 0:
+ print("No exited event has been seen.")
+ else:
+ total = len(self._events)
+ for idx, obj in enumerate(self._events, start=1):
+ inf_num = obj["inferior"]
+ exit_code = obj["exit_code"]
+
+ if exit_code is None:
+ msg = "None"
+ else:
+ msg = exit_code
+
+ print(
+ "Event {}/{}, Inferior {}, Exit Code {}".format(
+ idx, total, inf_num, msg
+ )
+ )
+ elif args == "reset":
+ self._events = []
+ else:
+ raise gdb.GdbError("Unknown command args: {}".format(args))
+
+
+events_cmd()
+events_corefile_changed_cmd()
+events_exited_cmd()
+
print("Success")