mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-05-29 07:10:55 +08:00

This patch teaches GDB about setting breakpoints in all scopes (namespaces and classes) by default. Here's a contrived example: (gdb) b func<tab> (anonymous namespace)::A::function() Bn::(anonymous namespace)::B::function() function(int, int) (anonymous namespace)::B::function() Bn::(anonymous namespace)::function() gdb::(anonymous namespace)::A::function() (anonymous namespace)::B::function() const Bn::(anonymous namespace)::function(int, int) gdb::(anonymous namespace)::function() (anonymous namespace)::function() Bn::B::func() gdb::(anonymous namespace)::function(int, int) (anonymous namespace)::function(int, int) Bn::B::function() gdb::A::func() A::func() Bn::func() gdb::A::function() A::function() Bn::function() gdb::func() B::func() Bn::function(int, int) gdb::function() B::function() Bn::function(long) gdb::function(int, int) B::function() const func() gdb::function(long) B::function_const() const function() (gdb) b function Breakpoint 1 at 0x4005ce: function. (26 locations) (gdb) b B::function<tab> (anonymous namespace)::B::function() B::function() const Bn::B::function() (anonymous namespace)::B::function() const B::function_const() const B::function() Bn::(anonymous namespace)::B::function() (gdb) b B::function Breakpoint 1 at 0x40072c: B::function. (6 locations) To get back the original behavior of interpreting the function name as a fully-qualified name, you can use the new "-qualified" (or "-q") option/flag (added by this commit). For example: (gdb) b B::function (anonymous namespace)::B::function() B::function() const Bn::B::function() (anonymous namespace)::B::function() const B::function_const() const B::function() Bn::(anonymous namespace)::B::function() vs: (gdb) b -qualified B::function B::function() B::function() const B::function_const() const I've chosen "-qualified" / "-q" because "-f" (for "full" or "fully-qualified") is already taken for "-function". Note: the "-qualified" option works with both linespecs and explicit locations. I.e., these are equivalent: (gdb) b -q func (gdb) b -q -f func and so are these: (gdb) b -q filename.cc:func (gdb) b -q -s filename.cc -f func (gdb) b -s filename.cc -q -f func (gdb) b -s filename.cc -f func -q To better understand why I consider wild matching the better default, consider what happens when we get to the point when _all_ of GDB is wrapped under "namespace gdb {}". I have a patch series that does that, and when I started debugging that GDB, I immediately became frustrated. You'd have to write "b gdb::internal_error", "b gdb::foo", "b gdb::bar", etc. etc., which gets annoying pretty quickly. OTOH, consider how this makes it very easy to set breakpoints in classes wrapped in anonymous namespaces. You just don't think of them, GDB finds the symbols for you automatically. (At the Cauldron a couple months ago, several people told me that they run into a similar issue when debugging other C++ projects. One example was when debugging LLVM, which puts all its code under the "llvm" namespace.) Implementation-wise, what the patch does is: - makes C++ symbol name hashing only consider the last component of a symbol name. (so that we can look up symbol names by last-component name only). - adds a C++ symbol name matcher for symbol_name_match_type::WILD, which ignores missing leading specifiers / components. - adjusts a few preexisting testsuite tests to use "-qualified" when they mean it. - adds new testsuite tests. - adds unit tests. Grows the gdb.linespec/ tests like this: -# of expected passes 7823 +# of expected passes 8977 gdb/ChangeLog: 2017-11-29 Pedro Alves <palves@redhat.com> * NEWS: Mention that breakpoints on C++ functions are now set on on all namespaces/classes by default, and mention "break -qualified". * ax-gdb.c (agent_command_1): Adjust to pass a symbol_name_match_type to new_linespec_location. * breakpoint.c (parse_breakpoint_sals): Adjust to get_linespec_location's return type change. (strace_marker_create_sals_from_location): Adjust to pass a symbol_name_match_type to new_linespec_location. (strace_marker_decode_location): Adjust to get_linespec_location's return type change. (strace_command): Adjust to pass a symbol_name_match_type to new_linespec_location. (LOCATION_HELP_STRING): Add paragraph about wildmatching, and mention "-qualified". * c-lang.c (cplus_language_defn): Install cp_search_name_hash. * completer.c (explicit_location_match_type::MATCH_QUALIFIED): New enumerator. (complete_address_and_linespec_locations): New parameter 'match_type'. Pass it down. (explicit_options): Add "-qualified". (collect_explicit_location_matches): Pass the requested match type to the linespec completers. Handle MATCH_QUALIFIED. (location_completer): Handle "-qualified" combined with linespecs. * cp-support.c (cp_search_name_hash): New. (cp_symbol_name_matches_1): Implement wild matching for C++. (cp_fq_symbol_name_matches): Reimplement. (cp_get_symbol_name_matcher): Return different matchers depending on the lookup name's match type. (selftests::test_cp_symbol_name_matches): Add wild matching tests. * cp-support.h (cp_search_name_hash): New declaration. * dwarf2read.c (selftests::dw2_expand_symtabs_matching::test_symbols): Add symbols. (test_dw2_expand_symtabs_matching_symbol): Add wild matching tests. * guile/scm-breakpoint.c (gdbscm_register_breakpoint_x): Adjust to pass a symbol_name_match_type to new_linespec_location. * linespec.c (linespec_parse_basic): Lookup function symbols using the parser's symbol name match type. (convert_explicit_location_to_linespec): New symbol_name_match_type parameter. Pass it down to find_linespec_symbols. (convert_explicit_location_to_sals): Pass the location's name match type to convert_explicit_location_to_linespec. (parse_linespec): New match_type parameter. Save it in the parser. (linespec_parser_new): Default to symbol_name_match_type::WILD. (linespec_complete_function): New symbol_name_match_type parameter. Use it. (complete_linespec_component): Pass down the parser's recorded name match type. (linespec_complete_label): New symbol_name_match_type parameter. Use it. (linespec_complete): New symbol_name_match_type parameter. Save it in the parser and pass it down. Adjust to get_linespec_location's prototype change. (find_function_symbols, find_linespec_symbols): New symbol_name_match_type parameter. Pass it down instead of assuming symbol_name_match_type::WILD. * linespec.h (linespec_complete, linespec_complete_function) (linespec_complete_label): New symbol_name_match_type parameter. * location.c (event_location::linespec_location): Now a struct linespec_location. (EL_LINESPEC): Adjust. (initialize_explicit_location): Default to symbol_name_match_type::WILD. (new_linespec_location): New symbol_name_match_type parameter. Record it in the location. (get_linespec_location): Now returns a struct linespec_location. (new_explicit_location): Also copy func_name_match_type. (explicit_to_string_internal) (string_to_explicit_location): Handle "-qualified". (copy_event_location): Adjust to LINESPEC_LOCATION type change. Copy symbol_name_match_type fields. (event_location_deleter::operator()): Adjust to LINESPEC_LOCATION type change. (event_location_to_string): Adjust to LINESPEC_LOCATION type change. Handle "-qualfied". (string_to_explicit_location): Handle "-qualified". (string_to_event_location_basic): New symbol_name_match_type parameter. Pass it down. (string_to_event_location): Handle "-qualified". * location.h (struct linespec_location): New. (explicit_location::func_name_match_type): New field. (new_linespec_location): Now returns a const linespec_location *. (string_to_event_location_basic): New symbol_name_match_type parameter. (explicit_completion_info::saw_explicit_location_option): New field. * mi/mi-cmd-break.c (mi_cmd_break_insert_1): Adjust to pass a symbol_name_match_type to new_linespec_location. * python/py-breakpoint.c (bppy_init): Likewise. * python/python.c (gdbpy_decode_line): Likewise. gdb/testsuite/ChangeLog: 2017-11-29 Pedro Alves <palves@redhat.com> * gdb.base/langs.exp: Use -qualified. * gdb.cp/meth-typedefs.exp: Use -qualified, and add tests without it. * gdb.cp/namespace.exp: Use -qualified. * gdb.linespec/cpcompletion.exp (overload-2, fqn, fqn-2) (overload-3, template-overload, template-ret-type, const-overload) (const-overload-quoted, anon-ns, ambiguous-prefix): New procedures. (test_driver): Call them. * gdb.cp/save-bp-qualified.cc: New. * gdb.cp/save-bp-qualified.exp: New. * gdb.linespec/explicit.exp: Test -qualified. * lib/completion-support.exp (completion::explicit_opts_list): Add "-qualified". * lib/gdb.exp (gdb_breakpoint): Handle "qualified". gdb/doc/ChangeLog: 2017-11-29 Pedro Alves <palves@redhat.com> * gdb.texinfo (Linespec Locations): Document how "function" is interpreted in C++ and Ada. Document "-qualified". (Explicit Locations): Document how "-function" is interpreted in C++ and Ada. Document "-qualified".
1136 lines
28 KiB
C
1136 lines
28 KiB
C
/* 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 const char 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.get ());
|
||
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)
|
||
{
|
||
const 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 const 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 (!gdb_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 (spec)));
|
||
const char *copy = copy_holder.get ();
|
||
|
||
switch (type)
|
||
{
|
||
case bp_breakpoint:
|
||
{
|
||
event_location_up location
|
||
= string_to_event_location_basic (©, current_language,
|
||
symbol_name_match_type::WILD);
|
||
create_breakpoint (python_gdbarch,
|
||
location.get (), NULL, -1, NULL,
|
||
0,
|
||
temporary_bp, bp_breakpoint,
|
||
0,
|
||
AUTO_BOOLEAN_TRUE,
|
||
&bkpt_breakpoint_ops,
|
||
0, 1, internal_bp, 0);
|
||
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;
|
||
|
||
state = PyGILState_Ensure ();
|
||
bp = get_breakpoint (num);
|
||
if (bp)
|
||
{
|
||
gdbpy_ref<gdbpy_breakpoint_object> bp_obj (bp->py_bp_object);
|
||
if (bp_obj != NULL)
|
||
{
|
||
if (!evregpy_no_listeners_p (gdb_py_events.breakpoint_deleted))
|
||
{
|
||
if (evpy_emit_event ((PyObject *) bp_obj.get (),
|
||
gdb_py_events.breakpoint_deleted) < 0)
|
||
gdbpy_print_stack ();
|
||
}
|
||
|
||
bp_obj->bp = NULL;
|
||
--bppy_live;
|
||
}
|
||
}
|
||
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)
|
||
{
|
||
std::string error_text
|
||
= string_printf (_("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.c_str ());
|
||
return -1;
|
||
}
|
||
}
|
||
|
||
return PyObject_GenericSetAttr ((PyObject *)self, name, v);
|
||
}
|
||
|
||
static gdb_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 */
|
||
};
|