binutils-gdb/gdb/python/py-breakpoint.c
Pedro Alves d7e747318f Eliminate make_cleanup_ui_file_delete / make ui_file a class hierarchy
This patch starts from the desire to eliminate
make_cleanup_ui_file_delete, but then goes beyond.  It makes ui_file &
friends a real C++ class hierarchy, and switches temporary
ui_file-like objects to stack-based allocation.

- mem_fileopen -> string_file

mem_fileopen is replaced with a new string_file class that is treated
as a value class created on the stack.  This alone eliminates most
make_cleanup_ui_file_delete calls, and, simplifies code a whole lot
(diffstat shows around 1k loc dropped.)

string_file's internal buffer is a std::string, thus the "string" in
the name.  This simplifies the implementation much, compared to
mem_fileopen, which managed growing its internal buffer manually.

- ui_file_as_string, ui_file_strdup, ui_file_obsavestring all gone

The new string_file class has a string() method that provides direct
writable access to the internal std::string buffer.  This replaced
ui_file_as_string, which forced a copy of the same data the stream had
inside.  With direct access via a writable reference, we can instead
move the string out of the string_stream, avoiding deep string
copying.

Related, ui_file_xstrdup calls are replaced with xstrdup'ping the
stream's string, and ui_file_obsavestring is replaced by
obstack_copy0.

With all those out of the way, getting rid of the weird ui_file_put
mechanism was possible.

- New ui_file::printf, ui_file::puts, etc. methods

These simplify / clarify client code.  I considered splitting
client-code changes, like these, e.g.:

  -  stb = mem_fileopen ();
  -  fprintf_unfiltered (stb, "%s%s%s",
  -		      _("The valid values are:\n"),
  -		      regdesc,
  -		      _("The default is \"std\"."));
  +  string_file stb;
  +  stb.printf ("%s%s%s",
  +	      _("The valid values are:\n"),
  +	      regdesc,
  +	      _("The default is \"std\"."));

In two steps, with the first step leaving fprintf_unfiltered (etc.)
calls in place, and only afterwards do a pass to change all those to
call stb.printf etc..  I didn't do that split, because (when I tried),
it turned out to be pointless make-work: the first pass would have to
touch the fprintf_unfiltered line anyway, to replace "stb" with
"&stb".

- gdb_fopen replaced with stack-based objects

This avoids the need for cleanups or unique_ptr's.  I.e., this:

      struct ui_file *file = gdb_fopen (filename, "w");
      if (filename == NULL)
 	perror_with_name (filename);
      cleanups = make_cleanup_ui_file_delete (file);
      // use file.
      do_cleanups (cleanups);

is replaced with this:

      stdio_file file;
      if (!file.open (filename, "w"))
 	perror_with_name (filename);
      // use file.

- odd contorsions in null_file_write / null_file_fputs around when to
  call to_fputs / to_write eliminated.

- Global null_stream object

A few places that were allocating a ui_file in order to print to
"nowhere" are adjusted to instead refer to a new 'null_stream' global
stream.

- TUI's tui_sfileopen eliminated.  TUI's ui_file much simplified

The TUI's ui_file was serving a dual purpose.  It supported being used
as string buffer, and supported being backed by a stdio FILE.  The
string buffer part is gone, replaced by using of string_file.  The
'FILE *' support is now much simplified, by making the TUI's ui_file
inherit from stdio_file.

