d7e747318f
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.
431 lines
12 KiB
C
431 lines
12 KiB
C
/* Code dealing with dummy stack frames, for GDB, the GNU debugger.
|
|
|
|
Copyright (C) 1986-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 "dummy-frame.h"
|
|
#include "regcache.h"
|
|
#include "frame.h"
|
|
#include "inferior.h"
|
|
#include "frame-unwind.h"
|
|
#include "command.h"
|
|
#include "gdbcmd.h"
|
|
#include "observer.h"
|
|
#include "gdbthread.h"
|
|
#include "infcall.h"
|
|
|
|
struct dummy_frame_id
|
|
{
|
|
/* This frame's ID. Must match the value returned by
|
|
gdbarch_dummy_id. */
|
|
struct frame_id id;
|
|
|
|
/* The thread this dummy_frame relates to. */
|
|
ptid_t ptid;
|
|
};
|
|
|
|
/* Return whether dummy_frame_id *ID1 and *ID2 are equal. */
|
|
|
|
static int
|
|
dummy_frame_id_eq (struct dummy_frame_id *id1,
|
|
struct dummy_frame_id *id2)
|
|
{
|
|
return frame_id_eq (id1->id, id2->id) && ptid_equal (id1->ptid, id2->ptid);
|
|
}
|
|
|
|
/* List of dummy_frame destructors. */
|
|
|
|
struct dummy_frame_dtor_list
|
|
{
|
|
/* Next element in the list or NULL if this is the last element. */
|
|
struct dummy_frame_dtor_list *next;
|
|
|
|
/* If non-NULL, a destructor that is run when this dummy frame is freed. */
|
|
dummy_frame_dtor_ftype *dtor;
|
|
|
|
/* Arbitrary data that is passed to DTOR. */
|
|
void *dtor_data;
|
|
};
|
|
|
|
/* Dummy frame. This saves the processor state just prior to setting
|
|
up the inferior function call. Older targets save the registers
|
|
on the target stack (but that really slows down function calls). */
|
|
|
|
struct dummy_frame
|
|
{
|
|
struct dummy_frame *next;
|
|
|
|
/* An id represents a dummy frame. */
|
|
struct dummy_frame_id id;
|
|
|
|
/* The caller's state prior to the call. */
|
|
struct infcall_suspend_state *caller_state;
|
|
|
|
/* First element of destructors list or NULL if there are no
|
|
destructors registered for this dummy_frame. */
|
|
struct dummy_frame_dtor_list *dtor_list;
|
|
};
|
|
|
|
static struct dummy_frame *dummy_frame_stack = NULL;
|
|
|
|
/* Push the caller's state, along with the dummy frame info, onto the
|
|
dummy-frame stack. */
|
|
|
|
void
|
|
dummy_frame_push (struct infcall_suspend_state *caller_state,
|
|
const struct frame_id *dummy_id, ptid_t ptid)
|
|
{
|
|
struct dummy_frame *dummy_frame;
|
|
|
|
dummy_frame = XCNEW (struct dummy_frame);
|
|
dummy_frame->caller_state = caller_state;
|
|
dummy_frame->id.id = (*dummy_id);
|
|
dummy_frame->id.ptid = ptid;
|
|
dummy_frame->next = dummy_frame_stack;
|
|
dummy_frame_stack = dummy_frame;
|
|
}
|
|
|
|
/* Remove *DUMMY_PTR from the dummy frame stack. */
|
|
|
|
static void
|
|
remove_dummy_frame (struct dummy_frame **dummy_ptr)
|
|
{
|
|
struct dummy_frame *dummy = *dummy_ptr;
|
|
|
|
while (dummy->dtor_list != NULL)
|
|
{
|
|
struct dummy_frame_dtor_list *list = dummy->dtor_list;
|
|
|
|
dummy->dtor_list = list->next;
|
|
list->dtor (list->dtor_data, 0);
|
|
xfree (list);
|
|
}
|
|
|
|
*dummy_ptr = dummy->next;
|
|
discard_infcall_suspend_state (dummy->caller_state);
|
|
xfree (dummy);
|
|
}
|
|
|
|
/* Delete any breakpoint B which is a momentary breakpoint for return from
|
|
inferior call matching DUMMY_VOIDP. */
|
|
|
|
static int
|
|
pop_dummy_frame_bpt (struct breakpoint *b, void *dummy_voidp)
|
|
{
|
|
struct dummy_frame *dummy = (struct dummy_frame *) dummy_voidp;
|
|
|
|
if (b->thread == ptid_to_global_thread_id (dummy->id.ptid)
|
|
&& b->disposition == disp_del && frame_id_eq (b->frame_id, dummy->id.id))
|
|
{
|
|
while (b->related_breakpoint != b)
|
|
delete_breakpoint (b->related_breakpoint);
|
|
|
|
delete_breakpoint (b);
|
|
|
|
/* Stop the traversal. */
|
|
return 1;
|
|
}
|
|
|
|
/* Continue the traversal. */
|
|
return 0;
|
|
}
|
|
|
|
/* Pop *DUMMY_PTR, restoring program state to that before the
|
|
frame was created. */
|
|
|
|
static void
|
|
pop_dummy_frame (struct dummy_frame **dummy_ptr)
|
|
{
|
|
struct dummy_frame *dummy = *dummy_ptr;
|
|
|
|
gdb_assert (ptid_equal (dummy->id.ptid, inferior_ptid));
|
|
|
|
while (dummy->dtor_list != NULL)
|
|
{
|
|
struct dummy_frame_dtor_list *list = dummy->dtor_list;
|
|
|
|
dummy->dtor_list = list->next;
|
|
list->dtor (list->dtor_data, 1);
|
|
xfree (list);
|
|
}
|
|
|
|
restore_infcall_suspend_state (dummy->caller_state);
|
|
|
|
iterate_over_breakpoints (pop_dummy_frame_bpt, dummy);
|
|
|
|
/* restore_infcall_control_state frees inf_state,
|
|
all that remains is to pop *dummy_ptr. */
|
|
*dummy_ptr = dummy->next;
|
|
xfree (dummy);
|
|
|
|
/* We've made right mess of GDB's local state, just discard
|
|
everything. */
|
|
reinit_frame_cache ();
|
|
}
|
|
|
|
/* Look up DUMMY_ID.
|
|
Return NULL if not found. */
|
|
|
|
static struct dummy_frame **
|
|
lookup_dummy_frame (struct dummy_frame_id *dummy_id)
|
|
{
|
|
struct dummy_frame **dp;
|
|
|
|
for (dp = &dummy_frame_stack; *dp != NULL; dp = &(*dp)->next)
|
|
{
|
|
if (dummy_frame_id_eq (&(*dp)->id, dummy_id))
|
|
return dp;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Find the dummy frame by DUMMY_ID and PTID, and pop it, restoring
|
|
program state to that before the frame was created.
|
|
On return reinit_frame_cache has been called.
|
|
If the frame isn't found, flag an internal error. */
|
|
|
|
void
|
|
dummy_frame_pop (struct frame_id dummy_id, ptid_t ptid)
|
|
{
|
|
struct dummy_frame **dp;
|
|
struct dummy_frame_id id = { dummy_id, ptid };
|
|
|
|
dp = lookup_dummy_frame (&id);
|
|
gdb_assert (dp != NULL);
|
|
|
|
pop_dummy_frame (dp);
|
|
}
|
|
|
|
/* Find the dummy frame by DUMMY_ID and PTID and drop it. Do nothing
|
|
if it is not found. Do not restore its state into inferior, just
|
|
free its memory. */
|
|
|
|
void
|
|
dummy_frame_discard (struct frame_id dummy_id, ptid_t ptid)
|
|
{
|
|
struct dummy_frame **dp;
|
|
struct dummy_frame_id id = { dummy_id, ptid };
|
|
|
|
dp = lookup_dummy_frame (&id);
|
|
if (dp)
|
|
remove_dummy_frame (dp);
|
|
}
|
|
|
|
/* See dummy-frame.h. */
|
|
|
|
void
|
|
register_dummy_frame_dtor (struct frame_id dummy_id, ptid_t ptid,
|
|
dummy_frame_dtor_ftype *dtor, void *dtor_data)
|
|
{
|
|
struct dummy_frame_id id = { dummy_id, ptid };
|
|
struct dummy_frame **dp, *d;
|
|
struct dummy_frame_dtor_list *list;
|
|
|
|
dp = lookup_dummy_frame (&id);
|
|
gdb_assert (dp != NULL);
|
|
d = *dp;
|
|
list = XNEW (struct dummy_frame_dtor_list);
|
|
list->next = d->dtor_list;
|
|
d->dtor_list = list;
|
|
list->dtor = dtor;
|
|
list->dtor_data = dtor_data;
|
|
}
|
|
|
|
/* See dummy-frame.h. */
|
|
|
|
int
|
|
find_dummy_frame_dtor (dummy_frame_dtor_ftype *dtor, void *dtor_data)
|
|
{
|
|
struct dummy_frame *d;
|
|
|
|
for (d = dummy_frame_stack; d != NULL; d = d->next)
|
|
{
|
|
struct dummy_frame_dtor_list *list;
|
|
|
|
for (list = d->dtor_list; list != NULL; list = list->next)
|
|
if (list->dtor == dtor && list->dtor_data == dtor_data)
|
|
return 1;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* There may be stale dummy frames, perhaps left over from when an uncaught
|
|
longjmp took us out of a function that was called by the debugger. Clean
|
|
them up at least once whenever we start a new inferior. */
|
|
|
|
static void
|
|
cleanup_dummy_frames (struct target_ops *target, int from_tty)
|
|
{
|
|
while (dummy_frame_stack != NULL)
|
|
remove_dummy_frame (&dummy_frame_stack);
|
|
}
|
|
|
|
/* Return the dummy frame cache, it contains both the ID, and a
|
|
pointer to the regcache. */
|
|
struct dummy_frame_cache
|
|
{
|
|
struct frame_id this_id;
|
|
struct regcache *prev_regcache;
|
|
};
|
|
|
|
static int
|
|
dummy_frame_sniffer (const struct frame_unwind *self,
|
|
struct frame_info *this_frame,
|
|
void **this_prologue_cache)
|
|
{
|
|
/* When unwinding a normal frame, the stack structure is determined
|
|
by analyzing the frame's function's code (be it using brute force
|
|
prologue analysis, or the dwarf2 CFI). In the case of a dummy
|
|
frame, that simply isn't possible. The PC is either the program
|
|
entry point, or some random address on the stack. Trying to use
|
|
that PC to apply standard frame ID unwind techniques is just
|
|
asking for trouble. */
|
|
|
|
/* Don't bother unless there is at least one dummy frame. */
|
|
if (dummy_frame_stack != NULL)
|
|
{
|
|
struct dummy_frame *dummyframe;
|
|
/* Use an architecture specific method to extract this frame's
|
|
dummy ID, assuming it is a dummy frame. */
|
|
struct frame_id this_id
|
|
= gdbarch_dummy_id (get_frame_arch (this_frame), this_frame);
|
|
struct dummy_frame_id dummy_id = { this_id, inferior_ptid };
|
|
|
|
/* Use that ID to find the corresponding cache entry. */
|
|
for (dummyframe = dummy_frame_stack;
|
|
dummyframe != NULL;
|
|
dummyframe = dummyframe->next)
|
|
{
|
|
if (dummy_frame_id_eq (&dummyframe->id, &dummy_id))
|
|
{
|
|
struct dummy_frame_cache *cache;
|
|
|
|
cache = FRAME_OBSTACK_ZALLOC (struct dummy_frame_cache);
|
|
cache->prev_regcache = get_infcall_suspend_state_regcache
|
|
(dummyframe->caller_state);
|
|
cache->this_id = this_id;
|
|
(*this_prologue_cache) = cache;
|
|
return 1;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Given a call-dummy dummy-frame, return the registers. Here the
|
|
register value is taken from the local copy of the register buffer. */
|
|
|
|
static struct value *
|
|
dummy_frame_prev_register (struct frame_info *this_frame,
|
|
void **this_prologue_cache,
|
|
int regnum)
|
|
{
|
|
struct dummy_frame_cache *cache
|
|
= (struct dummy_frame_cache *) *this_prologue_cache;
|
|
struct gdbarch *gdbarch = get_frame_arch (this_frame);
|
|
struct value *reg_val;
|
|
|
|
/* The dummy-frame sniffer always fills in the cache. */
|
|
gdb_assert (cache != NULL);
|
|
|
|
/* Describe the register's location. Generic dummy frames always
|
|
have the register value in an ``expression''. */
|
|
reg_val = value_zero (register_type (gdbarch, regnum), not_lval);
|
|
|
|
/* Use the regcache_cooked_read() method so that it, on the fly,
|
|
constructs either a raw or pseudo register from the raw
|
|
register cache. */
|
|
regcache_cooked_read (cache->prev_regcache, regnum,
|
|
value_contents_writeable (reg_val));
|
|
return reg_val;
|
|
}
|
|
|
|
/* Assuming that THIS_FRAME is a dummy, return its ID. That ID is
|
|
determined by examining the NEXT frame's unwound registers using
|
|
the method dummy_id(). As a side effect, THIS dummy frame's
|
|
dummy cache is located and saved in THIS_PROLOGUE_CACHE. */
|
|
|
|
static void
|
|
dummy_frame_this_id (struct frame_info *this_frame,
|
|
void **this_prologue_cache,
|
|
struct frame_id *this_id)
|
|
{
|
|
/* The dummy-frame sniffer always fills in the cache. */
|
|
struct dummy_frame_cache *cache
|
|
= (struct dummy_frame_cache *) *this_prologue_cache;
|
|
|
|
gdb_assert (cache != NULL);
|
|
(*this_id) = cache->this_id;
|
|
}
|
|
|
|
const struct frame_unwind dummy_frame_unwind =
|
|
{
|
|
DUMMY_FRAME,
|
|
default_frame_unwind_stop_reason,
|
|
dummy_frame_this_id,
|
|
dummy_frame_prev_register,
|
|
NULL,
|
|
dummy_frame_sniffer,
|
|
};
|
|
|
|
static void
|
|
fprint_dummy_frames (struct ui_file *file)
|
|
{
|
|
struct dummy_frame *s;
|
|
|
|
for (s = dummy_frame_stack; s != NULL; s = s->next)
|
|
{
|
|
gdb_print_host_address (s, file);
|
|
fprintf_unfiltered (file, ":");
|
|
fprintf_unfiltered (file, " id=");
|
|
fprint_frame_id (file, s->id.id);
|
|
fprintf_unfiltered (file, ", ptid=%s",
|
|
target_pid_to_str (s->id.ptid));
|
|
fprintf_unfiltered (file, "\n");
|
|
}
|
|
}
|
|
|
|
static void
|
|
maintenance_print_dummy_frames (char *args, int from_tty)
|
|
{
|
|
if (args == NULL)
|
|
fprint_dummy_frames (gdb_stdout);
|
|
else
|
|
{
|
|
stdio_file file;
|
|
|
|
if (!file.open (args, "w"))
|
|
perror_with_name (_("maintenance print dummy-frames"));
|
|
fprint_dummy_frames (&file);
|
|
}
|
|
}
|
|
|
|
extern void _initialize_dummy_frame (void);
|
|
|
|
void
|
|
_initialize_dummy_frame (void)
|
|
{
|
|
add_cmd ("dummy-frames", class_maintenance, maintenance_print_dummy_frames,
|
|
_("Print the contents of the internal dummy-frame stack."),
|
|
&maintenanceprintlist);
|
|
|
|
observer_attach_inferior_created (cleanup_dummy_frames);
|
|
}
|