/* write-barriers.c -
 *
 ****************************************************************
 * Copyright (C) 2000 Tom Lord
 * 
 * See the file "COPYING.PIW" for further information about
 * the copyright and warranty status of this work.
 */


#include "hackerlab/piw-malloc/write-barrier-breakpoints.h"
#include "hackerlab/piw-malloc/watchpt-breakpoints.h"
#include "hackerlab/piw-malloc/write-barriers.h"


/****************************************************************
 *(h0 "Using PIW Malloc With Write Barriers")
 *
 * Some compilers are capable of adding instrumentation to programs
 * automatically.  PIW is distributed with a modified version of the
 * GNU C compiler (`gcc') that has this capability.  This version of
 * the compiler has support for imposing a "write barrier" on C
 * programs it compiles.
 * 
 */ 

/*(menu)
 */

/*(include-documentation "gcc.c")
 */


/*(h1 "Using GCC Write Barriers With PIW")
 * 
 * PIW includes several write barrier functions suitable for use as an
 * implementation of `__user_supplied_write_barrier' (see xref:"GCC
 * Features for Write Barriers").  Those functions are described here.
 * 
 */


#if 0
/*(h2 "Using A Write Barrier and Tag Bits to Verify Writes")
 * 
 * The PIW implementation of `malloc' is able to maintain a bitmap
 * that describes which parts of the heap are allocated regions, and
 * which are part of the implementation's internal data structures.
 * (If your program uses `sbrk', the PIW malloc bitmap will become
 * confused.)  The variable `piw_keep_tag_bits' controls this.  (See
 * xref:"Malloc Heap Tag Bits".)
 * 
 * This bitmap has a relation to the correctness of your program: the
 * majority of your program should write only to regions which are
 * allocated regions.  Only the PIW run-time system should write to
 * the malloc arena's internal data structures.  If your program is
 * observed to write outside of any allocated region, but inside the
 * part of the heap known to malloc, that's a bug.
 *
 * PIW provides an implementation of `__user_supplied_write_barrier'
 * which checks to see whether each write to the heap is to an
 * allocated region.  If it detects a write to an illegal location, a
 * log message is generated and a breakpoint function is called.
 * (See xref:"Using GDB With PIW".)
 * 
 * The definition of this write barrier looks approximately like this:
 *
 insert*/

static __inline__ void * __attribute__((no_write_barrier))
piw_write_checking_barrier (void * addr, int length)
{
  if (   (addr < piw_malloc_heap_start) 
      || (addr >= piw_malloc_heap_end)
      || !piw_malloc_arena_tags)
    {
      /* The write is not even in the malloc arena
       * or tag bits are not being kept.
       */
      return addr;
    }
  else
    {
      int x;	
      int n_bits;
     
      /* The write is in the malloc arena.  Will it touch any
       * region that is not marked writable?
       */
      n_bits = ((length + piw_bytes_per_tag_bit - 1)
		/ piw_bytes_per_tag_bit);
 
      first_bit = (((char *)addr - (char *)piw_tagged_heap_start)
		   / piw_bytes_per_tag_bit);
 
      /* Find the first 0 bit among the tags, if any:
       */
      x = bitset_ffc_range (piw_malloc_arena_tags,
			    first_bit,
			    first_bit + n_bits);
 
      if (x >= 0)
	{	  
	  piw_log_bad_write (addr + x * piw_bytes_per_tag_bit);
	  return piw_write_barrier_breakpoint (addr, length);
	}
      else
	return addr;
    }
}

/*end-insert
 *
 * Note that if an error is detected, `piw_write_barrier' returns the
 * value of `piw_write_barrier_breakpoint'.  By default,
 * `piw_write_barrier_breakpoint' will return `addr', which means the
 * write will proceed (it will go ahead with the incorrect write).  If
 * you are using a debugger, you can cause
 * `piw_write_barrier_breakpoint' to return some other value, thus
 * redirecting the write to a safe location.
 *
 * Also note that the resolution of write checking is determined by
 * the compile-time constant `piw_bytes_per_tag_bit' which is usually
 * `sizeof (long)'.  This resolution limitation implies that a stray
 * write within 3 bytes of a valid location (7 bytes on a 64-bit
 * machine) might not be detected.
 * 
 * Finally, note that for this write barrier to be effective, you must
 * include `piw_keep_tag_bits' in the value of the environment variable
 * `PIWFLAGS' when you run your program.
 */
#endif



/*(include-documentation "write-barrier-breakpoints.c")
 */


#if 0
/*(h2 "Using A Write Barrier to Log All Writes")
 * 
 * PIW provides an implementation of `__user_supplied_write_barrier'
 * which logs each write to the heap.  Use of this barrier is
 * expensive both in time consumed, and in the size of the log.
 * 
 * The definition of this write barrier looks approximately like this:
 *
 insert*/
static __inline__ void * __attribute__((no_write_barrier))
piw_write_logging_barrier (void * addr, int length)
{
  if ((addr < piw_malloc_heap_start) || (addr >= piw_malloc_heap_end))
    {
      /* The write is not even in the malloc arena.
       */
      return addr;
    }
  else
    {
      piw_log_write (addr, length);
      return addr;
    }
}
/*end-insert
 */
#endif

