diff options
author | John Gilmore <gnu@cygnus> | 1992-02-22 01:46:16 +0000 |
---|---|---|
committer | John Gilmore <gnu@cygnus> | 1992-02-22 01:46:16 +0000 |
commit | 1ab3bf1b148d31aad66735f52f9ff72af8769cd0 (patch) | |
tree | f599e61700fc54d6ecd3d090e3d01cf6fa66a801 /gdb/state.c | |
parent | 8e48d87af60233cc7e8dc18ab4e8f63d223ac20f (diff) | |
download | gdb-1ab3bf1b148d31aad66735f52f9ff72af8769cd0.zip gdb-1ab3bf1b148d31aad66735f52f9ff72af8769cd0.tar.gz gdb-1ab3bf1b148d31aad66735f52f9ff72af8769cd0.tar.bz2 |
* Check in Fred Fish's changes in these modules. Fred
will make ChangeLog entries for all of them.
Diffstat (limited to 'gdb/state.c')
-rw-r--r-- | gdb/state.c | 777 |
1 files changed, 777 insertions, 0 deletions
diff --git a/gdb/state.c b/gdb/state.c new file mode 100644 index 0000000..b6ce398 --- /dev/null +++ b/gdb/state.c @@ -0,0 +1,777 @@ +/* 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 */ + +} |