mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-05-28 14:08:09 +08:00

This commit changes GDB to track thread numbers per-inferior. Then, if you're debugging multiple inferiors, GDB displays "inferior-num.thread-num" instead of just "thread-num" whenever it needs to display a thread: (gdb) info inferiors Num Description Executable 1 process 6022 /home/pedro/gdb/tests/threads * 2 process 6037 /home/pedro/gdb/tests/threads (gdb) info threads Id Target Id Frame 1.1 Thread 0x7ffff7fc2740 (LWP 6022) "threads" (running) 1.2 Thread 0x7ffff77c0700 (LWP 6028) "threads" (running) 1.3 Thread 0x7ffff7fc2740 (LWP 6032) "threads" (running) 2.1 Thread 0x7ffff7fc1700 (LWP 6037) "threads" (running) 2.2 Thread 0x7ffff77c0700 (LWP 6038) "threads" (running) * 2.3 Thread 0x7ffff7fc2740 (LWP 6039) "threads" (running) (gdb) ... (gdb) thread 1.1 [Switching to thread 1.1 (Thread 0x7ffff7fc2740 (LWP 8155))] (gdb) ... etc. You can still use "thread NUM", in which case GDB infers you're referring to thread NUM of the current inferior. The $_thread convenience var and Python's InferiorThread.num attribute are remapped to the new per-inferior thread number. It's a backward compatibility break, but since it only matters when debugging multiple inferiors, I think it's worth doing. Because MI thread IDs need to be a single integer, we keep giving threads a global identifier, _in addition_ to the per-inferior number, and make MI always refer to the global thread IDs. IOW, nothing changes from a MI frontend's perspective. Similarly, since Python's Breakpoint.thread and Guile's breakpoint-thread/set-breakpoint-thread breakpoint methods need to work with integers, those are adjusted to work with global thread IDs too. Follow up patches will provide convenient means to access threads' global IDs. To avoid potencially confusing users (which also avoids updating much of the testsuite), if there's only one inferior and its ID is "1", IOW, the user hasn't done anything multi-process/inferior related, then the "INF." part of thread IDs is not shown. E.g,.: (gdb) info inferiors Num Description Executable * 1 process 15275 /home/pedro/gdb/tests/threads (gdb) info threads Id Target Id Frame * 1 Thread 0x7ffff7fc1740 (LWP 15275) "threads" main () at threads.c:40 (gdb) add-inferior Added inferior 2 (gdb) info threads Id Target Id Frame * 1.1 Thread 0x7ffff7fc1740 (LWP 15275) "threads" main () at threads.c:40 (gdb) No regressions on x86_64 Fedora 20. gdb/ChangeLog: 2016-01-13 Pedro Alves <palves@redhat.com> * NEWS: Mention that thread IDs are now per inferior and global thread IDs. * Makefile.in (SFILES): Add tid-parse.c. (COMMON_OBS): Add tid-parse.o. (HFILES_NO_SRCDIR): Add tid-parse.h. * ada-tasks.c: Adjust to use ptid_to_global_thread_id. * breakpoint.c (insert_breakpoint_locations) (remove_threaded_breakpoints, bpstat_check_breakpoint_conditions) (print_one_breakpoint_location, set_longjmp_breakpoint) (check_longjmp_breakpoint_for_call_dummy) (set_momentary_breakpoint): Adjust to use global IDs. (find_condition_and_thread, watch_command_1): Use parse_thread_id. (until_break_command, longjmp_bkpt_dtor) (breakpoint_re_set_thread, insert_single_step_breakpoint): Adjust to use global IDs. * dummy-frame.c (pop_dummy_frame_bpt): Adjust to use ptid_to_global_thread_id. * elfread.c (elf_gnu_ifunc_resolver_stop): Likewise. * gdbthread.h (struct thread_info): Rename field 'num' to 'global_num. Add new fields 'per_inf_num' and 'inf'. (thread_id_to_pid): Rename thread_id_to_pid to global_thread_id_to_ptid. (pid_to_thread_id): Rename to ... (ptid_to_global_thread_id): ... this. (valid_thread_id): Rename to ... (valid_global_thread_id): ... this. (find_thread_id): Rename to ... (find_thread_global_id): ... this. (ALL_THREADS, ALL_THREADS_BY_INFERIOR): Declare. (print_thread_info): Add comment. * tid-parse.h: New file. * tid-parse.c: New file. * infcmd.c (step_command_fsm_prepare) (step_command_fsm_should_stop): Adjust to use the global thread ID. (until_next_command, until_next_command) (finish_command_fsm_should_stop): Adjust to use the global thread ID. (attach_post_wait): Adjust to check the inferior number too. * inferior.h (struct inferior) <highest_thread_num>: New field. * infrun.c (handle_signal_stop) (insert_exception_resume_breakpoint) (insert_exception_resume_from_probe): Adjust to use the global thread ID. * record-btrace.c (record_btrace_open): Use global thread IDs. * remote.c (process_initial_stop_replies): Also consider the inferior number. * target.c (target_pre_inferior): Clear the inferior's highest thread num. * thread.c (clear_thread_inferior_resources): Adjust to use the global thread ID. (new_thread): New inferior parameter. Adjust to use it. Set both the thread's global ID and the thread's per-inferior ID. (add_thread_silent): Adjust. (find_thread_global_id): New. (find_thread_id): Make static. Adjust to rename. (valid_thread_id): Rename to ... (valid_global_thread_id): ... this. (pid_to_thread_id): Rename to ... (ptid_to_global_thread_id): ... this. (thread_id_to_pid): Rename to ... (global_thread_id_to_ptid): ... this. Adjust. (first_thread_of_process): Adjust. (do_captured_list_thread_ids): Adjust to use global thread IDs. (should_print_thread): New function. (print_thread_info): Rename to ... (print_thread_info_1): ... this, and add new show_global_ids parameter. Handle it. Iterate over inferiors. (print_thread_info): Reimplement as wrapper around print_thread_info_1. (show_inferior_qualified_tids): New function. (print_thread_id): Use it. (tp_array_compar): Compare inferior numbers too. (thread_apply_command): Use tid_range_parser. (do_captured_thread_select): Use parse_thread_id. (thread_id_make_value): Adjust. (_initialize_thread): Adjust "info threads" help string. * varobj.c (struct varobj_root): Update comment. (varobj_create): Adjust to use global thread IDs. (value_of_root_1): Adjust to use global_thread_id_to_ptid. * windows-tdep.c (display_tib): No longer accept an argument. * cli/cli-utils.c (get_number_trailer): Make extern. * cli/cli-utils.h (get_number_trailer): Declare. (get_number_const): Adjust documentation. * mi/mi-cmd-var.c (mi_cmd_var_update_iter): Adjust to use global thread IDs. * mi/mi-interp.c (mi_new_thread, mi_thread_exit) (mi_on_normal_stop, mi_output_running_pid, mi_on_resume): * mi/mi-main.c (mi_execute_command, mi_cmd_execute): Likewise. * guile/scm-breakpoint.c (gdbscm_set_breakpoint_thread_x): Likewise. * python/py-breakpoint.c (bppy_set_thread): Likewise. * python/py-finishbreakpoint.c (bpfinishpy_init): Likewise. * python/py-infthread.c (thpy_get_num): Add comment and return the per-inferior thread ID. (thread_object_getset): Update comment of "num". gdb/testsuite/ChangeLog: 2016-01-07 Pedro Alves <palves@redhat.com> * gdb.base/break.exp: Adjust to output changes. * gdb.base/hbreak2.exp: Likewise. * gdb.base/sepdebug.exp: Likewise. * gdb.base/watch_thread_num.exp: Likewise. * gdb.linespec/keywords.exp: Likewise. * gdb.multi/info-threads.exp: Likewise. * gdb.threads/thread-find.exp: Likewise. * gdb.multi/tids.c: New file. * gdb.multi/tids.exp: New file. gdb/doc/ChangeLog: 2016-01-07 Pedro Alves <palves@redhat.com> * gdb.texinfo (Threads): Document per-inferior thread IDs, qualified thread IDs, global thread IDs and thread ID lists. (Set Watchpoints, Thread-Specific Breakpoints): Adjust to refer to thread IDs. (Convenience Vars): Document the $_thread convenience variable. (Ada Tasks): Adjust to refer to thread IDs. (GDB/MI Async Records, GDB/MI Thread Commands, GDB/MI Ada Tasking Commands, GDB/MI Variable Objects): Update to mention global thread IDs. * guile.texi (Breakpoints In Guile) <breakpoint-thread/set-breakpoint-thread breakpoint>: Mention global thread IDs instead of thread IDs. * python.texi (Threads In Python): Adjust documentation of InferiorThread.num. (Breakpoint.thread): Mention global thread IDs instead of thread IDs.
486 lines
15 KiB
C
486 lines
15 KiB
C
/* Python interface to finish breakpoints
|
|
|
|
Copyright (C) 2011-2016 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/>. */
|
|
|
|
|
|
|
|
#include "defs.h"
|
|
#include "python-internal.h"
|
|
#include "breakpoint.h"
|
|
#include "frame.h"
|
|
#include "gdbthread.h"
|
|
#include "arch-utils.h"
|
|
#include "language.h"
|
|
#include "observer.h"
|
|
#include "inferior.h"
|
|
#include "block.h"
|
|
#include "location.h"
|
|
|
|
/* Function that is called when a Python finish bp is found out of scope. */
|
|
static char * const outofscope_func = "out_of_scope";
|
|
|
|
/* struct implementing the gdb.FinishBreakpoint object by extending
|
|
the gdb.Breakpoint class. */
|
|
struct finish_breakpoint_object
|
|
{
|
|
/* gdb.Breakpoint base class. */
|
|
gdbpy_breakpoint_object py_bp;
|
|
/* gdb.Type object of the value return by the breakpointed function.
|
|
May be NULL if no debug information was available or return type
|
|
was VOID. */
|
|
PyObject *return_type;
|
|
/* gdb.Value object of the function finished by this breakpoint. Will be
|
|
NULL if return_type is NULL. */
|
|
PyObject *function_value;
|
|
/* When stopped at this FinishBreakpoint, gdb.Value object returned by
|
|
the function; Py_None if the value is not computable; NULL if GDB is
|
|
not stopped at a FinishBreakpoint. */
|
|
PyObject *return_value;
|
|
};
|
|
|
|
extern PyTypeObject finish_breakpoint_object_type
|
|
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("finish_breakpoint_object");
|
|
|
|
/* Python function to get the 'return_value' attribute of
|
|
FinishBreakpoint. */
|
|
|
|
static PyObject *
|
|
bpfinishpy_get_returnvalue (PyObject *self, void *closure)
|
|
{
|
|
struct finish_breakpoint_object *self_finishbp =
|
|
(struct finish_breakpoint_object *) self;
|
|
|
|
if (!self_finishbp->return_value)
|
|
Py_RETURN_NONE;
|
|
|
|
Py_INCREF (self_finishbp->return_value);
|
|
return self_finishbp->return_value;
|
|
}
|
|
|
|
/* Deallocate FinishBreakpoint object. */
|
|
|
|
static void
|
|
bpfinishpy_dealloc (PyObject *self)
|
|
{
|
|
struct finish_breakpoint_object *self_bpfinish =
|
|
(struct finish_breakpoint_object *) self;
|
|
|
|
Py_XDECREF (self_bpfinish->function_value);
|
|
Py_XDECREF (self_bpfinish->return_type);
|
|
Py_XDECREF (self_bpfinish->return_value);
|
|
}
|
|
|
|
/* Triggered when gdbpy_should_stop is about to execute the `stop' callback
|
|
of the gdb.FinishBreakpoint object BP_OBJ. Will compute and cache the
|
|
`return_value', if possible. */
|
|
|
|
void
|
|
bpfinishpy_pre_stop_hook (struct gdbpy_breakpoint_object *bp_obj)
|
|
{
|
|
struct finish_breakpoint_object *self_finishbp =
|
|
(struct finish_breakpoint_object *) bp_obj;
|
|
|
|
/* Can compute return_value only once. */
|
|
gdb_assert (!self_finishbp->return_value);
|
|
|
|
if (!self_finishbp->return_type)
|
|
return;
|
|
|
|
TRY
|
|
{
|
|
struct value *function =
|
|
value_object_to_value (self_finishbp->function_value);
|
|
struct type *value_type =
|
|
type_object_to_type (self_finishbp->return_type);
|
|
struct value *ret = get_return_value (function, value_type);
|
|
|
|
if (ret)
|
|
{
|
|
self_finishbp->return_value = value_to_value_object (ret);
|
|
if (!self_finishbp->return_value)
|
|
gdbpy_print_stack ();
|
|
}
|
|
else
|
|
{
|
|
Py_INCREF (Py_None);
|
|
self_finishbp->return_value = Py_None;
|
|
}
|
|
}
|
|
CATCH (except, RETURN_MASK_ALL)
|
|
{
|
|
gdbpy_convert_exception (except);
|
|
gdbpy_print_stack ();
|
|
}
|
|
END_CATCH
|
|
}
|
|
|
|
/* Triggered when gdbpy_should_stop has triggered the `stop' callback
|
|
of the gdb.FinishBreakpoint object BP_OBJ. */
|
|
|
|
void
|
|
bpfinishpy_post_stop_hook (struct gdbpy_breakpoint_object *bp_obj)
|
|
{
|
|
|
|
TRY
|
|
{
|
|
/* Can't delete it here, but it will be removed at the next stop. */
|
|
disable_breakpoint (bp_obj->bp);
|
|
gdb_assert (bp_obj->bp->disposition == disp_del);
|
|
}
|
|
CATCH (except, RETURN_MASK_ALL)
|
|
{
|
|
gdbpy_convert_exception (except);
|
|
gdbpy_print_stack ();
|
|
}
|
|
END_CATCH
|
|
}
|
|
|
|
/* Python function to create a new breakpoint. */
|
|
|
|
static int
|
|
bpfinishpy_init (PyObject *self, PyObject *args, PyObject *kwargs)
|
|
{
|
|
static char *keywords[] = { "frame", "internal", NULL };
|
|
struct finish_breakpoint_object *self_bpfinish =
|
|
(struct finish_breakpoint_object *) self;
|
|
int type = bp_breakpoint;
|
|
PyObject *frame_obj = NULL;
|
|
int thread;
|
|
struct frame_info *frame = NULL; /* init for gcc -Wall */
|
|
struct frame_info *prev_frame = NULL;
|
|
struct frame_id frame_id;
|
|
PyObject *internal = NULL;
|
|
int internal_bp = 0;
|
|
CORE_ADDR pc;
|
|
struct symbol *function;
|
|
|
|
if (!PyArg_ParseTupleAndKeywords (args, kwargs, "|OO", keywords,
|
|
&frame_obj, &internal))
|
|
return -1;
|
|
|
|
TRY
|
|
{
|
|
/* Default frame to newest frame if necessary. */
|
|
if (frame_obj == NULL)
|
|
frame = get_current_frame ();
|
|
else
|
|
frame = frame_object_to_frame_info (frame_obj);
|
|
|
|
if (frame == NULL)
|
|
{
|
|
PyErr_SetString (PyExc_ValueError,
|
|
_("Invalid ID for the `frame' object."));
|
|
}
|
|
else
|
|
{
|
|
prev_frame = get_prev_frame (frame);
|
|
if (prev_frame == 0)
|
|
{
|
|
PyErr_SetString (PyExc_ValueError,
|
|
_("\"FinishBreakpoint\" not "
|
|
"meaningful in the outermost "
|
|
"frame."));
|
|
}
|
|
else if (get_frame_type (prev_frame) == DUMMY_FRAME)
|
|
{
|
|
PyErr_SetString (PyExc_ValueError,
|
|
_("\"FinishBreakpoint\" cannot "
|
|
"be set on a dummy frame."));
|
|
}
|
|
else
|
|
{
|
|
frame_id = get_frame_id (prev_frame);
|
|
if (frame_id_eq (frame_id, null_frame_id))
|
|
PyErr_SetString (PyExc_ValueError,
|
|
_("Invalid ID for the `frame' object."));
|
|
}
|
|
}
|
|
}
|
|
CATCH (except, RETURN_MASK_ALL)
|
|
{
|
|
gdbpy_convert_exception (except);
|
|
return -1;
|
|
}
|
|
END_CATCH
|
|
|
|
if (PyErr_Occurred ())
|
|
return -1;
|
|
|
|
thread = ptid_to_global_thread_id (inferior_ptid);
|
|
if (thread == 0)
|
|
{
|
|
PyErr_SetString (PyExc_ValueError,
|
|
_("No thread currently selected."));
|
|
return -1;
|
|
}
|
|
|
|
if (internal)
|
|
{
|
|
internal_bp = PyObject_IsTrue (internal);
|
|
if (internal_bp == -1)
|
|
{
|
|
PyErr_SetString (PyExc_ValueError,
|
|
_("The value of `internal' must be a boolean."));
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
/* Find the function we will return from. */
|
|
self_bpfinish->return_type = NULL;
|
|
self_bpfinish->function_value = NULL;
|
|
|
|
TRY
|
|
{
|
|
if (get_frame_pc_if_available (frame, &pc))
|
|
{
|
|
function = find_pc_function (pc);
|
|
if (function != NULL)
|
|
{
|
|
struct type *ret_type =
|
|
TYPE_TARGET_TYPE (SYMBOL_TYPE (function));
|
|
|
|
/* Remember only non-void return types. */
|
|
if (TYPE_CODE (ret_type) != TYPE_CODE_VOID)
|
|
{
|
|
struct value *func_value;
|
|
|
|
/* Ignore Python errors at this stage. */
|
|
self_bpfinish->return_type = type_to_type_object (ret_type);
|
|
PyErr_Clear ();
|
|
func_value = read_var_value (function, NULL, frame);
|
|
self_bpfinish->function_value =
|
|
value_to_value_object (func_value);
|
|
PyErr_Clear ();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CATCH (except, RETURN_MASK_ALL)
|
|
{
|
|
/* Just swallow. Either the return type or the function value
|
|
remain NULL. */
|
|
}
|
|
END_CATCH
|
|
|
|
if (self_bpfinish->return_type == NULL || self_bpfinish->function_value == NULL)
|
|
{
|
|
/* Won't be able to compute return value. */
|
|
Py_XDECREF (self_bpfinish->return_type);
|
|
Py_XDECREF (self_bpfinish->function_value);
|
|
|
|
self_bpfinish->return_type = NULL;
|
|
self_bpfinish->function_value = NULL;
|
|
}
|
|
|
|
bppy_pending_object = &self_bpfinish->py_bp;
|
|
bppy_pending_object->number = -1;
|
|
bppy_pending_object->bp = NULL;
|
|
|
|
TRY
|
|
{
|
|
struct event_location *location;
|
|
struct cleanup *back_to;
|
|
|
|
/* Set a breakpoint on the return address. */
|
|
location = new_address_location (get_frame_pc (prev_frame));
|
|
back_to = make_cleanup_delete_event_location (location);
|
|
create_breakpoint (python_gdbarch,
|
|
location, NULL, thread, NULL,
|
|
0,
|
|
1 /*temp_flag*/,
|
|
bp_breakpoint,
|
|
0,
|
|
AUTO_BOOLEAN_TRUE,
|
|
&bkpt_breakpoint_ops,
|
|
0, 1, internal_bp, 0);
|
|
do_cleanups (back_to);
|
|
}
|
|
CATCH (except, RETURN_MASK_ALL)
|
|
{
|
|
GDB_PY_SET_HANDLE_EXCEPTION (except);
|
|
}
|
|
END_CATCH
|
|
|
|
self_bpfinish->py_bp.bp->frame_id = frame_id;
|
|
self_bpfinish->py_bp.is_finish_bp = 1;
|
|
|
|
/* Bind the breakpoint with the current program space. */
|
|
self_bpfinish->py_bp.bp->pspace = current_program_space;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Called when GDB notices that the finish breakpoint BP_OBJ is out of
|
|
the current callstack. Triggers the method OUT_OF_SCOPE if implemented,
|
|
then delete the breakpoint. */
|
|
|
|
static void
|
|
bpfinishpy_out_of_scope (struct finish_breakpoint_object *bpfinish_obj)
|
|
{
|
|
gdbpy_breakpoint_object *bp_obj = (gdbpy_breakpoint_object *) bpfinish_obj;
|
|
PyObject *py_obj = (PyObject *) bp_obj;
|
|
|
|
if (bpfinish_obj->py_bp.bp->enable_state == bp_enabled
|
|
&& PyObject_HasAttrString (py_obj, outofscope_func))
|
|
{
|
|
PyObject *meth_result;
|
|
|
|
meth_result = PyObject_CallMethod (py_obj, outofscope_func, NULL);
|
|
if (meth_result == NULL)
|
|
gdbpy_print_stack ();
|
|
Py_XDECREF (meth_result);
|
|
}
|
|
|
|
delete_breakpoint (bpfinish_obj->py_bp.bp);
|
|
}
|
|
|
|
/* Callback for `bpfinishpy_detect_out_scope'. Triggers Python's
|
|
`B->out_of_scope' function if B is a FinishBreakpoint out of its scope. */
|
|
|
|
static int
|
|
bpfinishpy_detect_out_scope_cb (struct breakpoint *b, void *args)
|
|
{
|
|
struct breakpoint *bp_stopped = (struct breakpoint *) args;
|
|
PyObject *py_bp = (PyObject *) b->py_bp_object;
|
|
struct gdbarch *garch = b->gdbarch ? b->gdbarch : get_current_arch ();
|
|
|
|
/* Trigger out_of_scope if this is a FinishBreakpoint and its frame is
|
|
not anymore in the current callstack. */
|
|
if (py_bp != NULL && b->py_bp_object->is_finish_bp)
|
|
{
|
|
struct finish_breakpoint_object *finish_bp =
|
|
(struct finish_breakpoint_object *) py_bp;
|
|
|
|
/* Check scope if not currently stopped at the FinishBreakpoint. */
|
|
if (b != bp_stopped)
|
|
{
|
|
TRY
|
|
{
|
|
if (b->pspace == current_inferior ()->pspace
|
|
&& (!target_has_registers
|
|
|| frame_find_by_id (b->frame_id) == NULL))
|
|
bpfinishpy_out_of_scope (finish_bp);
|
|
}
|
|
CATCH (except, RETURN_MASK_ALL)
|
|
{
|
|
gdbpy_convert_exception (except);
|
|
gdbpy_print_stack ();
|
|
}
|
|
END_CATCH
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Attached to `stop' notifications, check if the execution has run
|
|
out of the scope of any FinishBreakpoint before it has been hit. */
|
|
|
|
static void
|
|
bpfinishpy_handle_stop (struct bpstats *bs, int print_frame)
|
|
{
|
|
struct cleanup *cleanup = ensure_python_env (get_current_arch (),
|
|
current_language);
|
|
|
|
iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb,
|
|
bs == NULL ? NULL : bs->breakpoint_at);
|
|
|
|
do_cleanups (cleanup);
|
|
}
|
|
|
|
/* Attached to `exit' notifications, triggers all the necessary out of
|
|
scope notifications. */
|
|
|
|
static void
|
|
bpfinishpy_handle_exit (struct inferior *inf)
|
|
{
|
|
struct cleanup *cleanup = ensure_python_env (target_gdbarch (),
|
|
current_language);
|
|
|
|
iterate_over_breakpoints (bpfinishpy_detect_out_scope_cb, NULL);
|
|
|
|
do_cleanups (cleanup);
|
|
}
|
|
|
|
/* Initialize the Python finish breakpoint code. */
|
|
|
|
int
|
|
gdbpy_initialize_finishbreakpoints (void)
|
|
{
|
|
if (PyType_Ready (&finish_breakpoint_object_type) < 0)
|
|
return -1;
|
|
|
|
if (gdb_pymodule_addobject (gdb_module, "FinishBreakpoint",
|
|
(PyObject *) &finish_breakpoint_object_type) < 0)
|
|
return -1;
|
|
|
|
observer_attach_normal_stop (bpfinishpy_handle_stop);
|
|
observer_attach_inferior_exit (bpfinishpy_handle_exit);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static PyGetSetDef finish_breakpoint_object_getset[] = {
|
|
{ "return_value", bpfinishpy_get_returnvalue, NULL,
|
|
"gdb.Value object representing the return value, if any. \
|
|
None otherwise.", NULL },
|
|
{ NULL } /* Sentinel. */
|
|
};
|
|
|
|
PyTypeObject finish_breakpoint_object_type =
|
|
{
|
|
PyVarObject_HEAD_INIT (NULL, 0)
|
|
"gdb.FinishBreakpoint", /*tp_name*/
|
|
sizeof (struct finish_breakpoint_object), /*tp_basicsize*/
|
|
0, /*tp_itemsize*/
|
|
bpfinishpy_dealloc, /*tp_dealloc*/
|
|
0, /*tp_print*/
|
|
0, /*tp_getattr*/
|
|
0, /*tp_setattr*/
|
|
0, /*tp_compare*/
|
|
0, /*tp_repr*/
|
|
0, /*tp_as_number*/
|
|
0, /*tp_as_sequence*/
|
|
0, /*tp_as_mapping*/
|
|
0, /*tp_hash */
|
|
0, /*tp_call*/
|
|
0, /*tp_str*/
|
|
0, /*tp_getattro*/
|
|
0, /*tp_setattro */
|
|
0, /*tp_as_buffer*/
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
|
|
"GDB finish breakpoint object", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
0, /* tp_methods */
|
|
0, /* tp_members */
|
|
finish_breakpoint_object_getset,/* tp_getset */
|
|
&breakpoint_object_type, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
bpfinishpy_init, /* tp_init */
|
|
0, /* tp_alloc */
|
|
0 /* tp_new */
|
|
};
|