mirror of
https://github.com/espressif/binutils-gdb.git
synced 2025-05-24 02:37:23 +08:00

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.
780 lines
23 KiB
C
780 lines
23 KiB
C
/* Python frame unwinder interface.
|
|
|
|
Copyright (C) 2015-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 "arch-utils.h"
|
|
#include "frame-unwind.h"
|
|
#include "gdb_obstack.h"
|
|
#include "gdbcmd.h"
|
|
#include "language.h"
|
|
#include "observer.h"
|
|
#include "python-internal.h"
|
|
#include "regcache.h"
|
|
#include "valprint.h"
|
|
#include "user-regs.h"
|
|
#include "py-ref.h"
|
|
|
|
#define TRACE_PY_UNWIND(level, args...) if (pyuw_debug >= level) \
|
|
{ fprintf_unfiltered (gdb_stdlog, args); }
|
|
|
|
typedef struct
|
|
{
|
|
PyObject_HEAD
|
|
|
|
/* Frame we are unwinding. */
|
|
struct frame_info *frame_info;
|
|
|
|
/* Its architecture, passed by the sniffer caller. */
|
|
struct gdbarch *gdbarch;
|
|
} pending_frame_object;
|
|
|
|
/* Saved registers array item. */
|
|
|
|
typedef struct
|
|
{
|
|
int number;
|
|
PyObject *value;
|
|
} saved_reg;
|
|
DEF_VEC_O (saved_reg);
|
|
|
|
/* The data we keep for the PyUnwindInfo: pending_frame, saved registers
|
|
and frame ID. */
|
|
|
|
typedef struct
|
|
{
|
|
PyObject_HEAD
|
|
|
|
/* gdb.PendingFrame for the frame we are unwinding. */
|
|
PyObject *pending_frame;
|
|
|
|
/* Its ID. */
|
|
struct frame_id frame_id;
|
|
|
|
/* Saved registers array. */
|
|
VEC (saved_reg) *saved_regs;
|
|
} unwind_info_object;
|
|
|
|
/* The data we keep for a frame we can unwind: frame ID and an array of
|
|
(register_number, register_value) pairs. */
|
|
|
|
struct reg_info
|
|
{
|
|
/* Register number. */
|
|
int number;
|
|
|
|
/* Register data bytes pointer. */
|
|
gdb_byte data[MAX_REGISTER_SIZE];
|
|
};
|
|
|
|
typedef struct
|
|
{
|
|
/* Frame ID. */
|
|
struct frame_id frame_id;
|
|
|
|
/* GDB Architecture. */
|
|
struct gdbarch *gdbarch;
|
|
|
|
/* Length of the `reg' array below. */
|
|
int reg_count;
|
|
|
|
struct reg_info reg[];
|
|
} cached_frame_info;
|
|
|
|
extern PyTypeObject pending_frame_object_type
|
|
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("pending_frame_object");
|
|
|
|
extern PyTypeObject unwind_info_object_type
|
|
CPYCHECKER_TYPE_OBJECT_FOR_TYPEDEF ("unwind_info_object");
|
|
|
|
static unsigned int pyuw_debug = 0;
|
|
|
|
static struct gdbarch_data *pyuw_gdbarch_data;
|
|
|
|
/* Parses register id, which can be either a number or a name.
|
|
Returns 1 on success, 0 otherwise. */
|
|
|
|
static int
|
|
pyuw_parse_register_id (struct gdbarch *gdbarch, PyObject *pyo_reg_id,
|
|
int *reg_num)
|
|
{
|
|
if (pyo_reg_id == NULL)
|
|
return 0;
|
|
if (gdbpy_is_string (pyo_reg_id))
|
|
{
|
|
gdb::unique_xmalloc_ptr<char> reg_name (gdbpy_obj_to_string (pyo_reg_id));
|
|
|
|
if (reg_name == NULL)
|
|
return 0;
|
|
*reg_num = user_reg_map_name_to_regnum (gdbarch, reg_name.get (),
|
|
strlen (reg_name.get ()));
|
|
return *reg_num >= 0;
|
|
}
|
|
else if (PyInt_Check (pyo_reg_id))
|
|
{
|
|
long value;
|
|
if (gdb_py_int_as_long (pyo_reg_id, &value) && (int) value == value)
|
|
{
|
|
*reg_num = (int) value;
|
|
return user_reg_map_regnum_to_name (gdbarch, *reg_num) != NULL;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Convert gdb.Value instance to inferior's pointer. Return 1 on success,
|
|
0 on failure. */
|
|
|
|
static int
|
|
pyuw_value_obj_to_pointer (PyObject *pyo_value, CORE_ADDR *addr)
|
|
{
|
|
int rc = 0;
|
|
struct value *value;
|
|
|
|
TRY
|
|
{
|
|
if ((value = value_object_to_value (pyo_value)) != NULL)
|
|
{
|
|
*addr = unpack_pointer (value_type (value),
|
|
value_contents (value));
|
|
rc = 1;
|
|
}
|
|
}
|
|
CATCH (except, RETURN_MASK_ALL)
|
|
{
|
|
gdbpy_convert_exception (except);
|
|
}
|
|
END_CATCH
|
|
return rc;
|
|
}
|
|
|
|
/* Get attribute from an object and convert it to the inferior's
|
|
pointer value. Return 1 if attribute exists and its value can be
|
|
converted. Otherwise, if attribute does not exist or its value is
|
|
None, return 0. In all other cases set Python error and return
|
|
0. */
|
|
|
|
static int
|
|
pyuw_object_attribute_to_pointer (PyObject *pyo, const char *attr_name,
|
|
CORE_ADDR *addr)
|
|
{
|
|
int rc = 0;
|
|
|
|
if (PyObject_HasAttrString (pyo, attr_name))
|
|
{
|
|
gdbpy_ref pyo_value (PyObject_GetAttrString (pyo, attr_name));
|
|
|
|
if (pyo_value != NULL && pyo_value != Py_None)
|
|
{
|
|
rc = pyuw_value_obj_to_pointer (pyo_value.get (), addr);
|
|
if (!rc)
|
|
PyErr_Format (
|
|
PyExc_ValueError,
|
|
_("The value of the '%s' attribute is not a pointer."),
|
|
attr_name);
|
|
}
|
|
}
|
|
return rc;
|
|
}
|
|
|
|
/* Called by the Python interpreter to obtain string representation
|
|
of the UnwindInfo object. */
|
|
|
|
static PyObject *
|
|
unwind_infopy_str (PyObject *self)
|
|
{
|
|
unwind_info_object *unwind_info = (unwind_info_object *) self;
|
|
string_file stb;
|
|
|
|
stb.puts ("Frame ID: ");
|
|
fprint_frame_id (&stb, unwind_info->frame_id);
|
|
{
|
|
char *sep = "";
|
|
int i;
|
|
struct value_print_options opts;
|
|
saved_reg *reg;
|
|
|
|
get_user_print_options (&opts);
|
|
stb.printf ("\nSaved registers: (");
|
|
for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++)
|
|
{
|
|
struct value *value = value_object_to_value (reg->value);
|
|
|
|
stb.printf ("%s(%d, ", sep, reg->number);
|
|
if (value != NULL)
|
|
{
|
|
TRY
|
|
{
|
|
value_print (value, &stb, &opts);
|
|
stb.puts (")");
|
|
}
|
|
CATCH (except, RETURN_MASK_ALL)
|
|
{
|
|
GDB_PY_HANDLE_EXCEPTION (except);
|
|
}
|
|
END_CATCH
|
|
}
|
|
else
|
|
stb.puts ("<BAD>)");
|
|
sep = ", ";
|
|
}
|
|
stb.puts (")");
|
|
}
|
|
|
|
return PyString_FromString (stb.c_str ());
|
|
}
|
|
|
|
/* Create UnwindInfo instance for given PendingFrame and frame ID.
|
|
Sets Python error and returns NULL on error. */
|
|
|
|
static PyObject *
|
|
pyuw_create_unwind_info (PyObject *pyo_pending_frame,
|
|
struct frame_id frame_id)
|
|
{
|
|
unwind_info_object *unwind_info
|
|
= PyObject_New (unwind_info_object, &unwind_info_object_type);
|
|
|
|
if (((pending_frame_object *) pyo_pending_frame)->frame_info == NULL)
|
|
{
|
|
PyErr_SetString (PyExc_ValueError,
|
|
"Attempting to use stale PendingFrame");
|
|
return NULL;
|
|
}
|
|
unwind_info->frame_id = frame_id;
|
|
Py_INCREF (pyo_pending_frame);
|
|
unwind_info->pending_frame = pyo_pending_frame;
|
|
unwind_info->saved_regs = VEC_alloc (saved_reg, 4);
|
|
return (PyObject *) unwind_info;
|
|
}
|
|
|
|
/* The implementation of
|
|
gdb.UnwindInfo.add_saved_register (REG, VALUE) -> None. */
|
|
|
|
static PyObject *
|
|
unwind_infopy_add_saved_register (PyObject *self, PyObject *args)
|
|
{
|
|
unwind_info_object *unwind_info = (unwind_info_object *) self;
|
|
pending_frame_object *pending_frame
|
|
= (pending_frame_object *) (unwind_info->pending_frame);
|
|
PyObject *pyo_reg_id;
|
|
PyObject *pyo_reg_value;
|
|
int regnum;
|
|
|
|
if (pending_frame->frame_info == NULL)
|
|
{
|
|
PyErr_SetString (PyExc_ValueError,
|
|
"UnwindInfo instance refers to a stale PendingFrame");
|
|
return NULL;
|
|
}
|
|
if (!PyArg_UnpackTuple (args, "previous_frame_register", 2, 2,
|
|
&pyo_reg_id, &pyo_reg_value))
|
|
return NULL;
|
|
if (!pyuw_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num))
|
|
{
|
|
PyErr_SetString (PyExc_ValueError, "Bad register");
|
|
return NULL;
|
|
}
|
|
{
|
|
struct value *value;
|
|
size_t data_size;
|
|
|
|
if (pyo_reg_value == NULL
|
|
|| (value = value_object_to_value (pyo_reg_value)) == NULL)
|
|
{
|
|
PyErr_SetString (PyExc_ValueError, "Bad register value");
|
|
return NULL;
|
|
}
|
|
data_size = register_size (pending_frame->gdbarch, regnum);
|
|
if (data_size != TYPE_LENGTH (value_type (value)))
|
|
{
|
|
PyErr_Format (
|
|
PyExc_ValueError,
|
|
"The value of the register returned by the Python "
|
|
"sniffer has unexpected size: %u instead of %u.",
|
|
(unsigned) TYPE_LENGTH (value_type (value)),
|
|
(unsigned) data_size);
|
|
return NULL;
|
|
}
|
|
}
|
|
{
|
|
int i;
|
|
saved_reg *reg;
|
|
|
|
for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++)
|
|
{
|
|
if (regnum == reg->number)
|
|
{
|
|
Py_DECREF (reg->value);
|
|
break;
|
|
}
|
|
}
|
|
if (reg == NULL)
|
|
{
|
|
reg = VEC_safe_push (saved_reg, unwind_info->saved_regs, NULL);
|
|
reg->number = regnum;
|
|
}
|
|
Py_INCREF (pyo_reg_value);
|
|
reg->value = pyo_reg_value;
|
|
}
|
|
Py_RETURN_NONE;
|
|
}
|
|
|
|
/* UnwindInfo cleanup. */
|
|
|
|
static void
|
|
unwind_infopy_dealloc (PyObject *self)
|
|
{
|
|
unwind_info_object *unwind_info = (unwind_info_object *) self;
|
|
int i;
|
|
saved_reg *reg;
|
|
|
|
Py_XDECREF (unwind_info->pending_frame);
|
|
for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++)
|
|
Py_DECREF (reg->value);
|
|
VEC_free (saved_reg, unwind_info->saved_regs);
|
|
Py_TYPE (self)->tp_free (self);
|
|
}
|
|
|
|
/* Called by the Python interpreter to obtain string representation
|
|
of the PendingFrame object. */
|
|
|
|
static PyObject *
|
|
pending_framepy_str (PyObject *self)
|
|
{
|
|
struct frame_info *frame = ((pending_frame_object *) self)->frame_info;
|
|
const char *sp_str = NULL;
|
|
const char *pc_str = NULL;
|
|
|
|
if (frame == NULL)
|
|
return PyString_FromString ("Stale PendingFrame instance");
|
|
TRY
|
|
{
|
|
sp_str = core_addr_to_string_nz (get_frame_sp (frame));
|
|
pc_str = core_addr_to_string_nz (get_frame_pc (frame));
|
|
}
|
|
CATCH (except, RETURN_MASK_ALL)
|
|
{
|
|
GDB_PY_HANDLE_EXCEPTION (except);
|
|
}
|
|
END_CATCH
|
|
|
|
return PyString_FromFormat ("SP=%s,PC=%s", sp_str, pc_str);
|
|
}
|
|
|
|
/* Implementation of gdb.PendingFrame.read_register (self, reg) -> gdb.Value.
|
|
Returns the value of register REG as gdb.Value instance. */
|
|
|
|
static PyObject *
|
|
pending_framepy_read_register (PyObject *self, PyObject *args)
|
|
{
|
|
pending_frame_object *pending_frame = (pending_frame_object *) self;
|
|
struct value *val = NULL;
|
|
int regnum;
|
|
PyObject *pyo_reg_id;
|
|
|
|
if (pending_frame->frame_info == NULL)
|
|
{
|
|
PyErr_SetString (PyExc_ValueError,
|
|
"Attempting to read register from stale PendingFrame");
|
|
return NULL;
|
|
}
|
|
if (!PyArg_UnpackTuple (args, "read_register", 1, 1, &pyo_reg_id))
|
|
return NULL;
|
|
if (!pyuw_parse_register_id (pending_frame->gdbarch, pyo_reg_id, ®num))
|
|
{
|
|
PyErr_SetString (PyExc_ValueError, "Bad register");
|
|
return NULL;
|
|
}
|
|
|
|
TRY
|
|
{
|
|
/* Fetch the value associated with a register, whether it's
|
|
a real register or a so called "user" register, like "pc",
|
|
which maps to a real register. In the past,
|
|
get_frame_register_value() was used here, which did not
|
|
handle the user register case. */
|
|
val = value_of_register (regnum, pending_frame->frame_info);
|
|
if (val == NULL)
|
|
PyErr_Format (PyExc_ValueError,
|
|
"Cannot read register %d from frame.",
|
|
regnum);
|
|
}
|
|
CATCH (except, RETURN_MASK_ALL)
|
|
{
|
|
GDB_PY_HANDLE_EXCEPTION (except);
|
|
}
|
|
END_CATCH
|
|
|
|
return val == NULL ? NULL : value_to_value_object (val);
|
|
}
|
|
|
|
/* Implementation of
|
|
PendingFrame.create_unwind_info (self, frameId) -> UnwindInfo. */
|
|
|
|
static PyObject *
|
|
pending_framepy_create_unwind_info (PyObject *self, PyObject *args)
|
|
{
|
|
PyObject *pyo_frame_id;
|
|
CORE_ADDR sp;
|
|
CORE_ADDR pc;
|
|
CORE_ADDR special;
|
|
|
|
if (!PyArg_ParseTuple (args, "O:create_unwind_info", &pyo_frame_id))
|
|
return NULL;
|
|
if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "sp", &sp))
|
|
{
|
|
PyErr_SetString (PyExc_ValueError,
|
|
_("frame_id should have 'sp' attribute."));
|
|
return NULL;
|
|
}
|
|
|
|
/* The logic of building frame_id depending on the attributes of
|
|
the frame_id object:
|
|
Has Has Has Function to call
|
|
'sp'? 'pc'? 'special'?
|
|
------|------|--------------|-------------------------
|
|
Y N * frame_id_build_wild (sp)
|
|
Y Y N frame_id_build (sp, pc)
|
|
Y Y Y frame_id_build_special (sp, pc, special)
|
|
*/
|
|
if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "pc", &pc))
|
|
return pyuw_create_unwind_info (self, frame_id_build_wild (sp));
|
|
if (!pyuw_object_attribute_to_pointer (pyo_frame_id, "special", &special))
|
|
return pyuw_create_unwind_info (self, frame_id_build (sp, pc));
|
|
else
|
|
return pyuw_create_unwind_info (self,
|
|
frame_id_build_special (sp, pc, special));
|
|
}
|
|
|
|
/* frame_unwind.this_id method. */
|
|
|
|
static void
|
|
pyuw_this_id (struct frame_info *this_frame, void **cache_ptr,
|
|
struct frame_id *this_id)
|
|
{
|
|
*this_id = ((cached_frame_info *) *cache_ptr)->frame_id;
|
|
if (pyuw_debug >= 1)
|
|
{
|
|
fprintf_unfiltered (gdb_stdlog, "%s: frame_id: ", __FUNCTION__);
|
|
fprint_frame_id (gdb_stdlog, *this_id);
|
|
fprintf_unfiltered (gdb_stdlog, "\n");
|
|
}
|
|
}
|
|
|
|
/* frame_unwind.prev_register. */
|
|
|
|
static struct value *
|
|
pyuw_prev_register (struct frame_info *this_frame, void **cache_ptr,
|
|
int regnum)
|
|
{
|
|
cached_frame_info *cached_frame = (cached_frame_info *) *cache_ptr;
|
|
struct reg_info *reg_info = cached_frame->reg;
|
|
struct reg_info *reg_info_end = reg_info + cached_frame->reg_count;
|
|
|
|
TRACE_PY_UNWIND (1, "%s (frame=%p,...,reg=%d)\n", __FUNCTION__, this_frame,
|
|
regnum);
|
|
for (; reg_info < reg_info_end; ++reg_info)
|
|
{
|
|
if (regnum == reg_info->number)
|
|
return frame_unwind_got_bytes (this_frame, regnum, reg_info->data);
|
|
}
|
|
|
|
return frame_unwind_got_optimized (this_frame, regnum);
|
|
}
|
|
|
|
/* Frame sniffer dispatch. */
|
|
|
|
static int
|
|
pyuw_sniffer (const struct frame_unwind *self, struct frame_info *this_frame,
|
|
void **cache_ptr)
|
|
{
|
|
struct gdbarch *gdbarch = (struct gdbarch *) (self->unwind_data);
|
|
cached_frame_info *cached_frame;
|
|
|
|
gdbpy_enter enter_py (gdbarch, current_language);
|
|
|
|
TRACE_PY_UNWIND (3, "%s (SP=%s, PC=%s)\n", __FUNCTION__,
|
|
paddress (gdbarch, get_frame_sp (this_frame)),
|
|
paddress (gdbarch, get_frame_pc (this_frame)));
|
|
|
|
/* Create PendingFrame instance to pass to sniffers. */
|
|
pending_frame_object *pfo = PyObject_New (pending_frame_object,
|
|
&pending_frame_object_type);
|
|
gdbpy_ref pyo_pending_frame ((PyObject *) pfo);
|
|
if (pyo_pending_frame == NULL)
|
|
{
|
|
gdbpy_print_stack ();
|
|
return 0;
|
|
}
|
|
pfo->gdbarch = gdbarch;
|
|
scoped_restore invalidate_frame = make_scoped_restore (&pfo->frame_info,
|
|
this_frame);
|
|
|
|
/* Run unwinders. */
|
|
if (gdb_python_module == NULL
|
|
|| ! PyObject_HasAttrString (gdb_python_module, "execute_unwinders"))
|
|
{
|
|
PyErr_SetString (PyExc_NameError,
|
|
"Installation error: gdb.execute_unwinders function "
|
|
"is missing");
|
|
gdbpy_print_stack ();
|
|
return 0;
|
|
}
|
|
gdbpy_ref pyo_execute (PyObject_GetAttrString (gdb_python_module,
|
|
"execute_unwinders"));
|
|
if (pyo_execute == NULL)
|
|
{
|
|
gdbpy_print_stack ();
|
|
return 0;
|
|
}
|
|
|
|
gdbpy_ref pyo_unwind_info
|
|
(PyObject_CallFunctionObjArgs (pyo_execute.get (),
|
|
pyo_pending_frame.get (), NULL));
|
|
if (pyo_unwind_info == NULL)
|
|
{
|
|
gdbpy_print_stack ();
|
|
return 0;
|
|
}
|
|
if (pyo_unwind_info == Py_None)
|
|
return 0;
|
|
|
|
/* Received UnwindInfo, cache data. */
|
|
if (PyObject_IsInstance (pyo_unwind_info.get (),
|
|
(PyObject *) &unwind_info_object_type) <= 0)
|
|
error (_("A Unwinder should return gdb.UnwindInfo instance."));
|
|
|
|
{
|
|
unwind_info_object *unwind_info =
|
|
(unwind_info_object *) pyo_unwind_info.get ();
|
|
int reg_count = VEC_length (saved_reg, unwind_info->saved_regs);
|
|
saved_reg *reg;
|
|
int i;
|
|
|
|
cached_frame
|
|
= ((cached_frame_info *)
|
|
xmalloc (sizeof (*cached_frame)
|
|
+ reg_count * sizeof (cached_frame->reg[0])));
|
|
cached_frame->gdbarch = gdbarch;
|
|
cached_frame->frame_id = unwind_info->frame_id;
|
|
cached_frame->reg_count = reg_count;
|
|
|
|
/* Populate registers array. */
|
|
for (i = 0; VEC_iterate (saved_reg, unwind_info->saved_regs, i, reg); i++)
|
|
{
|
|
struct value *value = value_object_to_value (reg->value);
|
|
size_t data_size = register_size (gdbarch, reg->number);
|
|
|
|
cached_frame->reg[i].number = reg->number;
|
|
|
|
/* `value' validation was done before, just assert. */
|
|
gdb_assert (value != NULL);
|
|
gdb_assert (data_size == TYPE_LENGTH (value_type (value)));
|
|
gdb_assert (data_size <= MAX_REGISTER_SIZE);
|
|
|
|
memcpy (cached_frame->reg[i].data, value_contents (value), data_size);
|
|
}
|
|
}
|
|
|
|
*cache_ptr = cached_frame;
|
|
return 1;
|
|
}
|
|
|
|
/* Frame cache release shim. */
|
|
|
|
static void
|
|
pyuw_dealloc_cache (struct frame_info *this_frame, void *cache)
|
|
{
|
|
TRACE_PY_UNWIND (3, "%s: enter", __FUNCTION__);
|
|
xfree (cache);
|
|
}
|
|
|
|
struct pyuw_gdbarch_data_type
|
|
{
|
|
/* Has the unwinder shim been prepended? */
|
|
int unwinder_registered;
|
|
};
|
|
|
|
static void *
|
|
pyuw_gdbarch_data_init (struct gdbarch *gdbarch)
|
|
{
|
|
return GDBARCH_OBSTACK_ZALLOC (gdbarch, struct pyuw_gdbarch_data_type);
|
|
}
|
|
|
|
/* New inferior architecture callback: register the Python unwinders
|
|
intermediary. */
|
|
|
|
static void
|
|
pyuw_on_new_gdbarch (struct gdbarch *newarch)
|
|
{
|
|
struct pyuw_gdbarch_data_type *data
|
|
= (struct pyuw_gdbarch_data_type *) gdbarch_data (newarch,
|
|
pyuw_gdbarch_data);
|
|
|
|
if (!data->unwinder_registered)
|
|
{
|
|
struct frame_unwind *unwinder
|
|
= GDBARCH_OBSTACK_ZALLOC (newarch, struct frame_unwind);
|
|
|
|
unwinder->type = NORMAL_FRAME;
|
|
unwinder->stop_reason = default_frame_unwind_stop_reason;
|
|
unwinder->this_id = pyuw_this_id;
|
|
unwinder->prev_register = pyuw_prev_register;
|
|
unwinder->unwind_data = (const struct frame_data *) newarch;
|
|
unwinder->sniffer = pyuw_sniffer;
|
|
unwinder->dealloc_cache = pyuw_dealloc_cache;
|
|
frame_unwind_prepend_unwinder (newarch, unwinder);
|
|
data->unwinder_registered = 1;
|
|
}
|
|
}
|
|
|
|
/* Initialize unwind machinery. */
|
|
|
|
int
|
|
gdbpy_initialize_unwind (void)
|
|
{
|
|
int rc;
|
|
add_setshow_zuinteger_cmd
|
|
("py-unwind", class_maintenance, &pyuw_debug,
|
|
_("Set Python unwinder debugging."),
|
|
_("Show Python unwinder debugging."),
|
|
_("When non-zero, Python unwinder debugging is enabled."),
|
|
NULL,
|
|
NULL,
|
|
&setdebuglist, &showdebuglist);
|
|
pyuw_gdbarch_data
|
|
= gdbarch_data_register_post_init (pyuw_gdbarch_data_init);
|
|
observer_attach_architecture_changed (pyuw_on_new_gdbarch);
|
|
|
|
if (PyType_Ready (&pending_frame_object_type) < 0)
|
|
return -1;
|
|
rc = gdb_pymodule_addobject (gdb_module, "PendingFrame",
|
|
(PyObject *) &pending_frame_object_type);
|
|
if (rc)
|
|
return rc;
|
|
|
|
if (PyType_Ready (&unwind_info_object_type) < 0)
|
|
return -1;
|
|
return gdb_pymodule_addobject (gdb_module, "UnwindInfo",
|
|
(PyObject *) &unwind_info_object_type);
|
|
}
|
|
|
|
static PyMethodDef pending_frame_object_methods[] =
|
|
{
|
|
{ "read_register", pending_framepy_read_register, METH_VARARGS,
|
|
"read_register (REG) -> gdb.Value\n"
|
|
"Return the value of the REG in the frame." },
|
|
{ "create_unwind_info",
|
|
pending_framepy_create_unwind_info, METH_VARARGS,
|
|
"create_unwind_info (FRAME_ID) -> gdb.UnwindInfo\n"
|
|
"Construct UnwindInfo for this PendingFrame, using FRAME_ID\n"
|
|
"to identify it." },
|
|
{NULL} /* Sentinel */
|
|
};
|
|
|
|
PyTypeObject pending_frame_object_type =
|
|
{
|
|
PyVarObject_HEAD_INIT (NULL, 0)
|
|
"gdb.PendingFrame", /* tp_name */
|
|
sizeof (pending_frame_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 */
|
|
pending_framepy_str, /* tp_str */
|
|
0, /* tp_getattro */
|
|
0, /* tp_setattro */
|
|
0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT, /* tp_flags */
|
|
"GDB PendingFrame object", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
pending_frame_object_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
0, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
0, /* tp_init */
|
|
0, /* tp_alloc */
|
|
};
|
|
|
|
static PyMethodDef unwind_info_object_methods[] =
|
|
{
|
|
{ "add_saved_register",
|
|
unwind_infopy_add_saved_register, METH_VARARGS,
|
|
"add_saved_register (REG, VALUE) -> None\n"
|
|
"Set the value of the REG in the previous frame to VALUE." },
|
|
{ NULL } /* Sentinel */
|
|
};
|
|
|
|
PyTypeObject unwind_info_object_type =
|
|
{
|
|
PyVarObject_HEAD_INIT (NULL, 0)
|
|
"gdb.UnwindInfo", /* tp_name */
|
|
sizeof (unwind_info_object), /* tp_basicsize */
|
|
0, /* tp_itemsize */
|
|
unwind_infopy_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 */
|
|
unwind_infopy_str, /* tp_str */
|
|
0, /* tp_getattro */
|
|
0, /* tp_setattro */
|
|
0, /* tp_as_buffer */
|
|
Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, /* tp_flags */
|
|
"GDB UnwindInfo object", /* tp_doc */
|
|
0, /* tp_traverse */
|
|
0, /* tp_clear */
|
|
0, /* tp_richcompare */
|
|
0, /* tp_weaklistoffset */
|
|
0, /* tp_iter */
|
|
0, /* tp_iternext */
|
|
unwind_info_object_methods, /* tp_methods */
|
|
0, /* tp_members */
|
|
0, /* tp_getset */
|
|
0, /* tp_base */
|
|
0, /* tp_dict */
|
|
0, /* tp_descr_get */
|
|
0, /* tp_descr_set */
|
|
0, /* tp_dictoffset */
|
|
0, /* tp_init */
|
|
0, /* tp_alloc */
|
|
};
|