mirror of
https://sourceware.org/git/binutils-gdb.git
synced 2024-11-27 03:51:15 +08:00
2009-10-16 Hui Zhu <teawater@gmail.com>
Michael Snyder <msnyder@msnyder-server.eng.vmware.com> * record.c (RECORD_FILE_MAGIC): New constant. (record_arch_list_cleanups): Renamed from record_message_cleanups. (bfdcore_read): New function. (netorder64): New function. (netorder32): New function. (netorder16): New function. (record_restore): New function. Restore a saved record log. (bfdcore_write): New function. (cmd_record_restore): New function. (cmd_record_save): New function. Save a record log to a file. (_initialize_record): Set up commands for save and restore. 2009-10-17 Michael Snyder <msnyder@vmware.com> * gcore.h: New file. * gcore.c (create_gcore_bfd): New function. (write_gcore_file): New function. (call_target_sbrk): New function. Abstracted from gcore_command. (gcore_command): Call call_target_sbrk.
This commit is contained in:
parent
27699eeaab
commit
0156b21832
@ -1,3 +1,26 @@
|
||||
2009-10-16 Hui Zhu <teawater@gmail.com>
|
||||
Michael Snyder <msnyder@msnyder-server.eng.vmware.com>
|
||||
|
||||
* record.c (RECORD_FILE_MAGIC): New constant.
|
||||
(record_arch_list_cleanups): Renamed from record_message_cleanups.
|
||||
(bfdcore_read): New function.
|
||||
(netorder64): New function.
|
||||
(netorder32): New function.
|
||||
(netorder16): New function.
|
||||
(record_restore): New function. Restore a saved record log.
|
||||
(bfdcore_write): New function.
|
||||
(cmd_record_restore): New function.
|
||||
(cmd_record_save): New function. Save a record log to a file.
|
||||
(_initialize_record): Set up commands for save and restore.
|
||||
|
||||
2009-10-17 Michael Snyder <msnyder@vmware.com>
|
||||
|
||||
* gcore.h: New file.
|
||||
* gcore.c (create_gcore_bfd): New function.
|
||||
(write_gcore_file): New function.
|
||||
(call_target_sbrk): New function. Abstracted from gcore_command.
|
||||
(gcore_command): Call call_target_sbrk.
|
||||
|
||||
2009-10-22 Hui Zhu <teawater@gmail.com>
|
||||
Michael Snyder <msnyder@vmware.com>
|
||||
|
||||
|
177
gdb/gcore.c
177
gdb/gcore.c
@ -25,10 +25,14 @@
|
||||
#include "gdbcore.h"
|
||||
#include "objfiles.h"
|
||||
#include "symfile.h"
|
||||
|
||||
#include "arch-utils.h"
|
||||
#include "completer.h"
|
||||
#include "gcore.h"
|
||||
#include "cli/cli-decode.h"
|
||||
|
||||
#include "gdb_assert.h"
|
||||
#include <fcntl.h>
|
||||
#include "regcache.h"
|
||||
#include "regset.h"
|
||||
|
||||
/* The largest amount of memory to read from the target at once. We
|
||||
must throttle it to limit the amount of memory used by GDB during
|
||||
@ -40,45 +44,30 @@ static enum bfd_architecture default_gcore_arch (void);
|
||||
static unsigned long default_gcore_mach (void);
|
||||
static int gcore_memory_sections (bfd *);
|
||||
|
||||
/* Generate a core file from the inferior process. */
|
||||
/* create_gcore_bfd -- helper for gcore_command (exported).
|
||||
Open a new bfd core file for output, and return the handle. */
|
||||
|
||||
static void
|
||||
gcore_command (char *args, int from_tty)
|
||||
bfd *
|
||||
create_gcore_bfd (char *filename)
|
||||
{
|
||||
struct cleanup *old_chain;
|
||||
char *corefilename, corefilename_buffer[40];
|
||||
asection *note_sec = NULL;
|
||||
bfd *obfd;
|
||||
void *note_data = NULL;
|
||||
int note_size = 0;
|
||||
|
||||
/* No use generating a corefile without a target process. */
|
||||
if (!target_has_execution)
|
||||
noprocess ();
|
||||
|
||||
if (args && *args)
|
||||
corefilename = args;
|
||||
else
|
||||
{
|
||||
/* Default corefile name is "core.PID". */
|
||||
sprintf (corefilename_buffer, "core.%d", PIDGET (inferior_ptid));
|
||||
corefilename = corefilename_buffer;
|
||||
}
|
||||
|
||||
if (info_verbose)
|
||||
fprintf_filtered (gdb_stdout,
|
||||
"Opening corefile '%s' for output.\n", corefilename);
|
||||
|
||||
/* Open the output file. */
|
||||
obfd = bfd_openw (corefilename, default_gcore_target ());
|
||||
bfd *obfd = bfd_openw (filename, default_gcore_target ());
|
||||
if (!obfd)
|
||||
error (_("Failed to open '%s' for output."), corefilename);
|
||||
|
||||
/* Need a cleanup that will close the file (FIXME: delete it?). */
|
||||
old_chain = make_cleanup_bfd_close (obfd);
|
||||
|
||||
error (_("Failed to open '%s' for output."), filename);
|
||||
bfd_set_format (obfd, bfd_core);
|
||||
bfd_set_arch_mach (obfd, default_gcore_arch (), default_gcore_mach ());
|
||||
return obfd;
|
||||
}
|
||||
|
||||
/* write_gcore_file -- helper for gcore_command (exported).
|
||||
Compose and write the corefile data to the core file. */
|
||||
|
||||
|
||||
void
|
||||
write_gcore_file (bfd *obfd)
|
||||
{
|
||||
void *note_data = NULL;
|
||||
int note_size = 0;
|
||||
asection *note_sec = NULL;
|
||||
|
||||
/* An external target method must build the notes section. */
|
||||
note_data = target_make_corefile_notes (obfd, ¬e_size);
|
||||
@ -107,8 +96,46 @@ gcore_command (char *args, int from_tty)
|
||||
if (note_data != NULL && note_size != 0)
|
||||
{
|
||||
if (!bfd_set_section_contents (obfd, note_sec, note_data, 0, note_size))
|
||||
warning (_("writing note section (%s)"), bfd_errmsg (bfd_get_error ()));
|
||||
warning (_("writing note section (%s)"),
|
||||
bfd_errmsg (bfd_get_error ()));
|
||||
}
|
||||
}
|
||||
|
||||
/* gcore_command -- implements the 'gcore' command.
|
||||
Generate a core file from the inferior process. */
|
||||
|
||||
static void
|
||||
gcore_command (char *args, int from_tty)
|
||||
{
|
||||
struct cleanup *old_chain;
|
||||
char *corefilename, corefilename_buffer[40];
|
||||
bfd *obfd;
|
||||
|
||||
/* No use generating a corefile without a target process. */
|
||||
if (!target_has_execution)
|
||||
noprocess ();
|
||||
|
||||
if (args && *args)
|
||||
corefilename = args;
|
||||
else
|
||||
{
|
||||
/* Default corefile name is "core.PID". */
|
||||
sprintf (corefilename_buffer, "core.%d", PIDGET (inferior_ptid));
|
||||
corefilename = corefilename_buffer;
|
||||
}
|
||||
|
||||
if (info_verbose)
|
||||
fprintf_filtered (gdb_stdout,
|
||||
"Opening corefile '%s' for output.\n", corefilename);
|
||||
|
||||
/* Open the output file. */
|
||||
obfd = create_gcore_bfd (corefilename);
|
||||
|
||||
/* Need a cleanup that will close the file (FIXME: delete it?). */
|
||||
old_chain = make_cleanup_bfd_close (obfd);
|
||||
|
||||
/* Call worker function. */
|
||||
write_gcore_file (obfd);
|
||||
|
||||
/* Succeeded. */
|
||||
fprintf_filtered (gdb_stdout, "Saved corefile %s\n", corefilename);
|
||||
@ -212,6 +239,50 @@ derive_stack_segment (bfd_vma *bottom, bfd_vma *top)
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* call_target_sbrk --
|
||||
helper function for derive_heap_segment. */
|
||||
|
||||
static bfd_vma
|
||||
call_target_sbrk (int sbrk_arg)
|
||||
{
|
||||
struct objfile *sbrk_objf;
|
||||
struct gdbarch *gdbarch;
|
||||
bfd_vma top_of_heap;
|
||||
struct value *target_sbrk_arg;
|
||||
struct value *sbrk_fn, *ret;
|
||||
bfd_vma tmp;
|
||||
|
||||
if (lookup_minimal_symbol ("sbrk", NULL, NULL) != NULL)
|
||||
{
|
||||
sbrk_fn = find_function_in_inferior ("sbrk", &sbrk_objf);
|
||||
if (sbrk_fn == NULL)
|
||||
return (bfd_vma) 0;
|
||||
}
|
||||
else if (lookup_minimal_symbol ("_sbrk", NULL, NULL) != NULL)
|
||||
{
|
||||
sbrk_fn = find_function_in_inferior ("_sbrk", &sbrk_objf);
|
||||
if (sbrk_fn == NULL)
|
||||
return (bfd_vma) 0;
|
||||
}
|
||||
else
|
||||
return (bfd_vma) 0;
|
||||
|
||||
gdbarch = get_objfile_arch (sbrk_objf);
|
||||
target_sbrk_arg = value_from_longest (builtin_type (gdbarch)->builtin_int,
|
||||
sbrk_arg);
|
||||
gdb_assert (target_sbrk_arg);
|
||||
ret = call_function_by_hand (sbrk_fn, 1, &target_sbrk_arg);
|
||||
if (ret == NULL)
|
||||
return (bfd_vma) 0;
|
||||
|
||||
tmp = value_as_long (ret);
|
||||
if ((LONGEST) tmp <= 0 || (LONGEST) tmp == 0xffffffff)
|
||||
return (bfd_vma) 0;
|
||||
|
||||
top_of_heap = tmp;
|
||||
return top_of_heap;
|
||||
}
|
||||
|
||||
/* Derive a reasonable heap segment for ABFD by looking at sbrk and
|
||||
the static data sections. Store its limits in *BOTTOM and *TOP.
|
||||
Return non-zero if successful. */
|
||||
@ -219,12 +290,10 @@ derive_stack_segment (bfd_vma *bottom, bfd_vma *top)
|
||||
static int
|
||||
derive_heap_segment (bfd *abfd, bfd_vma *bottom, bfd_vma *top)
|
||||
{
|
||||
struct objfile *sbrk_objf;
|
||||
struct gdbarch *gdbarch;
|
||||
bfd_vma top_of_data_memory = 0;
|
||||
bfd_vma top_of_heap = 0;
|
||||
bfd_size_type sec_size;
|
||||
struct value *zero, *sbrk;
|
||||
bfd_vma sec_vaddr;
|
||||
asection *sec;
|
||||
|
||||
@ -259,30 +328,10 @@ derive_heap_segment (bfd *abfd, bfd_vma *bottom, bfd_vma *top)
|
||||
}
|
||||
}
|
||||
|
||||
/* Now get the top-of-heap by calling sbrk in the inferior. */
|
||||
if (lookup_minimal_symbol ("sbrk", NULL, NULL) != NULL)
|
||||
{
|
||||
sbrk = find_function_in_inferior ("sbrk", &sbrk_objf);
|
||||
if (sbrk == NULL)
|
||||
return 0;
|
||||
}
|
||||
else if (lookup_minimal_symbol ("_sbrk", NULL, NULL) != NULL)
|
||||
{
|
||||
sbrk = find_function_in_inferior ("_sbrk", &sbrk_objf);
|
||||
if (sbrk == NULL)
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
top_of_heap = call_target_sbrk (0);
|
||||
if (top_of_heap == (bfd_vma) 0)
|
||||
return 0;
|
||||
|
||||
gdbarch = get_objfile_arch (sbrk_objf);
|
||||
zero = value_from_longest (builtin_type (gdbarch)->builtin_int, 0);
|
||||
gdb_assert (zero);
|
||||
sbrk = call_function_by_hand (sbrk, 1, &zero);
|
||||
if (sbrk == NULL)
|
||||
return 0;
|
||||
top_of_heap = value_as_long (sbrk);
|
||||
|
||||
/* Return results. */
|
||||
if (top_of_heap > top_of_data_memory)
|
||||
{
|
||||
@ -299,13 +348,15 @@ static void
|
||||
make_output_phdrs (bfd *obfd, asection *osec, void *ignored)
|
||||
{
|
||||
int p_flags = 0;
|
||||
int p_type;
|
||||
int p_type = 0;
|
||||
|
||||
/* FIXME: these constants may only be applicable for ELF. */
|
||||
if (strncmp (bfd_section_name (obfd, osec), "load", 4) == 0)
|
||||
p_type = PT_LOAD;
|
||||
else
|
||||
else if (strncmp (bfd_section_name (obfd, osec), "note", 4) == 0)
|
||||
p_type = PT_NOTE;
|
||||
else
|
||||
p_type = PT_NULL;
|
||||
|
||||
p_flags |= PF_R; /* Segment is readable. */
|
||||
if (!(bfd_get_section_flags (obfd, osec) & SEC_READONLY))
|
||||
|
27
gdb/gcore.h
Normal file
27
gdb/gcore.h
Normal file
@ -0,0 +1,27 @@
|
||||
/* Support for reading/writing gcore files.
|
||||
|
||||
Copyright (C) 2009, 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/>. */
|
||||
|
||||
#if !defined (GCORE_H)
|
||||
#define GCORE_H 1
|
||||
|
||||
extern bfd *create_gcore_bfd (char *filename);
|
||||
extern void write_gcore_file (bfd *obfd);
|
||||
extern bfd *load_corefile (char *filename, int from_tty);
|
||||
|
||||
#endif /* GCORE_H */
|
566
gdb/record.c
566
gdb/record.c
@ -23,10 +23,15 @@
|
||||
#include "gdbthread.h"
|
||||
#include "event-top.h"
|
||||
#include "exceptions.h"
|
||||
#include "completer.h"
|
||||
#include "arch-utils.h"
|
||||
#include "gdbcore.h"
|
||||
#include "exec.h"
|
||||
#include "record.h"
|
||||
#include "elf-bfd.h"
|
||||
#include "gcore.h"
|
||||
|
||||
#include <byteswap.h>
|
||||
#include <signal.h>
|
||||
|
||||
/* This module implements "target record", also known as "process
|
||||
@ -54,6 +59,8 @@
|
||||
#define RECORD_IS_REPLAY \
|
||||
(record_list->next || execution_direction == EXEC_REVERSE)
|
||||
|
||||
#define RECORD_FILE_MAGIC netorder32(0x20091016)
|
||||
|
||||
/* These are the core structs of the process record functionality.
|
||||
|
||||
A record_entry is a record of the value change of a register
|
||||
@ -545,24 +552,24 @@ record_check_insn_num (int set_terminal)
|
||||
if (q)
|
||||
record_stop_at_limit = 0;
|
||||
else
|
||||
error (_("Process record: inferior program stopped."));
|
||||
error (_("Process record: stopped by user."));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
record_arch_list_cleanups (void *ignore)
|
||||
{
|
||||
record_list_release (record_arch_list_tail);
|
||||
}
|
||||
|
||||
/* Before inferior step (when GDB record the running message, inferior
|
||||
only can step), GDB will call this function to record the values to
|
||||
record_list. This function will call gdbarch_process_record to
|
||||
record the running message of inferior and set them to
|
||||
record_arch_list, and add it to record_list. */
|
||||
|
||||
static void
|
||||
record_message_cleanups (void *ignore)
|
||||
{
|
||||
record_list_release (record_arch_list_tail);
|
||||
}
|
||||
|
||||
struct record_message_args {
|
||||
struct regcache *regcache;
|
||||
enum target_signal signal;
|
||||
@ -574,7 +581,7 @@ record_message (void *args)
|
||||
int ret;
|
||||
struct record_message_args *myargs = args;
|
||||
struct gdbarch *gdbarch = get_regcache_arch (myargs->regcache);
|
||||
struct cleanup *old_cleanups = make_cleanup (record_message_cleanups, 0);
|
||||
struct cleanup *old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
|
||||
|
||||
record_arch_list_head = NULL;
|
||||
record_arch_list_tail = NULL;
|
||||
@ -714,8 +721,8 @@ record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
|
||||
{
|
||||
entry->u.mem.mem_entry_not_accessible = 1;
|
||||
if (record_debug)
|
||||
warning (_("Process record: error reading memory at "
|
||||
"addr = %s len = %d."),
|
||||
warning ("Process record: error reading memory at "
|
||||
"addr = %s len = %d.",
|
||||
paddress (gdbarch, entry->u.mem.addr),
|
||||
entry->u.mem.len);
|
||||
}
|
||||
@ -727,8 +734,8 @@ record_exec_insn (struct regcache *regcache, struct gdbarch *gdbarch,
|
||||
{
|
||||
entry->u.mem.mem_entry_not_accessible = 1;
|
||||
if (record_debug)
|
||||
warning (_("Process record: error writing memory at "
|
||||
"addr = %s len = %d."),
|
||||
warning ("Process record: error writing memory at "
|
||||
"addr = %s len = %d.",
|
||||
paddress (gdbarch, entry->u.mem.addr),
|
||||
entry->u.mem.len);
|
||||
}
|
||||
@ -765,6 +772,8 @@ static int (*tmp_to_insert_breakpoint) (struct gdbarch *,
|
||||
static int (*tmp_to_remove_breakpoint) (struct gdbarch *,
|
||||
struct bp_target_info *);
|
||||
|
||||
static void record_restore (void);
|
||||
|
||||
/* Open the process record target. */
|
||||
|
||||
static void
|
||||
@ -791,6 +800,7 @@ record_core_open_1 (char *name, int from_tty)
|
||||
}
|
||||
|
||||
push_target (&record_core_ops);
|
||||
record_restore ();
|
||||
}
|
||||
|
||||
/* "to_open" target method for 'live' processes. */
|
||||
@ -1438,8 +1448,8 @@ record_xfer_partial (struct target_ops *ops, enum target_object object,
|
||||
record_list_release (record_arch_list_tail);
|
||||
if (record_debug)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
_("Process record: failed to record "
|
||||
"execution log."));
|
||||
"Process record: failed to record "
|
||||
"execution log.");
|
||||
return -1;
|
||||
}
|
||||
if (record_arch_list_add_end ())
|
||||
@ -1447,8 +1457,8 @@ record_xfer_partial (struct target_ops *ops, enum target_object object,
|
||||
record_list_release (record_arch_list_tail);
|
||||
if (record_debug)
|
||||
fprintf_unfiltered (gdb_stdlog,
|
||||
_("Process record: failed to record "
|
||||
"execution log."));
|
||||
"Process record: failed to record "
|
||||
"execution log.");
|
||||
return -1;
|
||||
}
|
||||
record_list->next = record_arch_list_head;
|
||||
@ -1885,9 +1895,513 @@ info_record_command (char *args, int from_tty)
|
||||
record_insn_max_num);
|
||||
}
|
||||
|
||||
/* Record log save-file format
|
||||
Version 1 (never released)
|
||||
|
||||
Header:
|
||||
4 bytes: magic number htonl(0x20090829).
|
||||
NOTE: be sure to change whenever this file format changes!
|
||||
|
||||
Records:
|
||||
record_end:
|
||||
1 byte: record type (record_end, see enum record_type).
|
||||
record_reg:
|
||||
1 byte: record type (record_reg, see enum record_type).
|
||||
8 bytes: register id (network byte order).
|
||||
MAX_REGISTER_SIZE bytes: register value.
|
||||
record_mem:
|
||||
1 byte: record type (record_mem, see enum record_type).
|
||||
8 bytes: memory length (network byte order).
|
||||
8 bytes: memory address (network byte order).
|
||||
n bytes: memory value (n == memory length).
|
||||
|
||||
Version 2
|
||||
4 bytes: magic number netorder32(0x20091016).
|
||||
NOTE: be sure to change whenever this file format changes!
|
||||
|
||||
Records:
|
||||
record_end:
|
||||
1 byte: record type (record_end, see enum record_type).
|
||||
4 bytes: signal
|
||||
4 bytes: instruction count
|
||||
record_reg:
|
||||
1 byte: record type (record_reg, see enum record_type).
|
||||
4 bytes: register id (network byte order).
|
||||
n bytes: register value (n == actual register size).
|
||||
(eg. 4 bytes for x86 general registers).
|
||||
record_mem:
|
||||
1 byte: record type (record_mem, see enum record_type).
|
||||
4 bytes: memory length (network byte order).
|
||||
8 bytes: memory address (network byte order).
|
||||
n bytes: memory value (n == memory length).
|
||||
|
||||
*/
|
||||
|
||||
/* bfdcore_read -- read bytes from a core file section. */
|
||||
|
||||
static inline void
|
||||
bfdcore_read (bfd *obfd, asection *osec, void *buf, int len, int *offset)
|
||||
{
|
||||
int ret = bfd_get_section_contents (obfd, osec, buf, *offset, len);
|
||||
|
||||
if (ret)
|
||||
*offset += len;
|
||||
else
|
||||
error (_("Failed to read %d bytes from core file %s ('%s').\n"),
|
||||
len, bfd_get_filename (obfd),
|
||||
bfd_errmsg (bfd_get_error ()));
|
||||
}
|
||||
|
||||
static inline uint64_t
|
||||
netorder64 (uint64_t fromfile)
|
||||
{
|
||||
return (BYTE_ORDER == LITTLE_ENDIAN)
|
||||
? bswap_64 (fromfile)
|
||||
: fromfile;
|
||||
}
|
||||
|
||||
static inline uint32_t
|
||||
netorder32 (uint32_t fromfile)
|
||||
{
|
||||
return (BYTE_ORDER == LITTLE_ENDIAN)
|
||||
? bswap_32 (fromfile)
|
||||
: fromfile;
|
||||
}
|
||||
|
||||
static inline uint16_t
|
||||
netorder16 (uint16_t fromfile)
|
||||
{
|
||||
return (BYTE_ORDER == LITTLE_ENDIAN)
|
||||
? bswap_16 (fromfile)
|
||||
: fromfile;
|
||||
}
|
||||
|
||||
/* Restore the execution log from a core_bfd file. */
|
||||
static void
|
||||
record_restore (void)
|
||||
{
|
||||
uint32_t magic;
|
||||
struct cleanup *old_cleanups;
|
||||
struct record_entry *rec;
|
||||
asection *osec;
|
||||
uint32_t osec_size;
|
||||
int bfd_offset = 0;
|
||||
struct regcache *regcache;
|
||||
|
||||
/* We restore the execution log from the open core bfd,
|
||||
if there is one. */
|
||||
if (core_bfd == NULL)
|
||||
return;
|
||||
|
||||
/* "record_restore" can only be called when record list is empty. */
|
||||
gdb_assert (record_first.next == NULL);
|
||||
|
||||
if (record_debug)
|
||||
printf_filtered ("Restoring recording from core file.\n");
|
||||
|
||||
/* Now need to find our special note section. */
|
||||
osec = bfd_get_section_by_name (core_bfd, "null0");
|
||||
osec_size = bfd_section_size (core_bfd, osec);
|
||||
if (record_debug)
|
||||
printf_filtered ("Find precord section %s.\n",
|
||||
osec ? "succeeded" : "failed");
|
||||
if (!osec)
|
||||
return;
|
||||
if (record_debug)
|
||||
printf_filtered ("%s", bfd_section_name (core_bfd, osec));
|
||||
|
||||
/* Check the magic code. */
|
||||
bfdcore_read (core_bfd, osec, &magic, sizeof (magic), &bfd_offset);
|
||||
if (magic != RECORD_FILE_MAGIC)
|
||||
error (_("Version mis-match or file format error in core file %s."),
|
||||
bfd_get_filename (core_bfd));
|
||||
if (record_debug)
|
||||
printf_filtered ("\
|
||||
Reading 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n",
|
||||
netorder32 (magic));
|
||||
|
||||
/* Restore the entries in recfd into record_arch_list_head and
|
||||
record_arch_list_tail. */
|
||||
record_arch_list_head = NULL;
|
||||
record_arch_list_tail = NULL;
|
||||
record_insn_num = 0;
|
||||
old_cleanups = make_cleanup (record_arch_list_cleanups, 0);
|
||||
regcache = get_current_regcache ();
|
||||
|
||||
while (1)
|
||||
{
|
||||
int ret;
|
||||
uint8_t tmpu8;
|
||||
uint32_t regnum, len, signal, count;
|
||||
uint64_t addr;
|
||||
|
||||
/* We are finished when offset reaches osec_size. */
|
||||
if (bfd_offset >= osec_size)
|
||||
break;
|
||||
bfdcore_read (core_bfd, osec, &tmpu8, sizeof (tmpu8), &bfd_offset);
|
||||
|
||||
switch (tmpu8)
|
||||
{
|
||||
case record_reg: /* reg */
|
||||
/* Get register number to regnum. */
|
||||
bfdcore_read (core_bfd, osec, ®num,
|
||||
sizeof (regnum), &bfd_offset);
|
||||
regnum = netorder32 (regnum);
|
||||
|
||||
rec = record_reg_alloc (regcache, regnum);
|
||||
|
||||
/* Get val. */
|
||||
bfdcore_read (core_bfd, osec, record_get_loc (rec),
|
||||
rec->u.reg.len, &bfd_offset);
|
||||
|
||||
if (record_debug)
|
||||
printf_filtered ("\
|
||||
Reading register %d (1 plus %d plus %d bytes)\n",
|
||||
rec->u.reg.num,
|
||||
sizeof (regnum),
|
||||
rec->u.reg.len);
|
||||
break;
|
||||
|
||||
case record_mem: /* mem */
|
||||
/* Get len. */
|
||||
bfdcore_read (core_bfd, osec, &len,
|
||||
sizeof (len), &bfd_offset);
|
||||
len = netorder32 (len);
|
||||
|
||||
/* Get addr. */
|
||||
bfdcore_read (core_bfd, osec, &addr,
|
||||
sizeof (addr), &bfd_offset);
|
||||
addr = netorder64 (addr);
|
||||
|
||||
rec = record_mem_alloc (addr, len);
|
||||
|
||||
/* Get val. */
|
||||
bfdcore_read (core_bfd, osec, record_get_loc (rec),
|
||||
rec->u.mem.len, &bfd_offset);
|
||||
|
||||
if (record_debug)
|
||||
printf_filtered ("\
|
||||
Reading memory %s (1 plus %d plus %d plus %d bytes)\n",
|
||||
paddress (get_current_arch (),
|
||||
rec->u.mem.addr),
|
||||
sizeof (addr),
|
||||
sizeof (len),
|
||||
rec->u.mem.len);
|
||||
break;
|
||||
|
||||
case record_end: /* end */
|
||||
rec = record_end_alloc ();
|
||||
record_insn_num ++;
|
||||
|
||||
/* Get signal value. */
|
||||
bfdcore_read (core_bfd, osec, &signal,
|
||||
sizeof (signal), &bfd_offset);
|
||||
signal = netorder32 (signal);
|
||||
rec->u.end.sigval = signal;
|
||||
|
||||
/* Get insn count. */
|
||||
bfdcore_read (core_bfd, osec, &count,
|
||||
sizeof (count), &bfd_offset);
|
||||
count = netorder32 (count);
|
||||
rec->u.end.insn_num = count;
|
||||
record_insn_count = count + 1;
|
||||
if (record_debug)
|
||||
printf_filtered ("\
|
||||
Reading record_end (1 + %d + %d bytes), offset == %s\n",
|
||||
sizeof (signal),
|
||||
sizeof (count),
|
||||
paddress (get_current_arch (),
|
||||
bfd_offset));
|
||||
break;
|
||||
|
||||
default:
|
||||
error (_("Bad entry type in core file %s."),
|
||||
bfd_get_filename (core_bfd));
|
||||
break;
|
||||
}
|
||||
|
||||
/* Add rec to record arch list. */
|
||||
record_arch_list_add (rec);
|
||||
}
|
||||
|
||||
discard_cleanups (old_cleanups);
|
||||
|
||||
/* Add record_arch_list_head to the end of record list. */
|
||||
record_first.next = record_arch_list_head;
|
||||
record_arch_list_head->prev = &record_first;
|
||||
record_arch_list_tail->next = NULL;
|
||||
record_list = &record_first;
|
||||
|
||||
/* Update record_insn_max_num. */
|
||||
if (record_insn_num > record_insn_max_num)
|
||||
{
|
||||
record_insn_max_num = record_insn_num;
|
||||
warning (_("Auto increase record/replay buffer limit to %d."),
|
||||
record_insn_max_num);
|
||||
}
|
||||
|
||||
/* Succeeded. */
|
||||
printf_filtered (_("Restored records from core file %s.\n"),
|
||||
bfd_get_filename (core_bfd));
|
||||
|
||||
print_stack_frame (get_selected_frame (NULL), 1, SRC_AND_LOC);
|
||||
}
|
||||
|
||||
/* bfdcore_write -- write bytes into a core file section. */
|
||||
|
||||
static inline void
|
||||
bfdcore_write (bfd *obfd, asection *osec, void *buf, int len, int *offset)
|
||||
{
|
||||
int ret = bfd_set_section_contents (obfd, osec, buf, *offset, len);
|
||||
|
||||
if (ret)
|
||||
*offset += len;
|
||||
else
|
||||
error (_("Failed to write %d bytes to core file %s ('%s').\n"),
|
||||
len, bfd_get_filename (obfd),
|
||||
bfd_errmsg (bfd_get_error ()));
|
||||
}
|
||||
|
||||
/* Restore the execution log from a file. We use a modified elf
|
||||
corefile format, with an extra section for our data. */
|
||||
|
||||
static void
|
||||
cmd_record_restore (char *args, int from_tty)
|
||||
{
|
||||
core_file_command (args, from_tty);
|
||||
record_open (args, from_tty);
|
||||
}
|
||||
|
||||
static void
|
||||
record_save_cleanups (void *data)
|
||||
{
|
||||
bfd *obfd = data;
|
||||
char *pathname = xstrdup (bfd_get_filename (obfd));
|
||||
bfd_close (obfd);
|
||||
unlink (pathname);
|
||||
xfree (pathname);
|
||||
}
|
||||
|
||||
/* Save the execution log to a file. We use a modified elf corefile
|
||||
format, with an extra section for our data. */
|
||||
|
||||
static void
|
||||
cmd_record_save (char *args, int from_tty)
|
||||
{
|
||||
char *recfilename, recfilename_buffer[40];
|
||||
int recfd;
|
||||
struct record_entry *cur_record_list;
|
||||
uint32_t magic;
|
||||
struct regcache *regcache;
|
||||
struct gdbarch *gdbarch;
|
||||
struct cleanup *old_cleanups;
|
||||
struct cleanup *set_cleanups;
|
||||
bfd *obfd;
|
||||
int save_size = 0;
|
||||
asection *osec = NULL;
|
||||
int bfd_offset = 0;
|
||||
|
||||
if (strcmp (current_target.to_shortname, "record") != 0)
|
||||
error (_("This command can only be used with target 'record'.\n"
|
||||
"Use 'target record' first.\n"));
|
||||
|
||||
if (args && *args)
|
||||
recfilename = args;
|
||||
else
|
||||
{
|
||||
/* Default recfile name is "gdb_record.PID". */
|
||||
snprintf (recfilename_buffer, sizeof (recfilename_buffer),
|
||||
"gdb_record.%d", PIDGET (inferior_ptid));
|
||||
recfilename = recfilename_buffer;
|
||||
}
|
||||
|
||||
/* Open the save file. */
|
||||
if (record_debug)
|
||||
printf_filtered ("Saving execution log to core file '%s'\n", recfilename);
|
||||
|
||||
/* Open the output file. */
|
||||
obfd = create_gcore_bfd (recfilename);
|
||||
old_cleanups = make_cleanup (record_save_cleanups, obfd);
|
||||
|
||||
/* Save the current record entry to "cur_record_list". */
|
||||
cur_record_list = record_list;
|
||||
|
||||
/* Get the values of regcache and gdbarch. */
|
||||
regcache = get_current_regcache ();
|
||||
gdbarch = get_regcache_arch (regcache);
|
||||
|
||||
/* Disable the GDB operation record. */
|
||||
set_cleanups = record_gdb_operation_disable_set ();
|
||||
|
||||
/* Reverse execute to the begin of record list. */
|
||||
while (1)
|
||||
{
|
||||
/* Check for beginning and end of log. */
|
||||
if (record_list == &record_first)
|
||||
break;
|
||||
|
||||
record_exec_insn (regcache, gdbarch, record_list);
|
||||
|
||||
if (record_list->prev)
|
||||
record_list = record_list->prev;
|
||||
}
|
||||
|
||||
/* Compute the size needed for the extra bfd section. */
|
||||
save_size = 4; /* magic cookie */
|
||||
for (record_list = record_first.next; record_list;
|
||||
record_list = record_list->next)
|
||||
switch (record_list->type)
|
||||
{
|
||||
case record_end:
|
||||
save_size += 1 + 4 + 4;
|
||||
break;
|
||||
case record_reg:
|
||||
save_size += 1 + 4 + record_list->u.reg.len;
|
||||
break;
|
||||
case record_mem:
|
||||
save_size += 1 + 4 + 8 + record_list->u.mem.len;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Make the new bfd section. */
|
||||
osec = bfd_make_section_anyway_with_flags (obfd, "precord",
|
||||
SEC_HAS_CONTENTS
|
||||
| SEC_READONLY);
|
||||
if (osec == NULL)
|
||||
error (_("Failed to create 'precord' section for corefile %s: %s"),
|
||||
recfilename,
|
||||
bfd_errmsg (bfd_get_error ()));
|
||||
bfd_set_section_size (obfd, osec, save_size);
|
||||
bfd_set_section_vma (obfd, osec, 0);
|
||||
bfd_set_section_alignment (obfd, osec, 0);
|
||||
bfd_section_lma (obfd, osec) = 0;
|
||||
|
||||
/* Save corefile state. */
|
||||
write_gcore_file (obfd);
|
||||
|
||||
/* Write out the record log. */
|
||||
/* Write the magic code. */
|
||||
magic = RECORD_FILE_MAGIC;
|
||||
if (record_debug)
|
||||
printf_filtered ("\
|
||||
Writing 4-byte magic cookie RECORD_FILE_MAGIC (0x%08x)\n",
|
||||
magic);
|
||||
bfdcore_write (obfd, osec, &magic, sizeof (magic), &bfd_offset);
|
||||
|
||||
/* Save the entries to recfd and forward execute to the end of
|
||||
record list. */
|
||||
record_list = &record_first;
|
||||
while (1)
|
||||
{
|
||||
/* Save entry. */
|
||||
if (record_list != &record_first)
|
||||
{
|
||||
uint8_t type;
|
||||
uint32_t regnum, len, signal, count;
|
||||
uint64_t addr;
|
||||
|
||||
type = record_list->type;
|
||||
bfdcore_write (obfd, osec, &type, sizeof (type), &bfd_offset);
|
||||
|
||||
switch (record_list->type)
|
||||
{
|
||||
case record_reg: /* reg */
|
||||
if (record_debug)
|
||||
printf_filtered ("\
|
||||
Writing register %d (1 plus %d plus %d bytes)\n",
|
||||
record_list->u.reg.num,
|
||||
sizeof (regnum),
|
||||
record_list->u.reg.len);
|
||||
|
||||
/* Write regnum. */
|
||||
regnum = netorder32 (record_list->u.reg.num);
|
||||
bfdcore_write (obfd, osec, ®num,
|
||||
sizeof (regnum), &bfd_offset);
|
||||
|
||||
/* Write regval. */
|
||||
bfdcore_write (obfd, osec, record_get_loc (record_list),
|
||||
record_list->u.reg.len, &bfd_offset);
|
||||
break;
|
||||
|
||||
case record_mem: /* mem */
|
||||
if (record_debug)
|
||||
printf_filtered ("\
|
||||
Writing memory %s (1 plus %d plus %d plus %d bytes)\n",
|
||||
paddress (gdbarch,
|
||||
record_list->u.mem.addr),
|
||||
sizeof (addr),
|
||||
sizeof (len),
|
||||
record_list->u.mem.len);
|
||||
|
||||
/* Write memlen. */
|
||||
len = netorder32 (record_list->u.mem.len);
|
||||
bfdcore_write (obfd, osec, &len, sizeof (len), &bfd_offset);
|
||||
|
||||
/* Write memaddr. */
|
||||
addr = netorder64 (record_list->u.mem.addr);
|
||||
bfdcore_write (obfd, osec, &addr,
|
||||
sizeof (addr), &bfd_offset);
|
||||
|
||||
/* Write memval. */
|
||||
bfdcore_write (obfd, osec, record_get_loc (record_list),
|
||||
record_list->u.mem.len, &bfd_offset);
|
||||
break;
|
||||
|
||||
case record_end:
|
||||
if (record_debug)
|
||||
printf_filtered ("\
|
||||
Writing record_end (1 + %d + %d bytes)\n",
|
||||
sizeof (signal),
|
||||
sizeof (count));
|
||||
/* Write signal value. */
|
||||
signal = netorder32 (record_list->u.end.sigval);
|
||||
bfdcore_write (obfd, osec, &signal,
|
||||
sizeof (signal), &bfd_offset);
|
||||
|
||||
/* Write insn count. */
|
||||
count = netorder32 (record_list->u.end.insn_num);
|
||||
bfdcore_write (obfd, osec, &count,
|
||||
sizeof (count), &bfd_offset);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* Execute entry. */
|
||||
record_exec_insn (regcache, gdbarch, record_list);
|
||||
|
||||
if (record_list->next)
|
||||
record_list = record_list->next;
|
||||
else
|
||||
break;
|
||||
}
|
||||
|
||||
/* Reverse execute to cur_record_list. */
|
||||
while (1)
|
||||
{
|
||||
/* Check for beginning and end of log. */
|
||||
if (record_list == cur_record_list)
|
||||
break;
|
||||
|
||||
record_exec_insn (regcache, gdbarch, record_list);
|
||||
|
||||
if (record_list->prev)
|
||||
record_list = record_list->prev;
|
||||
}
|
||||
|
||||
do_cleanups (set_cleanups);
|
||||
bfd_close (obfd);
|
||||
discard_cleanups (old_cleanups);
|
||||
|
||||
/* Succeeded. */
|
||||
printf_filtered (_("Saved core file %s with execution log.\n"),
|
||||
recfilename);
|
||||
}
|
||||
|
||||
void
|
||||
_initialize_record (void)
|
||||
{
|
||||
struct cmd_list_element *c;
|
||||
|
||||
/* Init record_first. */
|
||||
record_first.prev = NULL;
|
||||
record_first.next = NULL;
|
||||
@ -1906,9 +2420,11 @@ _initialize_record (void)
|
||||
NULL, show_record_debug, &setdebuglist,
|
||||
&showdebuglist);
|
||||
|
||||
add_prefix_cmd ("record", class_obscure, cmd_record_start,
|
||||
_("Abbreviated form of \"target record\" command."),
|
||||
&record_cmdlist, "record ", 0, &cmdlist);
|
||||
c = add_prefix_cmd ("record", class_obscure, cmd_record_start,
|
||||
_("Abbreviated form of \"target record\" command."),
|
||||
&record_cmdlist, "record ", 0, &cmdlist);
|
||||
set_cmd_completer (c, filename_completer);
|
||||
|
||||
add_com_alias ("rec", "record", class_obscure, 1);
|
||||
add_prefix_cmd ("record", class_support, set_record_command,
|
||||
_("Set record options"), &set_record_cmdlist,
|
||||
@ -1923,6 +2439,18 @@ _initialize_record (void)
|
||||
"info record ", 0, &infolist);
|
||||
add_alias_cmd ("rec", "record", class_obscure, 1, &infolist);
|
||||
|
||||
c = add_cmd ("save", class_obscure, cmd_record_save,
|
||||
_("Save the execution log to a file.\n\
|
||||
Argument is optional filename.\n\
|
||||
Default filename is 'gdb_record.<process_id>'."),
|
||||
&record_cmdlist);
|
||||
set_cmd_completer (c, filename_completer);
|
||||
|
||||
c = add_cmd ("restore", class_obscure, cmd_record_restore,
|
||||
_("Restore the execution log from a file.\n\
|
||||
Argument is filename. File must be created with 'record save'."),
|
||||
&record_cmdlist);
|
||||
set_cmd_completer (c, filename_completer);
|
||||
|
||||
add_cmd ("delete", class_obscure, cmd_record_delete,
|
||||
_("Delete the rest of execution log and start recording it anew."),
|
||||
|
Loading…
Reference in New Issue
Block a user