/* AE program profiling system.
   Startup and termination code and routines to manipulate AE buffer.
   Copyright (C) 1989, 1990 by James R. Larus (larus@cs.wisc.edu)

   This file must be compiled by GCC!

   AE and AEC are 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 1, or (at your option) any
   later version.

   AE and AEC are 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 GNU CC; see the file COPYING.  If not, write to James R.
   Larus, Computer Sciences Department, University of Wisconsin--Madison,
   1210 West Dayton Street, Madison, WI 53706, USA or to the Free
   Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */

/* $Header: /var/home/larus/AE/AE/RCS/ae-buffer.c,v 2.0 90/02/09 17:18:53 larus Exp Locker: larus $ */


#include <sys/file.h>
#include <sys/time.h>
#include <sys/resource.h>
#include "ae.h"
#include "ae-machine.h"


/* Save pointer to beginning of AE buffer. */

char *ae_buffer_base;


/* Register or variable pointing to next free byte in AE buffer. */

#ifdef AE_BUFFER_REG
register char *ae_bp asm (AE_BUFFER_REG);
#elif AE_BUFFER_VAR
char *ae_bp;
#endif


/* Register or variable pointing to end of AE buffer. */

#ifdef AE_BUFFER_BOUND_REG
register char *ae_bound asm(AE_BUFFER_BOUND_REG);
#elif AE_BUFFER_BOUND_VAR
char *ae_bound;
#endif


/* File descriptor for writing AE buffer. */

int ae_fd;


/* Stack pointer upon entry to AE_INITIALIZE. */

static char *old_sp;



/* Initialize by allocating AE buffer on the stack and opening the
   output file. Invoked by AE_START (which is written in assembly
   language). Return the new stack pointer. */

char *
ae_initialize (argc, argv)	/* Define main's parameters for compiler */
     int argc;
     char *argv[];
{
  register char *sp asm (SP_REG);
  struct rlimit rl;

  /* Raise stacksize limit. */
  getrlimit (RLIMIT_STACK, &rl);
  rl.rlim_cur = rl.rlim_max;
  setrlimit (RLIMIT_STACK, &rl);

  ae_fd = open ("ae.out", O_WRONLY | O_CREAT | O_TRUNC, 0644);

  /* Make space on stack for AE buffer */
#ifdef AE_BUFFER_STACK_OFFSET
  ae_buffer_base =
    (char *) (STACK_TOP - (AE_BUFFER_SIZE + AE_BUFFER_STACK_OFFSET));
#else
  ae_buffer_base = (char *) (sp - AE_BUFFER_SIZE);
#endif

  ae_bp = ae_buffer_base;
#if (defined (AE_BUFFER_BOUND_REG) || defined(AE_BUFFER_BOUND_VAR))
  ae_bound = ae_bp + AE_BUFFER_SIZE - CHUNK_SIZE;
#endif

  old_sp = sp;
  *ae_bp = 0;			/* Force OS to extend stack */
  return (sp = (ae_buffer_base - AE_START_FRAME_SIZE));
}


/* Flush AE buffer by writing its contents to a file and reset the
   buffer pointer.  This routine probably must be written in assembly
   language since it cannot affect ANY register visible to its caller
   (not just the callee-saved registers) since it is called at arbitrary
   points in a function. */

#ifdef USE_C_FLUSH_BUFFER
void
ae_flush_buffer ()
{
  char *x[32];			/* Space to store registers */

  x[0] = ae_bp;			/* Example */
  write (ae_fd, ae_buffer_base, (int) ae_bp - (int) ae_buffer_base);
  ae_bp = ae_buffer_base;
#ifdef (defined (AE_BUFFER_BOUND_REG) || defined (AE_BUFFER_BOUND_VAR))
  ae_bound = ae_bp + AE_BUFFER_SIZE - 4;
#endif
}
#endif


/* Clean up at program termination by flushing the remaining events. */

void
ae_terminate ()
{
  register char *sp asm (SP_REG);

  ae_flush_buffer ();
  close (ae_fd);
  /* Restore the stack pointer. */
  sp = old_sp;
}


/* Redefine the EXIT routine to call AE_TERMINATE. */

exit (code)
     int code;
{
  ae_terminate ();
  _cleanup ();
  _exit (code);
}