gdb/ChangeLog:
2017-02-02  Pedro Alves  <palves@redhat.com>

	* ada-lang.c (type_as_string): Use string_file.
	* ada-valprint.c (ada_print_floating): Use string_file.
	* ada-varobj.c (ada_varobj_scalar_image)
	(ada_varobj_get_value_image): Use string_file.
	* aix-thread.c (aix_thread_extra_thread_info): Use string_file.
	* arm-tdep.c (_initialize_arm_tdep): Use string_printf.
	* breakpoint.c (update_inserted_breakpoint_locations)
	(insert_breakpoint_locations, reattach_breakpoints)
	(print_breakpoint_location, print_one_detail_ranged_breakpoint)
	(print_it_watchpoint): Use string_file.
	(save_breakpoints): Use stdio_file.
	* c-exp.y (oper): Use string_file.
	* cli/cli-logging.c (set_logging_redirect): Use ui_file_up and
	tee_file.
	(pop_output_files): Use delete.
	(handle_redirections): Use stdio_file and tee_file.
	* cli/cli-setshow.c (do_show_command): Use string_file.
	* compile/compile-c-support.c (c_compute_program): Use
	string_file.
	* compile/compile-c-symbols.c (generate_vla_size): Take a
	'string_file &' instead of a 'ui_file *'.
	(generate_c_for_for_one_variable): Take a 'string_file &' instead
	of a 'ui_file *'.  Use string_file.
	(generate_c_for_variable_locations): Take a 'string_file &'
	instead of a 'ui_file *'.
	* compile/compile-internal.h (generate_c_for_for_one_variable):
	Take a 'string_file &' instead of a 'ui_file *'.
	* compile/compile-loc2c.c (push, pushf, unary, binary)
	(print_label, pushf_register_address, pushf_register)
	(do_compile_dwarf_expr_to_c): Take a 'string_file &' instead of a
	'ui_file *'.  Adjust.
	* compile/compile.c (compile_to_object): Use string_file.
	* compile/compile.h (compile_dwarf_expr_to_c)
	(compile_dwarf_bounds_to_c): Take a 'string_file &' instead of a
	'ui_file *'.
	* cp-support.c (inspect_type): Use string_file and obstack_copy0.
	(replace_typedefs_qualified_name): Use string_file and
	obstack_copy0.
	* disasm.c (gdb_pretty_print_insn): Use string_file.
	(gdb_disassembly): Adjust reference the null_stream global.
	(do_ui_file_delete): Delete.
	(gdb_insn_length): Use null_stream.
	* dummy-frame.c (maintenance_print_dummy_frames): Use stdio_file.
	* dwarf2loc.c (dwarf2_compile_property_to_c)
	(locexpr_generate_c_location, loclist_generate_c_location): Take a
	'string_file &' instead of a 'ui_file *'.
	* dwarf2loc.h (dwarf2_compile_property_to_c): Likewise.
	* dwarf2read.c (do_ui_file_peek_last): Delete.
	(dwarf2_compute_name): Use string_file.
	* event-top.c (gdb_setup_readline): Use stdio_file.
	* gdbarch.sh (verify_gdbarch): Use string_file.
	* gdbtypes.c (safe_parse_type): Use null_stream.
	* guile/scm-breakpoint.c (gdbscm_breakpoint_commands): Use
	string_file.
	* guile/scm-disasm.c (gdbscm_print_insn_from_port): Take a
	'string_file *' instead of a 'ui_file *'.
	(gdbscm_arch_disassemble): Use string_file.
	* guile/scm-frame.c (frscm_print_frame_smob): Use string_file.
	* guile/scm-ports.c (class ioscm_file_port): Now a class that
	inherits from ui_file.
	(ioscm_file_port_delete, ioscm_file_port_rewind)
	(ioscm_file_port_put): Delete.
	(ioscm_file_port_write): Rename to ...
	(ioscm_file_port::write): ... this.  Remove file_port_magic
	checks.
	(ioscm_file_port_new): Delete.
	(ioscm_with_output_to_port_worker): Use ioscm_file_port and
	ui_file_up.
	* guile/scm-type.c (tyscm_type_name): Use string_file.
	* guile/scm-value.c (vlscm_print_value_smob, gdbscm_value_print):
	Use string_file.
	* infcmd.c (print_return_value_1): Use string_file.
	* infrun.c (print_target_wait_results): Use string_file.
	* language.c (add_language): Use string_file.
	* location.c (explicit_to_string_internal): Use string_file.
	* main.c (captured_main_1): Use null_file.
	* maint.c (maintenance_print_architecture): Use stdio_file.
	* mi/mi-cmd-stack.c (list_arg_or_local): Use string_file.
	* mi/mi-common.h (struct mi_interp) <out, err, log, targ,
	event_channel>: Change type to mi_console_file pointer.
	* mi/mi-console.c (mi_console_file_fputs, mi_console_file_flush)
	(mi_console_file_delete): Delete.
	(struct mi_console_file): Delete.
	(mi_console_file_magic): Delete.
	(mi_console_file_new): Delete.
	(mi_console_file::mi_console_file): New.
	(mi_console_file_delete): Delete.
	(mi_console_file_fputs): Delete.
	(mi_console_file::write): New.
	(mi_console_raw_packet): Delete.
	(mi_console_file::flush): New.
	(mi_console_file_flush): Delete.
	(mi_console_set_raw): Rename to ...
	(mi_console_file::set_raw): ... this.
	* mi/mi-console.h (class mi_console_file): New class.
	(mi_console_file_new, mi_console_set_raw): Delete.
	* mi/mi-interp.c (mi_interpreter_init): Use mi_console_file.
	(mi_set_logging): Use delete and tee_file.  Adjust.
	* mi/mi-main.c (output_register): Use string_file.
	(mi_cmd_data_evaluate_expression): Use string_file.
	(mi_cmd_data_read_memory): Use string_file.
	(mi_cmd_execute, print_variable_or_computed): Use string_file.
	* mi/mi-out.c (mi_ui_out::main_stream): New.
	(mi_ui_out::rewind): Use main_stream and
	string_file.
	(mi_ui_out::put): Use main_stream and string_file.
	(mi_ui_out::mi_ui_out): Remove 'stream' parameter.
	Allocate a 'string_file' instead.
	(mi_out_new): Don't allocate a mem_fileopen stream here.
	* mi/mi-out.h (mi_ui_out::mi_ui_out): Remove 'stream' parameter.
	(mi_ui_out::main_stream): Declare method.
	* printcmd.c (eval_command): Use string_file.
	* psymtab.c (maintenance_print_psymbols): Use stdio_file.
	* python/py-arch.c (archpy_disassemble): Use string_file.
	* python/py-breakpoint.c (bppy_get_commands): Use string_file.
	* python/py-frame.c (frapy_str): Use string_file.
	* python/py-framefilter.c (py_print_type, py_print_single_arg):
	Use string_file.
	* python/py-type.c (typy_str): Use string_file.
	* python/py-unwind.c (unwind_infopy_str): Use string_file.
	* python/py-value.c (valpy_str): Use string_file.
	* record-btrace.c (btrace_insn_history): Use string_file.
	* regcache.c (regcache_print): Use stdio_file.
	* reggroups.c (maintenance_print_reggroups): Use stdio_file.
	* remote.c (escape_buffer): Use string_file.
	* rust-lang.c (rust_get_disr_info): Use string_file.
	* serial.c (serial_open_ops_1): Use stdio_file.
	(do_serial_close): Use delete.
	* stack.c (print_frame_arg): Use string_file.
	(print_frame_args): Remove local mem_fileopen stream, not used.
	(print_frame): Use string_file.
	* symmisc.c (maintenance_print_symbols): Use stdio_file.
	* symtab.h (struct symbol_computed_ops) <generate_c_location>:
	Take a 'string_file *' instead of a 'ui_file *'.
	* top.c (new_ui): Use stdio_file and stderr_file.
	(free_ui): Use delete.
	(execute_command_to_string): Use string_file.
	(quit_confirm): Use string_file.
	* tracepoint.c (collection_list::append_exp): Use string_file.
	* tui/tui-disasm.c (tui_disassemble): Use string_file.
	* tui/tui-file.c: Don't include "ui-file.h".
	(enum streamtype, struct tui_stream): Delete.
	(tui_file_new, tui_file_delete, tui_fileopen, tui_sfileopen)
	(tui_file_isatty, tui_file_rewind, tui_file_put): Delete.
	(tui_file::tui_file): New method.
	(tui_file_fputs): Delete.
	(tui_file_get_strbuf): Delete.
	(tui_file::puts): New method.
	(tui_file_adjust_strbuf): Delete.
	(tui_file_flush): Delete.
	(tui_file::flush): New method.
	* tui/tui-file.h: Tweak intro comment.
	Include ui-file.h.
	(tui_fileopen, tui_sfileopen, tui_file_get_strbuf)
	(tui_file_adjust_strbuf): Delete declarations.
	(class tui_file): New class.
	* tui/tui-io.c (tui_initialize_io): Use tui_file.
	* tui/tui-regs.c (tui_restore_gdbout): Use delete.
	(tui_register_format): Use string_stream.
	* tui/tui-stack.c (tui_make_status_line): Use string_file.
	(tui_get_function_from_frame): Use string_file.
	* typeprint.c (type_to_string): Use string_file.
	* ui-file.c (struct ui_file, ui_file_magic, ui_file_new): Delete.
	(null_stream): New global.
	(ui_file_delete): Delete.
	(ui_file::ui_file): New.
	(null_file_isatty): Delete.
	(ui_file::~ui_file): New.
	(null_file_rewind): Delete.
	(ui_file::printf): New.
	(null_file_put): Delete.
	(null_file_flush): Delete.
	(ui_file::putstr): New.
	(null_file_write): Delete.
	(ui_file::putstrn): New.
	(null_file_read): Delete.
	(ui_file::putc): New.
	(null_file_fputs): Delete.
	(null_file_write_async_safe): Delete.
	(ui_file::vprintf): New.
	(null_file_delete): Delete.
	(null_file::write): New.
	(null_file_fseek): Delete.
	(null_file::puts): New.
	(ui_file_data): Delete.
	(null_file::write_async_safe): New.
	(gdb_flush, ui_file_isatty): Adjust.
	(ui_file_put, ui_file_rewind): Delete.
	(ui_file_write): Adjust.
	(ui_file_write_for_put): Delete.
	(ui_file_write_async_safe, ui_file_read): Adjust.
	(ui_file_fseek): Delete.
	(fputs_unfiltered): Adjust.
	(set_ui_file_flush, set_ui_file_isatty, set_ui_file_rewind)
	(set_ui_file_put, set_ui_file_write, set_ui_file_write_async_safe)
	(set_ui_file_read, set_ui_file_fputs, set_ui_file_fseek)
	(set_ui_file_data): Delete.
	(string_file::~string_file, string_file::write)
	(struct accumulated_ui_file, do_ui_file_xstrdup, ui_file_xstrdup)
	(do_ui_file_as_string, ui_file_as_string): Delete.
	(do_ui_file_obsavestring, ui_file_obsavestring): Delete.
	(struct mem_file): Delete.
	(mem_file_new): Delete.
	(stdio_file::stdio_file): New.
	(mem_file_delete): Delete.
	(stdio_file::stdio_file): New.
	(mem_fileopen): Delete.
	(stdio_file::~stdio_file): New.
	(mem_file_rewind): Delete.
	(stdio_file::set_stream): New.
	(mem_file_put): Delete.
	(stdio_file::open): New.
	(mem_file_write): Delete.
	(stdio_file_magic, struct stdio_file): Delete.
	(stdio_file_new, stdio_file_delete, stdio_file_flush): Delete.
	(stdio_file::flush): New.
	(stdio_file_read): Rename to ...
	(stdio_file::read): ... this.  Adjust.
	(stdio_file_write): Rename to ...
	(stdio_file::write): ... this.  Adjust.
	(stdio_file_write_async_safe): Rename to ...
	(stdio_file::write_async_safe) ... this.  Adjust.
	(stdio_file_fputs): Rename to ...
	(stdio_file::puts) ... this.  Adjust.
	(stdio_file_isatty): Delete.
	(stdio_file_fseek): Delete.
	(stdio_file::isatty): New.
	(stderr_file_write): Rename to ...
	(stderr_file::write) ... this.  Adjust.
	(stderr_file_fputs): Rename to ...
	(stderr_file::puts) ... this.  Adjust.
	(stderr_fileopen, stdio_fileopen, gdb_fopen): Delete.
	(stderr_file::stderr_file): New.
	(tee_file_magic): Delete.
	(struct tee_file): Delete.
	(tee_file::tee_file): New.
	(tee_file_new): Delete.
	(tee_file::~tee_file): New.
	(tee_file_delete): Delete.
	(tee_file_flush): Rename to ...
	(tee_file::flush): ... this.  Adjust.
	(tee_file_write): Rename to ...
	(tee_file::write): ... this.  Adjust.
	(tee_file::write_async_safe): New.
	(tee_file_fputs): Rename to ...
	(tee_file::puts): ... this.  Adjust.
	(tee_file_isatty): Rename to ...
	(tee_file::isatty): ... this.  Adjust.
	* ui-file.h (struct obstack, struct ui_file): Don't
	forward-declare.
	(ui_file_new, ui_file_flush_ftype, set_ui_file_flush)
	(ui_file_write_ftype)
	(set_ui_file_write, ui_file_fputs_ftype, set_ui_file_fputs)
	(ui_file_write_async_safe_ftype, set_ui_file_write_async_safe)
	(ui_file_read_ftype, set_ui_file_read, ui_file_isatty_ftype)
	(set_ui_file_isatty, ui_file_rewind_ftype, set_ui_file_rewind)
	(ui_file_put_method_ftype, ui_file_put_ftype, set_ui_file_put)
	(ui_file_delete_ftype, set_ui_file_data, ui_file_fseek_ftype)
	(set_ui_file_fseek): Delete.
	(ui_file_data, ui_file_delete, ui_file_rewind)
	(struct ui_file): New.
	(ui_file_up): New.
	(class null_file): New.
	(null_stream): Declare.
	(ui_file_write_for_put, ui_file_put): Delete.
	(ui_file_xstrdup, ui_file_as_string, ui_file_obsavestring):
	Delete.
	(ui_file_fseek, mem_fileopen, stdio_fileopen, stderr_fileopen)
	(gdb_fopen, tee_file_new): Delete.
	(struct string_file): New.
	(struct stdio_file): New.
	(stdio_file_up): New.
	(struct stderr_file): New.
	(class tee_file): New.
	* ui-out.c (ui_out::field_stream): Take a 'string_file &' instead
	of a 'ui_file *'.  Adjust.
	* ui-out.h (class ui_out) <field_stream>: Likewise.
	* utils.c (do_ui_file_delete, make_cleanup_ui_file_delete)
	(null_stream): Delete.
	(error_stream): Take a 'string_file &' instead of a 'ui_file *'.
	Adjust.
	* utils.h (struct ui_file): Delete forward declaration..
	(make_cleanup_ui_file_delete, null_stream): Delete declarations.
	(error_stream): Take a 'string_file &' instead of a
	'ui_file *'.
	* varobj.c (varobj_value_get_print_value): Use string_file.
	* xtensa-tdep.c (xtensa_verify_config): Use string_file.
	* gdbarch.c: Regenerate.
