#!/bin/bash
#
# memtest.sh
#
# Shell script to help isolate memory failures under linux
#
# Author: Doug Ledford  + contributors
#
# (C) Copyright 2000-2002 Doug Ledford; Red Hat, Inc.
# This shell script is released under the terms of the GNU General
# Public License Version 2, June 1991.  If you do not have a copy
# of the GNU General Public License Version 2, then one may be
# retrieved from http://people.redhat.com/dledford/GPL.html
#
# Note, this needs bash2 for the wait command support.

# This is where we will run the tests at
[ -z "$TEST_DIR" ] && TEST_DIR=/tmp

# The location of the linux kernel source file we will be using
[ -z "$SOURCE_FILE" ] && SOURCE_FILE=$TEST_DIR/linux.tar.gz

if [ ! -f "$SOURCE_FILE" ]; then
  wget http://www.kernel.org/pub/linux/kernel/v2.6/linux-2.6.11.12.tar.gz -O $SOURCE_FILE
  if [ $? -ne 0 ]; then
    echo "Missing source file $SOURCE_FILE"
    exit 1
  fi
fi

# How many passes to run of this test, higher numbers are better
[ -z "$NR_PASSES" ] && NR_PASSES=20

# Guess how many megs the unpacked archive is
[ -z "$MEG_PER_COPY" ] && MEG_PER_COPY=$(ls -l $SOURCE_FILE | awk '{print int($5/1024/1024) * 4}')

# How many trees do we have to unpack in order to make our trees be larger
# than physical RAM?  If we don't unpack more data than memory can hold
# before we start to run the diff program on the trees then we won't
# actually flush the data to disk and force the system to reread the data
# from disk.  Instead, the system will do everything in RAM.  That doesn't
# work (as far as the memory test is concerned).  It's the simultaneous
# unpacking of data in memory and the read/writes to hard disk via DMA that
# breaks the memory subsystem in most cases.  Doing everything in RAM without
# causing disk I/O will pass bad memory far more often than when you add
# in the disk I/O.
[ -z "$NR_SIMULTANEOUS" ] && NR_SIMULTANEOUS=$(free | awk -v meg_per_copy=$MEG_PER_COPY 'NR == 2 {print int($2*1.5/1024/meg_per_copy + (($2/1024)%meg_per_copy >= (meg_per_copy/2)) + (($2/1024/32) < 1))}')

# Should we unpack/diff the $NR_SIMULTANEOUS trees in series or in parallel?
if [ ! -z "$PARALLEL" ]; then
  PARALLEL="yes"
else
  PARALLEL="no"
fi

if [ ! -z "$JUST_INFO" ]; then
  echo "TEST_DIR:               $TEST_DIR"
  echo "SOURCE_FILE:            $SOURCE_FILE"
  echo "NR_PASSES:              $NR_PASSES"
  echo "MEG_PER_COPY:           $MEG_PER_COPY"
  echo "NR_SIMULTANEOUS:        $NR_SIMULTANEOUS"
  echo "PARALLEL:               $PARALLEL"
  echo
  exit
fi

cd $TEST_DIR

# Remove any possible left over directories from a cancelled previous run
rm -fr linux linux.orig linux.pass.*

# Unpack the one copy of the source tree that we will be comparing against
tar -xzf $SOURCE_FILE
mv linux linux.orig

i=0
while [ "$i" -lt "$NR_PASSES" ]; do
  j=0
  while [ "$j" -lt "$NR_SIMULTANEOUS" ]; do
    if [ $PARALLEL = "yes" ]; then
      (mkdir $j; tar -xzf $SOURCE_FILE -C $j; mv $j/linux linux.pass.$j; rmdir $j) &
    else
      tar -xzf $SOURCE_FILE
      mv linux linux.pass.$j
    fi
    j=$(($j + 1))
  done
  wait
  j=0
  while [ "$j" -lt "$NR_SIMULTANEOUS" ]; do
    if [ $PARALLEL = "yes" ]; then
      (diff -U 3 -rN linux.orig linux.pass.$j; rm -fr linux.pass.$j) &
    else
      diff -U 3 -rN linux.orig linux.pass.$j
      rm -fr linux.pass.$j
    fi
    j=$(($j + 1))
  done
  wait
  i=$(($i + 1))
done

# Clean up after ourselves
rm -fr linux linux.orig linux.pass.*

