1ab3bf1b14
will make ChangeLog entries for all of them.
777 lines
20 KiB
C
777 lines
20 KiB
C
/* Support for dumping and reloading various pieces of GDB's internal state.
|
||
Copyright 1992 Free Software Foundation, Inc.
|
||
Contributed by Cygnus Support, using pieces from other GDB modules.
|
||
|
||
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 2 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, write to the Free Software
|
||
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
|
||
|
||
/* This file provides support for dumping and then later reloading various
|
||
portions of gdb's internal state. It was originally implemented to
|
||
support a need for mapping in an image of gdb's symbol table from an
|
||
external file, where this image was created by an external program, such
|
||
as an incremental linker. However, it was generalized to enable future
|
||
support for dumping and reloading various other useful pieces of gdb's
|
||
internal state.
|
||
|
||
State files have a fairly simple form which is intended to be easily
|
||
extensible. The basic format is:
|
||
|
||
<file-header> <state-data> <form-tree>
|
||
|
||
Where:
|
||
|
||
file-header A simple file-header containing a magic number
|
||
so that gdb (and other readers) can quickly
|
||
determine what kind of file this is, and a file
|
||
offset to the root of the form-tree.
|
||
|
||
state-data The "raw" state-data that is referenced by nodes
|
||
in the form-tree.
|
||
|
||
form-tree A tree of arbitrarily sized nodes containing
|
||
information about gdb's internal state, and
|
||
possibly referencing data in the state-data section
|
||
of the file. Resembles DWARF in some respects.
|
||
|
||
When writing a state file, a hole is left for the file-header at the
|
||
beginning of the file, the state data is written immediately after the
|
||
file header (while storing the file offsets and sizes back into the
|
||
internal form-tree along the way), the form-tree itself is written
|
||
at the end of the file, and then the file header is written by seeking
|
||
back to the beginning of the file. This order is required because
|
||
the form tree contains file offsets and sizes in the state data portion
|
||
of the file, and the file header contains the file offset to the start
|
||
of the form tree.
|
||
|
||
Readers simply open the file, validate the magic number, seek to the
|
||
root of the form-tree, and walk the tree looking for the information that
|
||
they are interested in (and ignoring things that they aren't, or don't
|
||
understand).
|
||
|
||
*/
|
||
|
||
|
||
#include <stdio.h>
|
||
#include "defs.h"
|
||
#include "symtab.h"
|
||
#include "bfd.h"
|
||
#include "symfile.h"
|
||
#include "state.h"
|
||
|
||
#ifndef SEEK_SET
|
||
#define SEEK_SET 0
|
||
#endif
|
||
|
||
#ifndef SEEK_END
|
||
#define SEEK_END 2
|
||
#endif
|
||
|
||
/* Inside the state file, the form-tree consists of a series of
|
||
form-tree entries (FTE's). The parent/child/sibling relationships
|
||
are implied by the ordering and by an explicit sibling reference
|
||
in FTE's that have siblings.
|
||
|
||
Specifically, given two sequential FTE's, say A and B, if B immediately
|
||
follows A, and A does not have a sibling reference to B, then B is
|
||
the first child of A. Otherwise B must be a sibling of A and A must
|
||
have a sibling reference for it.
|
||
|
||
Each FTE is simply an array of long integers, with at least three
|
||
members. This form was chosen over a packed data form for simplicity
|
||
in access, not having to worry about the relative sizes of the different
|
||
integers (short, int, long), and not having to worry about alignment
|
||
constraints. Also in the name of simplicity, every FTE has a sibling
|
||
reference slot reserved for it, even if there are no siblings.
|
||
|
||
The first value in an FTE is the size of the FTE in bytes, including
|
||
the size value itself. The second entry contains a tag which indicates
|
||
the type of the FTE. The third entry is a sibling reference, which either
|
||
refers to a valid sibling node or is zero. Following is zero or more
|
||
attributes, each of which consists of one or more long values. */
|
||
|
||
/* Tag names and codes. */
|
||
|
||
#define TAG_padding 0x0000 /* Padding */
|
||
#define TAG_objfile 0x0001 /* Dumped objfile */
|
||
|
||
/* Form names, codes, and macros. */
|
||
|
||
#define FORM_ABSREF 0x01 /* Next long is absolute file offset */
|
||
#define FORM_RELREF 0x02 /* Next long is relative file offset */
|
||
#define FORM_IVAL 0x03 /* Next long is int value */
|
||
#define FORM_ADDR 0x04 /* Next long is mem addr */
|
||
|
||
#define FORM_MASK 0xFF
|
||
#define FORM_X(atr) ((atr) & FORM_MASK)
|
||
|
||
/* Attribute names and codes. */
|
||
|
||
#define AT_sibling (0x0100 | FORM_RELREF) /* Reference to sibling node */
|
||
#define AT_name (0x0200 | FORM_ABSREF) /* Reference to a string */
|
||
#define AT_offset (0x0300 | FORM_ABSREF) /* Reference to generic data */
|
||
#define AT_size (0x0400 | FORM_IVAL)
|
||
#define AT_addr (0x0500 | FORM_ADDR)
|
||
#define AT_aux_addr (0x0600 | FORM_ADDR)
|
||
|
||
/* */
|
||
|
||
static void
|
||
load_symbols PARAMS ((FILE *));
|
||
|
||
static void
|
||
dump_state_command PARAMS ((char *, int));
|
||
|
||
static void
|
||
load_state_command PARAMS ((char *, int));
|
||
|
||
#ifdef HAVE_MMAP
|
||
|
||
static void
|
||
write_header PARAMS ((sfd *));
|
||
|
||
static void
|
||
write_formtree PARAMS ((sfd *));
|
||
|
||
static void
|
||
write_objfile_state PARAMS ((sfd *));
|
||
|
||
static void
|
||
free_subtree PARAMS ((struct formnode *));
|
||
|
||
static void
|
||
size_subtree PARAMS ((struct formnode *));
|
||
|
||
#endif
|
||
|
||
struct formnode *formtree = NULL;
|
||
|
||
/* ARGSUSED */
|
||
static void
|
||
load_symbols (statefile)
|
||
FILE *statefile;
|
||
{
|
||
|
||
#if 0
|
||
/* Discard old symbols. FIXME: This is essentially symbol_file_command's
|
||
body when there is no name. Make it a common function that is
|
||
called from each place. */
|
||
|
||
if (symfile_objfile)
|
||
{
|
||
free_objfile (symfile_objfile);
|
||
}
|
||
symfile_objfile = NULL;
|
||
#endif
|
||
|
||
#if 0 && defined (HAVE_MMAP)
|
||
if (mtop > mbase)
|
||
{
|
||
warning ("internal error: mbase (%08x) != mtop (%08x)",
|
||
mbase, mtop);
|
||
munmap (mbase, mtop - mbase);
|
||
}
|
||
#endif /* HAVE_MMAP */
|
||
|
||
/* Getting new symbols may change our opinion about what is frameless. */
|
||
|
||
reinit_frame_cache ();
|
||
|
||
}
|
||
|
||
#ifdef HAVE_MMAP
|
||
|
||
/* Allocate a form node */
|
||
|
||
static struct formnode *
|
||
alloc_formnode ()
|
||
{
|
||
struct formnode *fnp;
|
||
|
||
fnp = (struct formnode *) xmalloc (sizeof (struct formnode));
|
||
(void) memset (fnp, 0, sizeof (struct formnode));
|
||
fnp -> sibling = formtree;
|
||
formtree = fnp;
|
||
return (fnp);
|
||
}
|
||
|
||
/* Recursively walk a form-tree from the specified node, freeing
|
||
nodes from the bottom up. The concept is pretty simple, just free
|
||
all the child nodes, then all the sibling nodes, then the node
|
||
itself. */
|
||
|
||
static void
|
||
free_subtree (fnp)
|
||
struct formnode *fnp;
|
||
{
|
||
if (fnp != NULL)
|
||
{
|
||
free_subtree (fnp -> child);
|
||
free_subtree (fnp -> sibling);
|
||
if (fnp -> nodedata != NULL)
|
||
{
|
||
free (fnp -> nodedata);
|
||
}
|
||
free (fnp);
|
||
}
|
||
}
|
||
|
||
/* Recursively walk a form-tree from the specified node, computing the
|
||
size of each subtree from the bottom up.
|
||
|
||
At each node, the file space that will be consumed by the subtree
|
||
rooted in that node is the sum of all the subtrees rooted in each
|
||
child node plus the size of the node itself.
|
||
|
||
Thus for each node, we size the child subtrees, add to that our
|
||
size, contribute this size towards the size of any parent node, and
|
||
then ask any of our siblings to do the same.
|
||
|
||
Also, once we know the size of any subtree rooted at this node, we
|
||
can initialize the offset to the sibling node (if any).
|
||
|
||
Since every form-tree node must have valid nodedata at this point,
|
||
we detect and report a warning for any node that doesn't. */
|
||
|
||
static void
|
||
size_subtree (fnp)
|
||
struct formnode *fnp;
|
||
{
|
||
long *lp;
|
||
|
||
if (fnp != NULL)
|
||
{
|
||
if (fnp -> nodedata == NULL)
|
||
{
|
||
warning ("internal error -- empty form node");
|
||
}
|
||
else
|
||
{
|
||
size_subtree (fnp -> child);
|
||
fnp -> treesize += *(long *) fnp -> nodedata;
|
||
if (fnp -> parent != NULL)
|
||
{
|
||
fnp -> parent -> treesize += fnp -> treesize;
|
||
}
|
||
if (fnp -> sibling)
|
||
{
|
||
size_subtree (fnp -> sibling);
|
||
lp = (long *) (fnp -> nodedata + 2 * sizeof (long));
|
||
*lp = fnp -> treesize;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* Recursively walk a form-tree from the specified node, writing
|
||
nodes from the top down. */
|
||
|
||
static void
|
||
write_subtree (fnp, asfd)
|
||
struct formnode *fnp;
|
||
sfd *asfd;
|
||
{
|
||
if (fnp != NULL)
|
||
{
|
||
if (fnp -> nodedata != NULL)
|
||
{
|
||
fwrite (fnp -> nodedata, *(long *) fnp -> nodedata, 1, asfd -> fp);
|
||
}
|
||
write_subtree (fnp -> child, asfd);
|
||
write_subtree (fnp -> sibling, asfd);
|
||
}
|
||
}
|
||
|
||
/* Free the entire current formtree. Called via do_cleanups, regardless
|
||
of whether there is an error or not. */
|
||
|
||
static void
|
||
free_formtree ()
|
||
{
|
||
free_subtree (formtree);
|
||
formtree = NULL;
|
||
}
|
||
|
||
/* Write out the file header. Generally this is done last, even though
|
||
it is located at the start of the file, since we need to have file
|
||
offset to where the annotated form tree was written, and it's size. */
|
||
|
||
static void
|
||
write_header (asfd)
|
||
sfd *asfd;
|
||
{
|
||
fseek (asfd -> fp, 0L, SEEK_SET);
|
||
fwrite ((char *) &asfd -> hdr, sizeof (asfd -> hdr), 1, asfd -> fp);
|
||
}
|
||
|
||
/* Write out the annotated form tree. We should already have written out
|
||
the state data, and noted the file offsets and sizes in each node of
|
||
the form tree that references part of the state data.
|
||
|
||
The form tree can be written anywhere in the file where there is room
|
||
for it. Since there is always room at the end of the file, we write
|
||
it there. We also need to record the file offset to the start of the
|
||
form tree, and it's size, for future use when writing the file header.
|
||
|
||
In order to compute the sibling references, we need to know, at
|
||
each node, how much space will be consumed when all of that node's
|
||
children nodes have been written. Thus we walk the tree, computing
|
||
the sizes of the subtrees from the bottom up. At any node, the
|
||
offset from the start of that node to the start of the sibling node
|
||
is simply the size of the node plus the size of the subtree rooted
|
||
in that node. */
|
||
|
||
static void
|
||
write_formtree (asfd)
|
||
sfd *asfd;
|
||
{
|
||
size_subtree (formtree);
|
||
fseek (asfd -> fp, 0L, SEEK_END);
|
||
asfd -> hdr.sf_ftoff = ftell (asfd -> fp);
|
||
write_subtree (formtree, asfd);
|
||
asfd -> hdr.sf_ftsize = ftell (asfd -> fp) - asfd -> hdr.sf_ftoff;
|
||
}
|
||
|
||
/* Note that we currently only support having one objfile with dumpable
|
||
state. */
|
||
|
||
static void
|
||
write_objfile_state (asfd)
|
||
sfd *asfd;
|
||
{
|
||
struct objfile *objfile;
|
||
struct formnode *fnp;
|
||
PTR base;
|
||
PTR breakval;
|
||
long *lp;
|
||
unsigned int ftesize;
|
||
long ftebuf[64];
|
||
long foffset;
|
||
|
||
/* First walk through the objfile list looking for the first objfile
|
||
that is dumpable. */
|
||
|
||
for (objfile = object_files; objfile != NULL; objfile = objfile -> next)
|
||
{
|
||
if (objfile -> flags & OBJF_DUMPABLE)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (objfile == NULL)
|
||
{
|
||
warning ("no dumpable objfile was found");
|
||
}
|
||
else
|
||
{
|
||
fnp = alloc_formnode ();
|
||
lp = ftebuf;
|
||
|
||
lp++; /* Skip FTE size slot, filled in at the end. */
|
||
*lp++ = TAG_objfile; /* This is an objfile FTE */
|
||
*lp++ = 0; /* Zero the sibling reference slot. */
|
||
|
||
/* Build an AT_name attribute for the objfile's name, and write
|
||
the name into the state data. */
|
||
|
||
*lp++ = AT_name;
|
||
*lp++ = (long) ftell (asfd -> fp);
|
||
fwrite (objfile -> name, strlen (objfile -> name) + 1, 1, asfd -> fp);
|
||
|
||
/* Build an AT_addr attribute for the virtual address to which the
|
||
objfile data is mapped (and needs to be remapped when read in). */
|
||
|
||
base = mmap_base ();
|
||
*lp++ = AT_addr;
|
||
*lp++ = (long) base;
|
||
|
||
/* Build an AT_aux_addr attribute for the address of the objfile
|
||
structure itself, within the dumpable data. When we read the objfile
|
||
back in, we use this address as the pointer the "struct objfile". */
|
||
|
||
*lp++ = AT_aux_addr;
|
||
*lp++ = (long) objfile;
|
||
|
||
/* Reposition in state file to next paging boundry so we can mmap the
|
||
dumpable objfile data when we reload it. */
|
||
|
||
foffset = (long) mmap_page_align ((PTR) ftell (asfd -> fp));
|
||
fseek (asfd -> fp, foffset, SEEK_SET);
|
||
|
||
/* Build an AT_offset attribute for the offset in the state file to
|
||
the start of the dumped objfile data. */
|
||
|
||
*lp++ = AT_offset;
|
||
*lp++ = (long) ftell (asfd -> fp);
|
||
|
||
/* Build an AT_size attribute for the size of the dumped objfile data. */
|
||
|
||
breakval = mmap_sbrk (0);
|
||
*lp++ = AT_size;
|
||
*lp++ = breakval - base;
|
||
|
||
/* Write the dumpable data. */
|
||
|
||
fwrite ((char *) base, breakval - base, 1, asfd -> fp);
|
||
|
||
/* Now finish up the FTE by filling in the size slot based on
|
||
how much of the ftebuf we have used, allocate some memory for
|
||
it hung off the form tree node, and copy it there. */
|
||
|
||
ftebuf[0] = (lp - ftebuf) * sizeof (ftebuf[0]);
|
||
fnp -> nodedata = (char *) xmalloc (ftebuf[0]);
|
||
memcpy (fnp -> nodedata, ftebuf, ftebuf[0]);
|
||
}
|
||
}
|
||
|
||
static void
|
||
load_state_command (arg_string, from_tty)
|
||
char *arg_string;
|
||
int from_tty;
|
||
{
|
||
char *filename;
|
||
char **argv;
|
||
FILE *fp;
|
||
struct cleanup *cleanups;
|
||
|
||
dont_repeat ();
|
||
|
||
if (arg_string == NULL)
|
||
{
|
||
error ("load-state takes a file name and optional state specifiers");
|
||
}
|
||
else if ((argv = buildargv (arg_string)) == NULL)
|
||
{
|
||
fatal ("virtual memory exhausted.", 0);
|
||
}
|
||
cleanups = make_cleanup (freeargv, argv);
|
||
|
||
filename = tilde_expand (*argv);
|
||
make_cleanup (free, filename);
|
||
|
||
if ((fp = fopen (filename, "r")) == NULL)
|
||
{
|
||
perror_with_name (filename);
|
||
}
|
||
make_cleanup (fclose, fp);
|
||
immediate_quit++;
|
||
|
||
while (*++argv != NULL)
|
||
{
|
||
if (strcmp (*argv, "symbols") == 0)
|
||
{
|
||
if (from_tty
|
||
&& !query ("load symbol table state from file \"%s\"? ",
|
||
filename))
|
||
{
|
||
error ("Not confirmed.");
|
||
}
|
||
load_symbols (fp);
|
||
}
|
||
else
|
||
{
|
||
error ("unknown state specifier '%s'", *argv);
|
||
}
|
||
}
|
||
immediate_quit--;
|
||
do_cleanups (cleanups);
|
||
}
|
||
|
||
/* ARGSUSED */
|
||
static void
|
||
dump_state_command (arg_string, from_tty)
|
||
char *arg_string;
|
||
int from_tty;
|
||
{
|
||
char *filename;
|
||
char **argv;
|
||
sfd *asfd;
|
||
struct cleanup *cleanups;
|
||
|
||
dont_repeat ();
|
||
|
||
if (arg_string == NULL)
|
||
{
|
||
error ("dump-state takes a file name and state specifiers");
|
||
}
|
||
else if ((argv = buildargv (arg_string)) == NULL)
|
||
{
|
||
fatal ("virtual memory exhausted.", 0);
|
||
}
|
||
cleanups = make_cleanup (freeargv, argv);
|
||
|
||
filename = tilde_expand (*argv);
|
||
make_cleanup (free, filename);
|
||
|
||
/* Now attempt to create a fresh state file. */
|
||
|
||
if ((asfd = sfd_fopen (filename, "w")) == NULL)
|
||
{
|
||
perror_with_name (filename);
|
||
}
|
||
make_cleanup (sfd_fclose, asfd);
|
||
make_cleanup (free_formtree, NULL);
|
||
immediate_quit++;
|
||
|
||
/* Now that we have an open and initialized state file, seek to the
|
||
proper offset to start writing state data and the process the
|
||
arguments. For each argument, write the state data and initialize
|
||
a form-tree node for each piece of state data. */
|
||
|
||
fseek (asfd -> fp, sizeof (sf_hdr), SEEK_SET);
|
||
while (*++argv != NULL)
|
||
{
|
||
if (strcmp (*argv, "objfile") == 0)
|
||
{
|
||
write_objfile_state (asfd);
|
||
}
|
||
else
|
||
{
|
||
error ("unknown state specifier '%s'", *argv);
|
||
}
|
||
|
||
}
|
||
|
||
/* We have written any state data. All that is left to do now is
|
||
write the form-tree and the file header. */
|
||
|
||
write_formtree (asfd);
|
||
write_header (asfd);
|
||
|
||
immediate_quit--;
|
||
do_cleanups (cleanups);
|
||
}
|
||
|
||
static char *
|
||
find_fte_by_walk (thisfte, endfte, tag)
|
||
char *thisfte;
|
||
char *endfte;
|
||
long tag;
|
||
{
|
||
char *found = NULL;
|
||
char *nextfte;
|
||
long thistag;
|
||
long thissize;
|
||
long siboffset;
|
||
|
||
while (thisfte < endfte)
|
||
{
|
||
if ((thistag = *(long *)(thisfte + sizeof (long))) == tag)
|
||
{
|
||
found = thisfte;
|
||
break;
|
||
}
|
||
else
|
||
{
|
||
thissize = *(long *)(thisfte);
|
||
siboffset = *(long *)(thisfte + (2 * sizeof (long)));
|
||
nextfte = thisfte + (siboffset != 0 ? siboffset : thissize);
|
||
found = find_fte_by_walk (thisfte + thissize, nextfte, tag);
|
||
thisfte = nextfte;
|
||
}
|
||
}
|
||
return (found);
|
||
}
|
||
|
||
/* Walk the form-tree looking for a specific FTE type. Returns the first
|
||
one found that matches the specified tag. */
|
||
|
||
static char *
|
||
find_fte (asfd, tag)
|
||
sfd *asfd;
|
||
long tag;
|
||
{
|
||
char *ftbase;
|
||
char *ftend;
|
||
char *ftep;
|
||
char *found = NULL;
|
||
|
||
if (fseek (asfd -> fp, asfd -> hdr.sf_ftoff, SEEK_SET) == 0)
|
||
{
|
||
ftbase = xmalloc (asfd -> hdr.sf_ftsize);
|
||
ftend = ftbase + asfd -> hdr.sf_ftsize;
|
||
if (fread (ftbase, asfd -> hdr.sf_ftsize, 1, asfd -> fp) == 1)
|
||
{
|
||
ftep = find_fte_by_walk (ftbase, ftend, tag);
|
||
if (ftep != NULL)
|
||
{
|
||
found = xmalloc (*(long *)ftep);
|
||
memcpy (found, ftep, (int) *(long *)ftep);
|
||
}
|
||
}
|
||
free (ftbase);
|
||
}
|
||
return (found);
|
||
}
|
||
|
||
struct objfile *
|
||
objfile_from_statefile (asfd)
|
||
sfd *asfd;
|
||
{
|
||
struct objfile *objfile = NULL;
|
||
char *ftep;
|
||
long *thisattr;
|
||
long *endattr;
|
||
PTR base;
|
||
long foffset;
|
||
long mapsize;
|
||
|
||
ftep = find_fte (asfd, TAG_objfile);
|
||
thisattr = (long *) (ftep + 3 * sizeof (long));
|
||
endattr = (long *) (ftep + *(long *)ftep);
|
||
while (thisattr < endattr)
|
||
{
|
||
switch (*thisattr++)
|
||
{
|
||
case AT_name:
|
||
/* Ignore for now */
|
||
thisattr++;
|
||
break;
|
||
case AT_addr:
|
||
base = (PTR) *thisattr++;
|
||
break;
|
||
case AT_aux_addr:
|
||
objfile = (struct objfile *) *thisattr++;
|
||
break;
|
||
case AT_offset:
|
||
foffset = *thisattr++;
|
||
break;
|
||
case AT_size:
|
||
mapsize = *thisattr++;
|
||
break;
|
||
}
|
||
}
|
||
if (mmap_remap (base, mapsize, (int) fileno (asfd -> fp), foffset) != base)
|
||
{
|
||
print_sys_errmsg (asfd -> filename, errno);
|
||
error ("mapping failed");
|
||
}
|
||
|
||
return (objfile);
|
||
}
|
||
|
||
#else
|
||
|
||
struct objfile *
|
||
objfile_from_statefile (asfd)
|
||
sfd *asfd;
|
||
{
|
||
error ("this version of gdb doesn't support reloading symtabs from state files");
|
||
}
|
||
|
||
#endif /* HAVE_MMAP */
|
||
|
||
/* Close a state file, freeing all memory that was used by the state
|
||
file descriptor, closing the raw file pointer, etc. */
|
||
|
||
void
|
||
sfd_fclose (asfd)
|
||
sfd *asfd;
|
||
{
|
||
if (asfd != NULL)
|
||
{
|
||
if (asfd -> fp != NULL)
|
||
{
|
||
fclose (asfd -> fp);
|
||
}
|
||
if (asfd -> filename != NULL)
|
||
{
|
||
free (asfd -> filename);
|
||
}
|
||
free (asfd);
|
||
}
|
||
}
|
||
|
||
/* Given the name of a possible statefile, and flags to use to open it,
|
||
try to open the file and prepare it for use.
|
||
|
||
If the flags contain 'r', then we want to read an existing state
|
||
file, so attempt to read in the state file header and determine if this
|
||
is a valid state file. If not, return NULL.
|
||
|
||
Returns a pointer to a properly initialized state file descriptor if
|
||
successful. */
|
||
|
||
sfd *
|
||
sfd_fopen (name, flags)
|
||
char *name;
|
||
char *flags;
|
||
{
|
||
int success = 0;
|
||
sfd *asfd;
|
||
|
||
asfd = (sfd *) xmalloc (sizeof (sfd));
|
||
(void) memset (asfd, 0, sizeof (sfd));
|
||
asfd -> filename = xmalloc (strlen (name) + 1);
|
||
(void) strcpy (asfd -> filename, name);
|
||
|
||
if ((asfd -> fp = fopen (asfd -> filename, flags)) != NULL)
|
||
{
|
||
/* We have the file, now see if we are reading an existing file
|
||
or writing to a new file. We don't currently support "rw". */
|
||
if (strchr (flags, 'r') != NULL)
|
||
{
|
||
if (fread ((char *) &asfd -> hdr, sizeof (asfd -> hdr), 1,
|
||
asfd -> fp) == 1)
|
||
{
|
||
if (SF_GOOD_MAGIC (asfd))
|
||
{
|
||
success = 1;
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/* This is a new state file. Initialize various things. */
|
||
asfd -> hdr.sf_mag0 = SF_MAG0;
|
||
asfd -> hdr.sf_mag1 = SF_MAG1;
|
||
asfd -> hdr.sf_mag2 = SF_MAG2;
|
||
asfd -> hdr.sf_mag3 = SF_MAG3;
|
||
success = 1;
|
||
}
|
||
}
|
||
|
||
if (!success)
|
||
{
|
||
sfd_fclose (asfd);
|
||
asfd = NULL;
|
||
}
|
||
return (asfd);
|
||
|
||
}
|
||
|
||
|
||
void
|
||
_initialize_state ()
|
||
{
|
||
|
||
#ifdef HAVE_MMAP
|
||
|
||
add_com ("load-state", class_support, load_state_command,
|
||
"Load some saved gdb state from FILE.\n\
|
||
Select and load some portion of gdb's saved state from the specified file.\n\
|
||
The dump-state command may be used to save various portions of gdb's\n\
|
||
internal state.");
|
||
|
||
add_com ("dump-state", class_support, dump_state_command,
|
||
"Dump some of gdb's state to FILE.\n\
|
||
Select and dump some portion of gdb's internal state to the specified file.\n\
|
||
The load-state command may be used to reload various portions of gdb's\n\
|
||
internal state from the file.");
|
||
|
||
#endif /* HAVE_MMAP */
|
||
|
||
}
|