#if 0
/*(h2 "Watchpoints: Using A Write Barrier to Trap Writes to Specific Locations")
 * 
 * PIW provides an implementation of `__user_supplied_write_barrier'
 * which checks each write to the heap to see if it is to one of two
 * specific regions called "watchpoints".  Use of this barrier is
 * expensive in time consumed, but it can be used in combination with
 * a symbolic debugger to find the exact point during the execution of
 * a program when a particular location is being modified.
 * 
 * (The GNU debugger, GDB, provides a built-in watchpoint facility.
 * GDB's watchpoints are fast and efficient on systems that have
 * hardware support for watchpoints, but very slow on systems without
 * hardware support.  PIW watchpoints provide a practical alternative
 * for systems without hardware watchpoint support.)
 * 
 * The definition of the PIW watchpoint write barrier looks
 * approximately like this:
 *
 insert*/
static __inline__ void * __attribute__((no_write_barrier))
piw_write_watchpoint_barrier (void * addr, int length)
{
  if (   ((addr + length) >= piw_watchpt[0].start)
      && (addr < piw_watchpt[0].end))
    return piw_watchpt0_breakpoint (addr, length);
  else if (   ((addr + length) >= piw_watchpt[1].start)
	   && (addr < piw_watchpt[1].end))
    return piw_watchpt1_breakpoint (addr, length);
  else
    return addr;
}
/*end-insert
 * 
 */
/*(menu)
 */
#endif


/*(menu)
 */

#if 0
/****************************************************************
 *(h3 "Setting Watched Locations")
 * 
 * Two variables determine which locations are observed by the PIW
 * watchpoint write-barrier. 
 * 
 insert*/
struct piw_watchpt
{
  void * start;
  void * end;
} piw_watchpt[2];
/*end-insert
 * 
 * These definitions may be convenient if added to your .gdbinit:
 * 
 *   define piw-watch
 *     set piw_watchpt[($arg0)].start = (void *)&($arg1)
 *     set piw_watchpt[($arg0)].end = \
 *	  (void *)((char *)(&($arg1)) + sizeof ($arg1)
 *   end
 * 
 *   define delete-piw-watch
 *     set piw_watchpt[($arg0)].start = 0
 *     set piw_watchpt[($arg0)].end = 0
 *   end
 * 
 *   define show-piw-watch
 *     echo "piw_watchpt[0] = "
 *     print piw_watchpt[0]
 *     echo "piw_watchpt[1] = "
 *     print piw_watchpt[1]
 *   end
 */
#endif
struct piw_watchpt piw_watchpt[2];

/*(include-documentation "watchpt-breakpoints.c")
 */


#if 0
/*(h2 "Installing an Inline PIW Write Barrier in Your Programs")
 * 
 * To use a PIW write barrier as an inline function, you must compile
 * at least part of your program with write barriers, using either the
 * `write_barrier' function attribute or the `write-barriers' compiler
 * flag (see xref:"GCC Features for Write Barriers").  That part of
 * your program should be compiled in the scope of the definition of
 * one of the PIW write barrier functions, and in the scope of a
 * definition for `__user_supplied_write_barrier', such as:
 * 
 insert*/

/* Check for illegal writes to the heap.
 */
static __inline__ void * __attribute__((no_write_barrier))
__user_supplied_write_barrier (void * addr, int length)
{
  return piw_write_barrier (addr, length);
}

/*end-insert
 * 
 * or
 * 
 insert*/

/* Log writes to the heap and Check for illegal writes.
 */
static __inline__ void * __attribute__((no_write_barrier))
__user_supplied_write_barrier (void * addr, int length)
{
  piw_logging_write_barrier (addr, length);
  return piw_write_barrier (addr, length);
}
 
/*end-insert
 *
 * This is most easily accomplished by including the PIW header file
 * {<hackerlab/piw-malloc/piw-write-barriers.h>}, with suitable
 * pre-processor symbols defined:
 * 
 insert*/

/* To check writes and log illegal writes to the heap,
 * define PIW_CHECK_WRITES:
 */
#define PIW_CHECK_WRITES

/* To log all writes to the heap, define PIW_LOG_WRITES:
 *
 * #define PIW_LOG_WRITES
 */


/* To include PIW watchpoint support, define PIW_WATCHPOINTS:
 *
 * #define PIW_WATCHPOINTS
 */

#include "hackerlab/piw-malloc/piw-write-barriers.h"
 
/*end-insert
 *
 * It is also possible to use one of the PIW barrier functions in your
 * own definition of `__user_supplied_write_barrier'.  
 *
 * `piw-write-barriers.h' provides definitions for
 * `piw_write_checking_barrier', `piw_write_logging_barrier', and
 * `piw_write_watchpoint_barrier'.  To cause its definition of
 * `__user_supplied_write_barrier' to be omitted, use:
 * 
 insert*/

#define PIW_NO_UDWB		/* no user defined write barrier */

/*end-insert
 * 
 * before including "hackerlab/piw-malloc/piw-write-barriers.h".
 */
#endif


/*(h2 "Installing an Function Call PIW Write Barrier in Your Programs")
 *
 * 
 * To use a PIW write barrier as a normal function (rather than an
 * inline function), you must compile at least part of your program
 * with write barriers, using either the `write_barrier' function
 * attribute or the `write-barriers' compiler flag (see xref:"GCC
 * Features for Write Barriers").  PIW provides a default implementation
 * of `__user_supplied_write_barrier' which combines the functions
 * `piw_write_checking_barrier' and `piw_write_watchpoint_barrier'.
 * 
 *
 */