2017-02-02 11:11:47 +00:00

1145 lines
28 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* Python interface to breakpoints
Copyright (C) 2008-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/>. */
#include "defs.h"
#include "value.h"
#include "python-internal.h"
#include "python.h"
#include "charset.h"
#include "breakpoint.h"
#include "gdbcmd.h"
#include "gdbthread.h"
#include "observer.h"
#include "cli/cli-script.h"
#include "ada-lang.h"
#include "arch-utils.h"
#include "language.h"
#include "location.h"
#include "py-event.h"
/* Number of live breakpoints. */
static int bppy_live;
/* Variables used to pass information between the Breakpoint
constructor and the breakpoint-created hook function. */
gdbpy_breakpoint_object *bppy_pending_object;
/* Function that is called when a Python condition is evaluated. */
static char * const stop_func = "stop";
/* This is used to initialize various gdb.bp_* constants. */
struct pybp_code
{
/* The name. */
const char *name;
/* The code. */
int code;
};
/* Entries related to the type of user set breakpoints. */
static struct pybp_code pybp_codes[] =
{
{ "BP_NONE", bp_none},
{ "BP_BREAKPOINT", bp_breakpoint},
{ "BP_WATCHPOINT", bp_watchpoint},
{ "BP_HARDWARE_WATCHPOINT", bp_hardware_watchpoint},
{ "BP_READ_WATCHPOINT", bp_read_watchpoint},
{ "BP_ACCESS_WATCHPOINT", bp_access_watchpoint},
{NULL} /* Sentinel. */
};
/* Entries related to the type of watchpoint. */
static struct pybp_code pybp_watch_types[] =
{
{ "WP_READ", hw_read},
{ "WP_WRITE", hw_write},
{ "WP_ACCESS", hw_access},
{NULL} /* Sentinel. */
};
/* Python function which checks the validity of a breakpoint object. */
static PyObject *
bppy_is_valid (PyObject *self, PyObject *args)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
if (self_bp->bp)
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
/* Python function to test whether or not the breakpoint is enabled. */
static PyObject *
bppy_get_enabled (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
if (! self_bp->bp)
Py_RETURN_FALSE;
if (self_bp->bp->enable_state == bp_enabled)
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
/* Python function to test whether or not the breakpoint is silent. */
static PyObject *
bppy_get_silent (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
if (self_bp->bp->silent)
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
/* Python function to set the enabled state of a breakpoint. */
static int
bppy_set_enabled (PyObject *self, PyObject *newvalue, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
int cmp;
BPPY_SET_REQUIRE_VALID (self_bp);
if (newvalue == NULL)
{
PyErr_SetString (PyExc_TypeError,
_("Cannot delete `enabled' attribute."));
return -1;
}
else if (! PyBool_Check (newvalue))
{
PyErr_SetString (PyExc_TypeError,
_("The value of `enabled' must be a boolean."));
return -1;
}
cmp = PyObject_IsTrue (newvalue);
if (cmp < 0)
return -1;
TRY
{
if (cmp == 1)
enable_breakpoint (self_bp->bp);
else
disable_breakpoint (self_bp->bp);
}
CATCH (except, RETURN_MASK_ALL)
{
GDB_PY_SET_HANDLE_EXCEPTION (except);
}
END_CATCH
return 0;
}
/* Python function to set the 'silent' state of a breakpoint. */
static int
bppy_set_silent (PyObject *self, PyObject *newvalue, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
int cmp;
BPPY_SET_REQUIRE_VALID (self_bp);
if (newvalue == NULL)
{
PyErr_SetString (PyExc_TypeError,
_("Cannot delete `silent' attribute."));
return -1;
}
else if (! PyBool_Check (newvalue))
{
PyErr_SetString (PyExc_TypeError,
_("The value of `silent' must be a boolean."));
return -1;
}
cmp = PyObject_IsTrue (newvalue);
if (cmp < 0)
return -1;
else
breakpoint_set_silent (self_bp->bp, cmp);
return 0;
}
/* Python function to set the thread of a breakpoint. */
static int
bppy_set_thread (PyObject *self, PyObject *newvalue, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
long id;
BPPY_SET_REQUIRE_VALID (self_bp);
if (newvalue == NULL)
{
PyErr_SetString (PyExc_TypeError,
_("Cannot delete `thread' attribute."));
return -1;
}
else if (PyInt_Check (newvalue))
{
if (! gdb_py_int_as_long (newvalue, &id))
return -1;
if (!valid_global_thread_id (id))
{
PyErr_SetString (PyExc_RuntimeError,
_("Invalid thread ID."));
return -1;
}
}
else if (newvalue == Py_None)
id = -1;
else
{
PyErr_SetString (PyExc_TypeError,
_("The value of `thread' must be an integer or None."));
return -1;
}
breakpoint_set_thread (self_bp->bp, id);
return 0;
}
/* Python function to set the (Ada) task of a breakpoint. */
static int
bppy_set_task (PyObject *self, PyObject *newvalue, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
long id;
int valid_id = 0;
BPPY_SET_REQUIRE_VALID (self_bp);
if (newvalue == NULL)
{
PyErr_SetString (PyExc_TypeError,
_("Cannot delete `task' attribute."));
return -1;
}
else if (PyInt_Check (newvalue))
{
if (! gdb_py_int_as_long (newvalue, &id))
return -1;
TRY
{
valid_id = valid_task_id (id);
}
CATCH (except, RETURN_MASK_ALL)
{
GDB_PY_SET_HANDLE_EXCEPTION (except);
}
END_CATCH
if (! valid_id)
{
PyErr_SetString (PyExc_RuntimeError,
_("Invalid task ID."));
return -1;
}
}
else if (newvalue == Py_None)
id = 0;
else
{
PyErr_SetString (PyExc_TypeError,
_("The value of `task' must be an integer or None."));
return -1;
}
breakpoint_set_task (self_bp->bp, id);
return 0;
}
/* Python function which deletes the underlying GDB breakpoint. This
triggers the breakpoint_deleted observer which will call
gdbpy_breakpoint_deleted; that function cleans up the Python
sections. */
static PyObject *
bppy_delete_breakpoint (PyObject *self, PyObject *args)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
TRY
{
delete_breakpoint (self_bp->bp);
}
CATCH (except, RETURN_MASK_ALL)
{
GDB_PY_HANDLE_EXCEPTION (except);
}
END_CATCH
Py_RETURN_NONE;
}
/* Python function to set the ignore count of a breakpoint. */
static int
bppy_set_ignore_count (PyObject *self, PyObject *newvalue, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
long value;
BPPY_SET_REQUIRE_VALID (self_bp);
if (newvalue == NULL)
{
PyErr_SetString (PyExc_TypeError,
_("Cannot delete `ignore_count' attribute."));
return -1;
}
else if (! PyInt_Check (newvalue))
{
PyErr_SetString (PyExc_TypeError,
_("The value of `ignore_count' must be an integer."));
return -1;
}
if (! gdb_py_int_as_long (newvalue, &value))
return -1;
if (value < 0)
value = 0;
TRY
{
set_ignore_count (self_bp->number, (int) value, 0);
}
CATCH (except, RETURN_MASK_ALL)
{
GDB_PY_SET_HANDLE_EXCEPTION (except);
}
END_CATCH
return 0;
}
/* Python function to set the hit count of a breakpoint. */
static int
bppy_set_hit_count (PyObject *self, PyObject *newvalue, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_SET_REQUIRE_VALID (self_bp);
if (newvalue == NULL)
{
PyErr_SetString (PyExc_TypeError,
_("Cannot delete `hit_count' attribute."));
return -1;
}
else
{
long value;
if (! gdb_py_int_as_long (newvalue, &value))
return -1;
if (value != 0)
{
PyErr_SetString (PyExc_AttributeError,
_("The value of `hit_count' must be zero."));
return -1;
}
}
self_bp->bp->hit_count = 0;
return 0;
}
/* Python function to get the location of a breakpoint. */
static PyObject *
bppy_get_location (PyObject *self, void *closure)
{
const char *str;
gdbpy_breakpoint_object *obj = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (obj);
if (obj->bp->type != bp_breakpoint)
Py_RETURN_NONE;
str = event_location_to_string (obj->bp->location);
if (! str)
str = "";
return host_string_to_python_string (str);
}
/* Python function to get the breakpoint expression. */
static PyObject *
bppy_get_expression (PyObject *self, void *closure)
{
char *str;
gdbpy_breakpoint_object *obj = (gdbpy_breakpoint_object *) self;
struct watchpoint *wp;
BPPY_REQUIRE_VALID (obj);
if (!is_watchpoint (obj->bp))
Py_RETURN_NONE;
wp = (struct watchpoint *) obj->bp;
str = wp->exp_string;
if (! str)
str = "";
return host_string_to_python_string (str);
}
/* Python function to get the condition expression of a breakpoint. */
static PyObject *
bppy_get_condition (PyObject *self, void *closure)
{
char *str;
gdbpy_breakpoint_object *obj = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (obj);
str = obj->bp->cond_string;
if (! str)
Py_RETURN_NONE;
return host_string_to_python_string (str);
}
/* Returns 0 on success. Returns -1 on error, with a python exception set.
*/
static int
bppy_set_condition (PyObject *self, PyObject *newvalue, void *closure)
{
gdb::unique_xmalloc_ptr<char> exp_holder;
const char *exp = NULL;
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
struct gdb_exception except = exception_none;
BPPY_SET_REQUIRE_VALID (self_bp);
if (newvalue == NULL)
{
PyErr_SetString (PyExc_TypeError,
_("Cannot delete `condition' attribute."));
return -1;
}
else if (newvalue == Py_None)
exp = "";
else
{
exp_holder = python_string_to_host_string (newvalue);
if (exp_holder == NULL)
return -1;
exp = exp_holder.get ();
}
TRY
{
set_breakpoint_condition (self_bp->bp, exp, 0);
}
CATCH (ex, RETURN_MASK_ALL)
{
except = ex;
}
END_CATCH
GDB_PY_SET_HANDLE_EXCEPTION (except);
return 0;
}
/* Python function to get the commands attached to a breakpoint. */
static PyObject *
bppy_get_commands (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
struct breakpoint *bp = self_bp->bp;
long length;
PyObject *result;
BPPY_REQUIRE_VALID (self_bp);
if (! self_bp->bp->commands)
Py_RETURN_NONE;
string_file stb;
current_uiout->redirect (&stb);
TRY
{
print_command_lines (current_uiout, breakpoint_commands (bp), 0);
}
CATCH (except, RETURN_MASK_ALL)
{
current_uiout->redirect (NULL);
gdbpy_convert_exception (except);
return NULL;
}
END_CATCH
current_uiout->redirect (NULL);
return host_string_to_python_string (stb.c_str ());
}
/* Python function to get the breakpoint type. */
static PyObject *
bppy_get_type (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
return PyInt_FromLong (self_bp->bp->type);
}
/* Python function to get the visibility of the breakpoint. */
static PyObject *
bppy_get_visibility (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
if (user_breakpoint_p (self_bp->bp))
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
/* Python function to determine if the breakpoint is a temporary
breakpoint. */
static PyObject *
bppy_get_temporary (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
if (self_bp->bp->disposition == disp_del
|| self_bp->bp->disposition == disp_del_at_next_stop)
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
/* Python function to determine if the breakpoint is a pending
breakpoint. */
static PyObject *
bppy_get_pending (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
if (is_watchpoint (self_bp->bp))
Py_RETURN_FALSE;
if (pending_breakpoint_p (self_bp->bp))
Py_RETURN_TRUE;
Py_RETURN_FALSE;
}
/* Python function to get the breakpoint's number. */
static PyObject *
bppy_get_number (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
return PyInt_FromLong (self_bp->number);
}
/* Python function to get the breakpoint's thread ID. */
static PyObject *
bppy_get_thread (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
if (self_bp->bp->thread == -1)
Py_RETURN_NONE;
return PyInt_FromLong (self_bp->bp->thread);
}
/* Python function to get the breakpoint's task ID (in Ada). */
static PyObject *
bppy_get_task (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
if (self_bp->bp->task == 0)
Py_RETURN_NONE;
return PyInt_FromLong (self_bp->bp->task);
}
/* Python function to get the breakpoint's hit count. */
static PyObject *
bppy_get_hit_count (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
return PyInt_FromLong (self_bp->bp->hit_count);
}
/* Python function to get the breakpoint's ignore count. */
static PyObject *
bppy_get_ignore_count (PyObject *self, void *closure)
{
gdbpy_breakpoint_object *self_bp = (gdbpy_breakpoint_object *) self;
BPPY_REQUIRE_VALID (self_bp);
return PyInt_FromLong (self_bp->bp->ignore_count);
}
/* Python function to create a new breakpoint. */
static int
bppy_init (PyObject *self, PyObject *args, PyObject *kwargs)
{
static char *keywords[] = { "spec", "type", "wp_class", "internal",
"temporary", NULL };
const char *spec;
int type = bp_breakpoint;
int access_type = hw_write;
PyObject *internal = NULL;
PyObject *temporary = NULL;
int internal_bp = 0;
int temporary_bp = 0;
if (! PyArg_ParseTupleAndKeywords (args, kwargs, "s|iiOO", keywords,
&spec, &type, &access_type,
&internal, &temporary))
return -1;
if (internal)
{
internal_bp = PyObject_IsTrue (internal);
if (internal_bp == -1)
return -1;
}
if (temporary != NULL)
{
temporary_bp = PyObject_IsTrue (temporary);
if (temporary_bp == -1)
return -1;
}
bppy_pending_object = (gdbpy_breakpoint_object *) self;
bppy_pending_object->number = -1;
bppy_pending_object->bp = NULL;
TRY
{
gdb::unique_xmalloc_ptr<char>
copy_holder (xstrdup (skip_spaces_const (spec)));
char *copy = copy_holder.get ();
switch (type)
{
case bp_breakpoint:
{
struct event_location *location;
struct cleanup *cleanup;
location
= string_to_event_location_basic (&copy, current_language);
cleanup = make_cleanup_delete_event_location (location);
create_breakpoint (python_gdbarch,
location, NULL, -1, NULL,
0,
temporary_bp, bp_breakpoint,
0,
AUTO_BOOLEAN_TRUE,
&bkpt_breakpoint_ops,
0, 1, internal_bp, 0);
do_cleanups (cleanup);
break;
}
case bp_watchpoint:
{
if (access_type == hw_write)
watch_command_wrapper (copy, 0, internal_bp);
else if (access_type == hw_access)
awatch_command_wrapper (copy, 0, internal_bp);
else if (access_type == hw_read)
rwatch_command_wrapper (copy, 0, internal_bp);
else
error(_("Cannot understand watchpoint access type."));
break;
}
default:
error(_("Do not understand breakpoint type to set."));
}
}
CATCH (except, RETURN_MASK_ALL)
{
bppy_pending_object = NULL;
PyErr_Format (except.reason == RETURN_QUIT
? PyExc_KeyboardInterrupt : PyExc_RuntimeError,
"%s", except.message);
return -1;
}
END_CATCH
BPPY_SET_REQUIRE_VALID ((gdbpy_breakpoint_object *) self);
return 0;
}
static int
build_bp_list (struct breakpoint *b, void *arg)
{
PyObject *list = (PyObject *) arg;
PyObject *bp = (PyObject *) b->py_bp_object;
int iserr = 0;
/* Not all breakpoints will have a companion Python object.
Only breakpoints that were created via bppy_new, or
breakpoints that were created externally and are tracked by
the Python Scripting API. */
if (bp)
iserr = PyList_Append (list, bp);
if (iserr == -1)
return 1;
return 0;
}
/* Static function to return a tuple holding all breakpoints. */
PyObject *
gdbpy_breakpoints (PyObject *self, PyObject *args)
{
if (bppy_live == 0)
return PyTuple_New (0);
gdbpy_ref list (PyList_New (0));
if (list == NULL)
return NULL;
/* If iterate_over_breakpoints returns non NULL it signals an error
condition. In that case abandon building the list and return
NULL. */
if (iterate_over_breakpoints (build_bp_list, list.get ()) != NULL)
return NULL;
return PyList_AsTuple (list.get ());
}
/* Call the "stop" method (if implemented) in the breakpoint
class. If the method returns True, the inferior will be
stopped at the breakpoint. Otherwise the inferior will be
allowed to continue. */
enum ext_lang_bp_stop
gdbpy_breakpoint_cond_says_stop (const struct extension_language_defn *extlang,
struct breakpoint *b)
{
int stop;
struct gdbpy_breakpoint_object *bp_obj = b->py_bp_object;
PyObject *py_bp = (PyObject *) bp_obj;
struct gdbarch *garch;
if (bp_obj == NULL)
return EXT_LANG_BP_STOP_UNSET;
stop = -1;
garch = b->gdbarch ? b->gdbarch : get_current_arch ();
gdbpy_enter enter_py (garch, current_language);
if (bp_obj->is_finish_bp)
bpfinishpy_pre_stop_hook (bp_obj);
if (PyObject_HasAttrString (py_bp, stop_func))
{
gdbpy_ref result (PyObject_CallMethod (py_bp, stop_func, NULL));
stop = 1;
if (result != NULL)
{
int evaluate = PyObject_IsTrue (result.get ());
if (evaluate == -1)
gdbpy_print_stack ();
/* If the "stop" function returns False that means
the Python breakpoint wants GDB to continue. */
if (! evaluate)
stop = 0;
}
else
gdbpy_print_stack ();
}
if (bp_obj->is_finish_bp)
bpfinishpy_post_stop_hook (bp_obj);
if (stop < 0)
return EXT_LANG_BP_STOP_UNSET;
return stop ? EXT_LANG_BP_STOP_YES : EXT_LANG_BP_STOP_NO;
}
/* Checks if the "stop" method exists in this breakpoint.
Used by condition_command to ensure mutual exclusion of breakpoint
conditions. */
int
gdbpy_breakpoint_has_cond (const struct extension_language_defn *extlang,
struct breakpoint *b)
{
PyObject *py_bp;
struct gdbarch *garch;
if (b->py_bp_object == NULL)
return 0;
py_bp = (PyObject *) b->py_bp_object;
garch = b->gdbarch ? b->gdbarch : get_current_arch ();
gdbpy_enter enter_py (garch, current_language);
return PyObject_HasAttrString (py_bp, stop_func);
}
/* Event callback functions. */
/* Callback that is used when a breakpoint is created. This function
will create a new Python breakpoint object. */
static void
gdbpy_breakpoint_created (struct breakpoint *bp)
{
gdbpy_breakpoint_object *newbp;
PyGILState_STATE state;
if (!user_breakpoint_p (bp) && bppy_pending_object == NULL)
return;
if (bp->type != bp_breakpoint
&& bp->type != bp_watchpoint
&& bp->type != bp_hardware_watchpoint
&& bp->type != bp_read_watchpoint
&& bp->type != bp_access_watchpoint)
return;
state = PyGILState_Ensure ();
if (bppy_pending_object)
{
newbp = bppy_pending_object;
bppy_pending_object = NULL;
}
else
newbp = PyObject_New (gdbpy_breakpoint_object, &breakpoint_object_type);
if (newbp)
{
newbp->number = bp->number;
newbp->bp = bp;
newbp->bp->py_bp_object = newbp;
newbp->is_finish_bp = 0;
Py_INCREF (newbp);
++bppy_live;
}
else
{
PyErr_SetString (PyExc_RuntimeError,
_("Error while creating breakpoint from GDB."));
gdbpy_print_stack ();
}
if (!evregpy_no_listeners_p (gdb_py_events.breakpoint_created))
{
if (evpy_emit_event ((PyObject *) newbp,
gdb_py_events.breakpoint_created) < 0)
gdbpy_print_stack ();
}
PyGILState_Release (state);
}
/* Callback that is used when a breakpoint is deleted. This will
invalidate the corresponding Python object. */
static void
gdbpy_breakpoint_deleted (struct breakpoint *b)
{
int num = b->number;
PyGILState_STATE state;
struct breakpoint *bp = NULL;
gdbpy_breakpoint_object *bp_obj;
state = PyGILState_Ensure ();
bp = get_breakpoint (num);
if (bp)
{
bp_obj = bp->py_bp_object;
if (bp_obj)
{
if (!evregpy_no_listeners_p (gdb_py_events.breakpoint_deleted))
{
if (evpy_emit_event ((PyObject *) bp_obj,
gdb_py_events.breakpoint_deleted) < 0)
gdbpy_print_stack ();
}
bp_obj->bp = NULL;
--bppy_live;
Py_DECREF (bp_obj);
}
}
PyGILState_Release (state);
}
/* Callback that is used when a breakpoint is modified. */
static void
gdbpy_breakpoint_modified (struct breakpoint *b)
{
int num = b->number;
PyGILState_STATE state;
struct breakpoint *bp = NULL;
gdbpy_breakpoint_object *bp_obj;
state = PyGILState_Ensure ();
bp = get_breakpoint (num);
if (bp)
{
PyObject *bp_obj = (PyObject *) bp->py_bp_object;
if (bp_obj)
{
if (!evregpy_no_listeners_p (gdb_py_events.breakpoint_modified))
{
if (evpy_emit_event (bp_obj,
gdb_py_events.breakpoint_modified) < 0)
gdbpy_print_stack ();
}
}
}
PyGILState_Release (state);
}
/* Initialize the Python breakpoint code. */
int
gdbpy_initialize_breakpoints (void)
{
int i;
breakpoint_object_type.tp_new = PyType_GenericNew;
if (PyType_Ready (&breakpoint_object_type) < 0)
return -1;
if (gdb_pymodule_addobject (gdb_module, "Breakpoint",
(PyObject *) &breakpoint_object_type) < 0)
return -1;
observer_attach_breakpoint_created (gdbpy_breakpoint_created);
observer_attach_breakpoint_deleted (gdbpy_breakpoint_deleted);
observer_attach_breakpoint_modified (gdbpy_breakpoint_modified);
/* Add breakpoint types constants. */
for (i = 0; pybp_codes[i].name; ++i)
{
if (PyModule_AddIntConstant (gdb_module,
/* Cast needed for Python 2.4. */
(char *) pybp_codes[i].name,
pybp_codes[i].code) < 0)
return -1;
}
/* Add watchpoint types constants. */
for (i = 0; pybp_watch_types[i].name; ++i)
{
if (PyModule_AddIntConstant (gdb_module,
/* Cast needed for Python 2.4. */
(char *) pybp_watch_types[i].name,
pybp_watch_types[i].code) < 0)
return -1;
}
return 0;
}
/* Helper function that overrides this Python object's
PyObject_GenericSetAttr to allow extra validation of the attribute
being set. */
static int
local_setattro (PyObject *self, PyObject *name, PyObject *v)
{
gdbpy_breakpoint_object *obj = (gdbpy_breakpoint_object *) self;
gdb::unique_xmalloc_ptr<char> attr (python_string_to_host_string (name));
if (attr == NULL)
return -1;
/* If the attribute trying to be set is the "stop" method,
but we already have a condition set in the CLI or other extension
language, disallow this operation. */
if (strcmp (attr.get (), stop_func) == 0)
{
const struct extension_language_defn *extlang = NULL;
if (obj->bp->cond_string != NULL)
extlang = get_ext_lang_defn (EXT_LANG_GDB);
if (extlang == NULL)
extlang = get_breakpoint_cond_ext_lang (obj->bp, EXT_LANG_PYTHON);
if (extlang != NULL)
{
char *error_text;
error_text
= xstrprintf (_("Only one stop condition allowed. There is"
" currently a %s stop condition defined for"
" this breakpoint."),
ext_lang_capitalized_name (extlang));
PyErr_SetString (PyExc_RuntimeError, error_text);
xfree (error_text);
return -1;
}
}
return PyObject_GenericSetAttr ((PyObject *)self, name, v);
}
static PyGetSetDef breakpoint_object_getset[] = {
{ "enabled", bppy_get_enabled, bppy_set_enabled,
"Boolean telling whether the breakpoint is enabled.", NULL },
{ "silent", bppy_get_silent, bppy_set_silent,
"Boolean telling whether the breakpoint is silent.", NULL },
{ "thread", bppy_get_thread, bppy_set_thread,
"Thread ID for the breakpoint.\n\
If the value is a thread ID (integer), then this is a thread-specific breakpoint.\n\
If the value is None, then this breakpoint is not thread-specific.\n\
No other type of value can be used.", NULL },
{ "task", bppy_get_task, bppy_set_task,
"Thread ID for the breakpoint.\n\
If the value is a task ID (integer), then this is an Ada task-specific breakpoint.\n\
If the value is None, then this breakpoint is not task-specific.\n\
No other type of value can be used.", NULL },
{ "ignore_count", bppy_get_ignore_count, bppy_set_ignore_count,
"Number of times this breakpoint should be automatically continued.",
NULL },
{ "number", bppy_get_number, NULL,
"Breakpoint's number assigned by GDB.", NULL },
{ "hit_count", bppy_get_hit_count, bppy_set_hit_count,
"Number of times the breakpoint has been hit.\n\
Can be set to zero to clear the count. No other value is valid\n\
when setting this property.", NULL },
{ "location", bppy_get_location, NULL,
"Location of the breakpoint, as specified by the user.", NULL},
{ "expression", bppy_get_expression, NULL,
"Expression of the breakpoint, as specified by the user.", NULL},
{ "condition", bppy_get_condition, bppy_set_condition,
"Condition of the breakpoint, as specified by the user,\
or None if no condition set."},
{ "commands", bppy_get_commands, NULL,
"Commands of the breakpoint, as specified by the user."},
{ "type", bppy_get_type, NULL,
"Type of breakpoint."},
{ "visible", bppy_get_visibility, NULL,
"Whether the breakpoint is visible to the user."},
{ "temporary", bppy_get_temporary, NULL,
"Whether this breakpoint is a temporary breakpoint."},
{ "pending", bppy_get_pending, NULL,
"Whether this breakpoint is a pending breakpoint."},
{ NULL } /* Sentinel. */
};
static PyMethodDef breakpoint_object_methods[] =
{
{ "is_valid", bppy_is_valid, METH_NOARGS,
"Return true if this breakpoint is valid, false if not." },
{ "delete", bppy_delete_breakpoint, METH_NOARGS,
"Delete the underlying GDB breakpoint." },
{ NULL } /* Sentinel. */
};
PyTypeObject breakpoint_object_type =
{
PyVarObject_HEAD_INIT (NULL, 0)
"gdb.Breakpoint", /*tp_name*/
sizeof (gdbpy_breakpoint_object), /*tp_basicsize*/
0, /*tp_itemsize*/
0, /*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*/
(setattrofunc)local_setattro, /*tp_setattro */
0, /*tp_as_buffer*/
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /*tp_flags*/
"GDB breakpoint object", /* tp_doc */
0, /* tp_traverse */
0, /* tp_clear */
0, /* tp_richcompare */
0, /* tp_weaklistoffset */
0, /* tp_iter */
0, /* tp_iternext */
breakpoint_object_methods, /* tp_methods */
0, /* tp_members */
breakpoint_object_getset, /* tp_getset */
0, /* tp_base */
0, /* tp_dict */
0, /* tp_descr_get */
0, /* tp_descr_set */
0, /* tp_dictoffset */
bppy_init, /* tp_init */
0, /* tp_alloc */
};