Patch title: Release 87 bulk changes
Abstract:
File: /pliant/language/os/dump.c
Key:
    Removed line
    Added line
// Copyright  Hubert Tonneau  hubert.tonneau@pliant.cx
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License version 2
// as published by the Free Software Foundation.
// 
// 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
// version 2 along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

/*
abstract
  [Functions to backup and restart a Pliant process !]
doc
  [This is the ugly, tricky and very powerfull function that enables Pliant to dump the content of the memory to a file, then restore it, and readjust all details to be able to continue just as if the process had never been stopped (This could be provided at OS level because it's part of Posix if I remember well, but it seems not to be able on many OS).] ; eol
  [It's the current only mechanism for precompiling some modules.] ; eol
  [It's advantages are:]
  list
    item [it's very fast (much faster than loading DLLs).]
  [It's disadvantages are:]
  list
    item [it can be done only once at the beginning of the process, so it's not as modular as loading and unloading DLLs.]
    item [any change in the process memory layout due to OS configuration change will prevent 'process_restore' to work: this often append when you add, remove or update some packages in the Linux distribution.]
  para
    [At some point, we will need to implement a second mechanism that will enable to fast compile (load a DLL) a module, because both mechanisms are valuable and needed in real life.]
  para
    [The actions to be performed to set up back the process are recorded as a list of hooks in 'G.restore_actions'.]
    [The 'G.backup_actions' list of hooks enables you to write extra informations in the dump file that you'll read at restore time using hooks recorded in 'G.restore_actions'.]
*/


struct PliantSignature {
  uInt32 signature;
  uInt32 c_level;
  uInt32 release; 
  #if defined(_DLL_)
    Address expression_compile;
  #endif
  };

struct PliantArea {
  Address address;
  Int size; };


static Void add_string(Char *string,struct Str *temp) {
  Int len=string_len(string);
  memory_copy(string,temp->chars+temp->len2,len); temp->len2+=len; }

static Bool build_full_br_name(struct Str *filename,Char *buffer,struct Str *temp) {
  Char *point; Int delta;
  if(Str_len(filename)==0 || filename->chars[0]!='/')
    return false;
  point=memory_search(filename->chars,Str_len(filename),".",1);
  if(point==null)
    return false;
  delta=point-filename->chars;
  temp->chars=buffer,temp->len2=Str_len(&G.pliant_root_path);
  memory_copy(G.pliant_root_path.chars,buffer,temp->len2);
  memory_copy(filename->chars+1,temp->chars+temp->len2,delta-1); temp->len2+=delta-1;
  add_string("-debug",temp);
  temp->chars[temp->len2++]='0'+G.debugging_level;
  memory_copy(filename->chars+delta,temp->chars+temp->len2,Str_len(filename)-delta); temp->len2+=Str_len(filename)-delta;
  return true; }

