home *** CD-ROM | disk | FTP | other *** search
/ The World of Computer Software / World_Of_Computer_Software-02-387-Vol-3of3.iso / p / plbin.zip / pl / src / pl-save.c < prev    next >
C/C++ Source or Header  |  1993-03-04  |  17KB  |  605 lines

  1. /*  pl-save.c,v 1.11 1993/02/23 13:16:45 jan Exp
  2.  
  3.     Copyright (c) 1991 Jan Wielemaker. All rights reserved.
  4.     See ../LICENCE to find out about your rights.
  5.     jan@swi.psy.uva.nl
  6.  
  7.     Purpose: Create saved state
  8. */
  9.  
  10. #include "pl-incl.h"
  11.  
  12. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  13. This file defines  a package to  create and restore  states of Prolog.
  14. Actually there is little  that makes it specific  to prolog.   Earlier
  15. versions of Prolog used the GNU unexec() code underlying gnu-emacs and
  16. undump.  Together with  dynamic  stack  allocation and loading foreign
  17. code, this used the be the main reason for Prolog not  to be portable.
  18. This package  is an  attempt  to  create  a reasonable   portable save
  19. functionality.
  20.  
  21.  
  22. HOW DOES IT WORK?
  23.  
  24. This program tries  to find all  parts of memory  that contains  valid
  25. information, except for the text-segment.  These sections are saved on
  26. file together with some administrative information.  Restoring a state
  27. implies memory areas are set-up equivalent to the state when the state
  28. is  saved; all the  sections are read back  in  place and  the  system
  29. performs a longjmp(), either to the start of the  program, or to where
  30. it was.
  31.  
  32.  
  33. ASSUMPTIONS
  34.  
  35.   1) We  assume  the running prolog image to  consists of a  `text' area
  36.   that   is read-only and in   any  run  of prolog   loaded  at the same
  37.   addresses.  This  will be  true  on almost any machine  using  virtual
  38.   memory (and (thus) no relocation).
  39.  
  40.   2) There is memory area  that holds the programs data  as well as  the
  41.   data belonging to malloc(), etc.   By default,  this is area  &environ
  42.   ... sbrk(0)
  43.  
  44.   3)  (for save/[1,2] only)  The C-stack  can be   reloaded at the  same
  45.   position.
  46.  
  47.   4) The program  knows  about possible other  memory areas.  When using
  48.   dynamic-stacks, the mapped areas are passed for save/[1,2].
  49.  
  50.  
  51. PORTABILITY/OPTIONS
  52.  
  53.   O_C_STACK_GROWS_UP
  54.     Each subsequent C-stackframe  is  at a  higher address.  Note that
  55.     addresses are considered unsigned.  By default we assume the stack
  56.     grows downwards.
  57.  
  58.   BASE_OF_C_STACK 
  59.     If your machine has a fixed address where the C-stack  starts, you
  60.     may #define BASE_OF_C_STACK to  this  address.  Normally using the
  61.     STACK_BASE_ALIGN   mechanism  is   a better   guarantee   you  are
  62.     independant of versions of the OS.
  63.  
  64.   STACK_BASE_ALIGN
  65.     If BASE_OF_C_STACK is  not defined,  we will round  the pointer to
  66.     argc  of main()  depending  on  O_C_STACK_GROWS_UP down/up  to   a
  67.     multiple of this value (default: 64 Kbytes).  This generally finds
  68.     the stack's base address.
  69. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  70.  
  71. #if OS2 && EMX
  72. /*                      Notes on the OS/2 port.
  73.             atoenne@mpi-sb.mpg.de
  74.  
  75.     The save/2 primitive had a rather unusual problem in OS/2. The save
  76.     function tries to write the memory between start of data and the
  77.     end of the heap as one huge chunk to disk. OS/2 however does not
  78.     allocate the junk area between the end of data and the start of
  79.     the heap. Consequently we get a protection violation with the original
  80.     save function. We have to write two sections, one for data and
  81.     one for the heap. These areas are determined as follows:
  82.  
  83.     data ranges from &_data to &_end
  84.     heap ranges from _heap_base to sbrk(0)
  85.  
  86.     The routines for determing the stack frame are fine.
  87.  
  88. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  89. #endif /* OS2 */
  90.  
  91. #if TEST
  92. #undef DEBUG
  93. #define DEBUG(l, g) {g;}
  94. #endif
  95.  
  96. #if O_SAVE
  97. #include <sys/types.h>
  98. #include <unistd.h>
  99. #include <stdio.h>
  100. #include <setjmp.h>
  101. #include <fcntl.h>
  102. #include "pl-save.h"
  103.  
  104. #if sun
  105. int    brk P((caddr_t));
  106. caddr_t sbrk P((int));
  107. #endif
  108. #if OS2 & EMX
  109. extern caddr _heap_base;
  110. extern long _heap_end;
  111. #endif /* OS2 */
  112.  
  113. extern char **environ;
  114.  
  115. #ifndef DATA_START
  116. #ifndef FIRST_DATA_SYMBOL
  117. #define FIRST_DATA_SYMBOL environ;
  118. #endif
  119. #define DATA_START &FIRST_DATA_SYMBOL
  120. #endif
  121.  
  122. #ifndef DATA_END
  123. #ifdef LAST_DATA_SYMBOL
  124. #define DATA_END ((caddr) &LAST_DATA_SYMBOL)
  125. #else
  126. #define DATA_END ((caddr) sbrk(0))
  127. #endif
  128. #endif
  129.  
  130. #ifdef  FIRST_DATA_SYMBOL
  131. extern  char **FIRST_DATA_SYMBOL;
  132. #endif
  133. #ifdef LAST_DATA_SYMBOL            /* --- atoenne@mpi-sb.mpg.de --- */
  134. extern  char **LAST_DATA_SYMBOL;
  135. #endif
  136.  
  137. #ifndef STACK_BASE_ALIGN
  138. #define STACK_BASE_ALIGN (1<<16)    /* 64K */
  139. #endif
  140. #define MAXSTACKFRAMESIZE 256    /* Should generally do ... */
  141. #define MAXLINE1    256    /* Maximum length of the ASCII header */
  142.  
  143. #define SAVE_MAGIC    3823212    /* Just a random number */
  144.  
  145. typedef struct save_header
  146. { long    magic;            /* Determine type of file */
  147.   long  save_version;        /* Version of the hosting process */
  148.   caddr brk;            /* Value of the break when saved */
  149.   int    nsections;        /* Number of sections available */
  150. } * SaveHeader;
  151.  
  152. #define exit(status)    Halt(status);
  153.  
  154. #define SectionOffset(n)    (sizeof(struct save_header) + \
  155.                  (n) * sizeof(struct save_section))
  156.  
  157. #define tryRead(fd, buf, n) \
  158.     if ( read(fd, buf, n) != n ) \
  159.         return warning("restore/1: read failed: %s", OsError())
  160. #define tryWrite(fd, buf, n) \
  161.     if ( write(fd, buf, n) != n ) \
  162.         return warning("save/1: write failed: %s", OsError())
  163. #define trySeek(fd, where) \
  164.     if ( lseek(fd, where, SEEK_SET) != where ) \
  165.         return warning("restore/1: seek failed: %s", OsError())
  166.  
  167.  
  168.         /********************************
  169.         *           VARIABLES        *
  170.         ********************************/
  171.  
  172. static long    save_version;    /* Magic code to verify same version */
  173. static jmp_buf ret_main_ctx;    /* Context of RET_MAIN */
  174. static jmp_buf ret_return_ctx;    /* Context of RET_RETURN */
  175. static caddr   c_stack_base;    /* Base address of C-stack */
  176.  
  177.  
  178.         /********************************
  179.         *          SAVE VERSION        *
  180.         ********************************/
  181.  
  182. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  183. This function  returns  a number depending on  the text-segment.   The
  184. text-segment should be  the same when  reloading a state.  This is not
  185. an ideal solution: it forces the entire text to be loaded.
  186. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  187.  
  188. static long
  189. saveVersion()
  190. { if ( save_version == 0 )
  191.   { long *start = (long *) saveVersion;
  192.     long *end    = (long *) startProlog;
  193.     int  step   = (end - start) / 500;
  194.     
  195.     DEBUG(1, printf("Computing saveVersion in 0x%x .. 0x%x\n", start, end));
  196.  
  197.     for(; start < end; start += step)
  198.       save_version ^= *start;
  199.   }
  200.  
  201.   DEBUG(1, printf("saveVersion = %ld\n", save_version));
  202.  
  203.   return save_version;
  204. }
  205.  
  206.  
  207.         /********************************
  208.         *              IO        *
  209.         ********************************/
  210.  
  211. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  212. The FILE related IO buffer  will be restored  in the same  state as it
  213. was  before the saved state  was created.  We want  to  read new input
  214. instead  of saved input   and therefore we   have to  clear  the input
  215. stream.  Currently this is done by setting the _cnt slot of  the stdio
  216. structure to 0.  This is not very portable.
  217.  
  218. Alternatives?   One  would be  to allocate  new  IO buffers, but then,
  219. assigning these to stdin is not very neat either.   I guess a macro is
  220. best ...
  221. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  222.  
  223. static void
  224. resetIO()
  225. { ResetTty();
  226. }
  227.  
  228.  
  229.         /********************************
  230.         *            C-STACK        *
  231.         ********************************/
  232.  
  233. static caddr
  234. topOfCStack()
  235. { int local;
  236.  
  237.   return (caddr) &local;
  238. }
  239.  
  240. static caddr
  241. baseOfCStack(argc, argv, env)
  242. int *argc;
  243. char **argv, **env;
  244. #ifdef BASE_OF_C_STACK
  245.   return (caddr) BASE_OF_C_STACK;
  246. #else
  247.  
  248.   ulong base = (ulong) &argc;
  249.   char **p;
  250.  
  251. #ifdef O_C_STACK_GROWS_UP
  252. #define BoundStack(a,b) ((a) < (b) ? (a) : (b))
  253. #else
  254. #define BoundStack(a,b) ((a) > (b) ? (a) : (b))
  255. #endif
  256.  
  257.   base = BoundStack(base, (ulong)argv);
  258.   for(p = argv; *p; p++)
  259.     base = BoundStack(base, (ulong)*p);
  260.   base = BoundStack(base, (ulong)env);
  261.   for(p = env; *p; p++)
  262.     base = BoundStack(base, (ulong)*p);
  263.  
  264. # if ( O_C_STACK_GROWS_UP )        /* round-down */
  265.   return (caddr) (base & ~(STACK_BASE_ALIGN-1));
  266. # else
  267.   return (caddr) ((base+STACK_BASE_ALIGN-1) & ~(STACK_BASE_ALIGN-1));
  268. # endif
  269. #endif
  270. }
  271.  
  272.  
  273. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  274. Read the C-stack back.  First of all, we must  try  to get out  of the
  275. region that  is  overwritten by  the  read.   We do  this   by calling
  276. ourselfves recursively until  we are  out.  We  take a  small security
  277. region: MAXSTACKFRAMESIZE.
  278.  
  279. Note, your  compiler  might  be smart  enough   to  use tail-recursion
  280. optimization on this function.  If so, you'll have to fool it or don't
  281. optimise this file.
  282. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  283.  
  284. static int
  285. readCStack(fd, start, length)
  286. int fd;
  287. caddr start;
  288. long length;
  289. #if O_C_STACK_GROWS_UP
  290.   if ( (ulong) &fd - MAXSTACKFRAMESIZE > (ulong) start )
  291. #else
  292.   if ( (ulong) &fd + MAXSTACKFRAMESIZE < (ulong) start )
  293. #endif
  294.   { DEBUG(1, printf("&fd = 0x%x\n", (unsigned) &fd));
  295.     tryRead(fd, start, length);
  296.     DEBUG(1, printf("C-stack read; starting longjmp\n"));
  297.     resetIO();
  298.     longjmp(ret_return_ctx, 1);
  299.   } else
  300.     return readCStack(fd, start, length);
  301. }
  302.  
  303.  
  304.         /********************************
  305.         *            RESTORE        *
  306.         ********************************/
  307.  
  308. #ifndef O_BINARY
  309. #define O_BINARY 0
  310. #endif
  311.  
  312. int
  313. restore(file, allocf)
  314. char *file;
  315. int (*allocf) P((SaveSection));
  316. { int fd;
  317.   char buf[MAXLINE1];
  318.   int n;
  319.   long header_offset = 0;
  320.   struct save_header header;
  321.   char *s;
  322.  
  323.   if ((fd = open(file, O_RDONLY|O_BINARY)) < 0)
  324.     return warning("restore/1: cannot open %s: %s", file, OsError());
  325.   tryRead(fd, buf, MAXLINE1);
  326.  
  327.   for(n = 0, s = buf; n++ < MAXLINE1; s++)
  328.   { if ( *s == EOS )
  329.     { header_offset = n;
  330.       break;
  331.     }
  332.   }
  333.   DEBUG(1, printf("header_offset = %d\n", header_offset));
  334.   if ( header_offset == 0 )
  335.     return warning("restore/1: %s is not a saved state", file);
  336.  
  337.   trySeek(fd, header_offset);
  338.   tryRead(fd, &header, sizeof(header));
  339.  
  340.   if ( header.magic != SAVE_MAGIC )
  341.     return warning("restore/1: %s is not a saved state", file);
  342.   if ( header.save_version != saveVersion() )
  343.     return warning("restore/1: %s has incompatible save version", file);
  344.   
  345.   if ( brk(header.brk) )
  346.     return warning("restore/1: failed to set the break: %s", OsError());
  347.  
  348.   { jmp_buf restore_ctx;    /* Will be destroyed */
  349.  
  350.     memcpy(restore_ctx, ret_main_ctx, sizeof(restore_ctx));
  351.  
  352.     for(n = 0; n < header.nsections; n++)
  353.     { struct save_section section_header;
  354.  
  355.       trySeek(fd, header_offset+SectionOffset(n));
  356.       tryRead(fd, §ion_header, sizeof(section_header));
  357.       DEBUG(1, printf("Restoring # %d (0x%x-0x%x) offset = %ld)\n",
  358.               n, (unsigned) section_header.start,
  359.               (unsigned) section_header.start + section_header.length,
  360.               header_offset+section_header.offset));
  361.       trySeek(fd, header_offset+section_header.offset);
  362.       if ( section_header.type == S_CSTACK )
  363.     readCStack(fd, section_header.start, section_header.length);
  364.       else 
  365.       { if ( allocf )
  366.       (*allocf)(§ion_header);
  367.     tryRead(fd, section_header.start, section_header.length);
  368.       }
  369.     }
  370.  
  371.     memcpy(ret_main_ctx, restore_ctx, sizeof(restore_ctx));
  372.  
  373.     resetIO();
  374.     longjmp(ret_main_ctx, 1);
  375.     /*NOTREACHED*/
  376.   }
  377. }
  378.  
  379.         /********************************
  380.         *            SAVE        *
  381.         ********************************/
  382.  
  383. #ifdef HEAP_START
  384. #define C_DATA_SECTIONS 2        /* heap separate from data  */
  385. #else
  386. #define C_DATA_SECTIONS 1
  387. #endif
  388.  
  389. int
  390. save(file, interpreter, kind, nsections, sections)
  391. char *file;
  392. char *interpreter;
  393. int kind;            /* RET_RETURN; RET_MAIN */
  394. int nsections;            /* additional sections */
  395. SaveSection sections;
  396. { int fd;
  397.   struct save_section sects[MAX_SAVE_SECTIONS];
  398.   char buf[MAXLINE1];
  399.   long header_offset;
  400.   volatile long section_offset;        /* volatile to keep gcc happy */
  401.   struct save_header header;
  402.   int nsects = nsections + C_DATA_SECTIONS;
  403.   int sects_size;
  404.   int n;
  405.   SaveSection sect;
  406.  
  407.   if ( kind == RET_RETURN )
  408.     nsects++;                /* C-stack section  */
  409.   sects_size = sizeof(struct save_section) * nsects;
  410.  
  411.   if ( (fd = open(file, O_WRONLY|O_CREAT|O_TRUNC|O_BINARY, 0777)) < 0 )
  412.     return warning("save/1: cannot write %s: %s\n", file, OsError());
  413.   
  414. #if OS2
  415.   sprintf(buf, "/* Self-starting SWI-Prolog state */\r\n'@ECHO OFF'\r\nparse source . . name\r\n\"%s -r \" name arg(1)\r\nexit\r\n\032", OsPath(interpreter));
  416. #else
  417.   sprintf(buf, "#!/bin/sh\nexec %s -r $0 $*\n", OsPath(interpreter));
  418. #endif
  419.   header_offset = strlen(buf) + 1; /* +1 to write the EOS too */
  420.   DEBUG(1, printf("header_offset = %d\n", header_offset));
  421.   tryWrite(fd, buf, header_offset);
  422.  
  423.   header.magic         = SAVE_MAGIC;
  424.   header.save_version   = saveVersion();
  425.   header.brk        = sbrk(0);
  426.   header.nsections    = nsects;
  427.     
  428.   section_offset = sizeof(header) + sects_size;
  429.  
  430.   sects[0].start    = (caddr) DATA_START;
  431.   sects[0].length    = (long) DATA_END - (long) sects[0].start;
  432.   sects[0].type        = S_DATA;
  433.   sects[0].flags    = 0;
  434. #ifdef HEAP_START
  435.   sects[1].start        = HEAP_START;
  436.   sects[1].length       = (long) sbrk(0) - (long) sects[1].start;
  437.   sects[1].type         = S_DATA;
  438.   sects[1].flags        = 0;
  439. #endif /* HEAP_START */
  440.  
  441.   memcpy(§s[C_DATA_SECTIONS], sections,
  442.      nsections * sizeof(struct save_section));
  443.  
  444.   if ( kind == RET_RETURN )
  445.   { SaveSection stack_sect = §s[nsections+C_DATA_SECTIONS];
  446.  
  447.     if ( setjmp(ret_return_ctx) )
  448.     { DEBUG(1, printf("Yipie, returning from state\n"));
  449.       return SAVE_RESTORE;
  450.     } 
  451.     
  452. #if O_C_STACK_GROWS_UP
  453.     stack_sect->start  = c_stack_base;
  454.     stack_sect->length = (ulong) topOfCStack() - (ulong) stack_sect->start;
  455. #else
  456.     stack_sect->start  = topOfCStack();
  457.     stack_sect->length = (ulong) c_stack_base - (ulong) stack_sect->start;
  458. #endif
  459.     stack_sect->type   = S_CSTACK;
  460.     stack_sect->flags  = 0;
  461.   }
  462.  
  463.   for(n=0, sect = sects; n++ < header.nsections; sect++)
  464.   { sect->offset = section_offset;
  465.     section_offset += sect->length;
  466.   }
  467.  
  468.   tryWrite(fd, &header, sizeof(header));
  469.   tryWrite(fd, sects, sects_size);
  470.  
  471.   for(n=0, sect = sects; n++ < header.nsections; sect++)
  472.   { DEBUG(1, printf("Saving # %d 0x%x-0x%x (offset = %ld)\n",
  473.             n,
  474.             (unsigned) sect->start,
  475.             (unsigned) sect->start+sect->length,
  476.             tell(fd)));
  477.     tryWrite(fd, sect->start, sect->length);
  478.   }
  479.  
  480.   close(fd);
  481.  
  482.   return SAVE_SAVE;
  483. }
  484.  
  485.  
  486. /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
  487. main() stub
  488. - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  489.  
  490. int
  491. main(argc, argv, env)
  492. int argc;
  493. char **argv;
  494. char **env;
  495. { int rval;
  496.  
  497.   c_stack_base = baseOfCStack(&argc, argv, env);
  498.  
  499.   if ( setjmp(ret_main_ctx) )
  500.   { DEBUG(1, printf("Restarting startProlog()\n"));
  501.     environ = env;
  502.     rval = startProlog(argc, argv, env);
  503.   } else
  504.   { if ( argc >= 3 && streq(argv[1], "-r") )
  505. #if O_DYNAMIC_STACKS
  506.     { extern int allocateSection P((SaveSection));
  507.  
  508.       if ( !restore(argv[2], allocateSection) )
  509.     exit(1);
  510.     }
  511. #else
  512.     { if ( !restore(argv[2], NULL) )
  513.     exit(1);
  514.     }
  515. #endif
  516.  
  517.     rval = startProlog(argc, argv, env);
  518.   }
  519.  
  520.   exit(rval);
  521.   return 1;
  522. }
  523.  
  524. #else /* !O_SAVE */
  525.  
  526. int
  527. main(argc, argv, env)
  528. int argc;
  529. char **argv;
  530. char **env;
  531. { exit(startProlog(argc, argv, env));
  532.   return 1;
  533. }
  534.  
  535. #endif
  536.  
  537.  
  538. #if TEST                /* test stand-alone */
  539.  
  540. static value = 1;
  541. static dumped = 0;
  542.  
  543. bool
  544. warning(va_alist)
  545. va_dcl
  546. { va_list args;
  547.   char *fm;
  548.  
  549.   va_start(args);
  550.   fm = va_arg(args, char *);
  551.   fprintf(stderr, "[WARNING: ");
  552.   vfprintf(stderr, fm, args);
  553.   fprintf(stderr, "]\n");
  554.   va_end(args);
  555.  
  556.   fail;
  557. }
  558.  
  559.  
  560. char *
  561. OsError()
  562. { static char errmsg[64];
  563.   extern int sys_nerr;
  564.   extern char *sys_errlist[];
  565.   extern int errno;
  566.  
  567.   if ( errno < sys_nerr )
  568.     return sys_errlist[errno];
  569.  
  570.   sprintf(errmsg, "Unknown Error (%d)", errno);
  571.   return errmsg;
  572. }
  573.  
  574.  
  575. startProlog(argc, argv, env)
  576. int argc;
  577. char **argv, **env;
  578. { char *interpreter = argv[0];
  579.  
  580.   argc--; argv++;
  581.  
  582.   printf("value = %d\n", value++);
  583.  
  584.   for ( ; argc > 1; argc--, argv++ )
  585.   { if ( streq(argv[0], "-s") )
  586.     { save(argv[1], interpreter, RET_MAIN, 0, NULL);
  587.       printf("Saved (-s) in %s\n", argv[1]);
  588.       exit(0);
  589.     }
  590.     if ( streq(argv[0], "-S") )
  591.     { if ( save(argv[1], interpreter, RET_RETURN, 0, NULL) == SAVE_SAVE )
  592.     printf("Saved (-S) in %s\n", argv[1]);
  593.       else
  594.     printf("Restored from %s\n", argv[1]);
  595.       exit(0);
  596.     }
  597.   }
  598.  
  599.   exit(0);
  600. }
  601.  
  602. #endif
  603.