FUNCTION Void process_backup(struct Str *filename) {
  Char buffer[256]; struct Str temp; Any handle;
  struct PliantSignature s;
  struct MemoryPool *pool; Int i; struct MemoryChunk *c; struct MemoryChunk2 *c2;
  struct PliantArea a;
  Arrow *r; struct DelayedAction *da;
  memory_shrink(true);
  if(!build_full_br_name(filename,buffer,&temp)) {
    consolen(Z,"invalid dump file name ",S,filename,EOL);
    return; }
  handle=file_open(&temp,file_out);
  if(handle==-1) {
    consolen(Z,"failed to open dump file ",S,&temp,EOL);
    return; }
  G.execution_phase=phase_shutdown;
  for(r=List_last(G.shutdown_actions); r!=G.null_constant; r=List_previous(r)) {
    check(entry_type(*r)==G.type_DelayedAction);
    da=(struct DelayedAction *)*r;
    ((ShutdownActionPrototype)da->function->exe)(da->parameter); }
  G.execution_phase=phase_backup;
  s.signature=0x738F5EC1;
  s.c_level=c_debugging_level;
  s.release=pliant_release_number;
  #if defined(_DLL_)
    s.expression_compile=Expression_compile;
  #endif
  file_write(handle,(Address)&s,sizeof(s));
  file_write(handle,(Address)&G,sizeof(G));
  for(pool=&G.memory_pool; pool!=null; pool=pool->next)
    for(i=0; i<pool->nb_area; i++) {
      a.address=pool->areas[i].address;
      for(c=Chunk_first(a.address); !Chunk_is_stop(c); c=Chunk_translate(c,Chunk_size(c)))
        #ifdef _MARK_OBJECTS_
         if(!Chunk_is_inuse(c) && !Chunk_is_committed(c)) {
        #else
         if(!Chunk_is_committed(c)) {
        #endif
          c2=(struct MemoryChunk2 *)c;
          a.size=(Int)c2->decomitted_address-(Int)a.address;
          file_write(handle,(Address)&a,sizeof(a));
          file_write(handle,a.address,a.size);
          a.address=address_translate(c2->decomitted_address,c2->decomitted_size); }
      a.size=(Int)address_translate(pool->areas[i].address,pool->areas[i].consumed)-(Int)a.address;
      file_write(handle,(Address)&a,sizeof(a));
      file_write(handle,a.address,a.size); }
  a.address=null,a.size=0;
  file_write(handle,(Address)&a,sizeof(a));
  for(r=List_first(G.backup_actions); r!=G.null_constant; r=List_next(r)) {
    check(entry_type(*r)==G.type_DelayedAction);
    da=(struct DelayedAction *)*r;
    ((BackupActionPrototype)da->function->exe)(da->parameter,handle); }
  file_close(handle);
  G.execution_phase=phase_wakeup;
  for(r=List_first(G.wakeup_actions); r!=G.null_constant; r=List_next(r)) {
    check(entry_type(*r)==G.type_DelayedAction);
    da=(struct DelayedAction *)*r;
    ((WakeupActionPrototype)da->function->exe)(da->parameter); }
  G.execution_phase=phase_run; }


FUNCTION Bool process_restore(Int debugging_level,Char *pliant_path,struct Str *filename) {
  Char buffer[256]; struct Str temp; Any handle;
  struct PliantSignature s;
  struct MemoryPool *pool; Int i;
  struct PliantArea a; Int red;
  Arrow *r; struct DelayedAction *da;
  #if defined(_LINUX_API_) && defined(_CHECK_) && defined(_VERBOSE_)
    write(1,"R1\r",3);
  #endif
  G.debugging_level=debugging_level;
  G.pliant_root_path.chars=pliant_path; G.pliant_root_path.len2=string_len(pliant_path);
  if(!build_full_br_name(filename,buffer,&temp)) return false;
  handle=file_open(&temp,file_in); if(handle==-1) return false;
  file_read(handle,(Address)&s,sizeof(s));
  #if defined(_DLL_)
    if(s.signature!=0x738F5EC1 || s.c_level!=c_debugging_level || s.release!=pliant_release_number || s.expression_compile!=Expression_compile) {
  #else
    if(s.signature!=0x738F5EC1 || s.c_level!=c_debugging_level || s.release!=pliant_release_number) {
  #endif
    file_close(handle);
    consolen(Z,"File ",S,filename,Z," is not compatible with your configuration !\nPlease rebuild it.",EOL);
    // consolen(Z,"File ",S,filename,Z," is not compatible with your configuration !\nPlease rebuild it.",EOL);
    return false; }
  file_read(handle,(Address)&G,sizeof(G));
  G.memory_current_consumed=0;
  G.memory_maximum_consumed=0;
  G.execution_phase=phase_restore;
  for(pool=&G.memory_pool; pool!=null; pool=pool->next)
    for(i=0; i<pool->nb_area; i++) {
      a.address=memory_page_reserve(pool->areas[i].address,pool->areas[i].size);
      if(a.address!=pool->areas[i].address) {
        consolen(Z,"Failed to restore memory from ",S,filename,EOL);
        process_exit(error_id_restore); } }
  for(;;) {
    a.address=null; file_read(handle,(Address)&a,sizeof(a)); if(a.address==null) break;
    memory_page_commit(a.address,a.size);
    red=file_read(handle,a.address,a.size);
    if(red!=a.size) {
      consolen(Z,"File ",S,filename,Z," is corrupted !",EOL);
      file_close(handle);
      handle=file_open(&temp,file_out);
      s.signature=0;
      file_write(handle,(Address)&s,sizeof(s));
      file_close(handle);
      process_exit(error_id_restore); } }
  #if defined(_LINUX_API_) && defined(_CHECK_) && defined(_VERBOSE_)
    write(1,"R2\r",3);
  #endif
  #ifndef _STATIC_
    restore_externals();
  #endif
  for(r=List_first(G.restore_actions); r!=G.null_constant; r=List_next(r)) {
    check(entry_type(*r)==G.type_DelayedAction);
    da=(struct DelayedAction *)*r;
    ((RestoreActionPrototype)da->function->exe)(da->parameter,handle); }
  #if defined(_LINUX_API_) && defined(_CHECK_) && defined(_VERBOSE_)
    write(1,"R3\r",3);
  #endif
  file_close(handle);
  #if defined(_LINUX_API_) && defined(_CHECK_) && defined(_VERBOSE_)
    write(1,"  \r",3);
  #endif
  G.execution_phase=phase_wakeup;
  for(r=List_first(G.wakeup_actions); r!=G.null_constant; r=List_next(r)) {
    check(entry_type(*r)==G.type_DelayedAction);
    da=(struct DelayedAction *)*r;
    ((WakeupActionPrototype)da->function->exe)(da->parameter); }
  G.execution_phase=phase_run;
  return true; }