#!/bin/sh
#
# Copyright (c) 2011, Couchbase, Inc.
# All rights reserved
#
root="/opt/couchbase"

usage()
{
  cat <<EOF

Usage $0 [-r root] [-f reportfile] corefile
   -r root       Search for the binary in root
   -f reportfile Use the specified file for output.

EOF
  exit 1
}

set -- `getopt r:f:h $*`
if [ $? != 0 ]
then
   usage
fi

for i in $*
do
   case $i in
   -r)  root=$2
        shift 2
        ;;
   -f)  report=$2
        shift 2
        ;;
   -h)  usage
        ;;
    --) shift
        break
        ;;
   esac
done

corefile=$1
if [ "x${corefile}" = "x" ]
then
   usage
fi

if [ -z "${report}" ]
then
   report=${corefile}.log
fi

if ! [ -r ${corefile} ]
then
   echo "Can't read ${corefile}"
   exit 1
fi

touch ${report} 2>/dev/zero
if [ $? -ne 0 ]
then
   echo "Can't write to ${report}"
   exit 1
fi

digest -l > /dev/null 2>&1
if [ $? -eq 0 ]
then
   digest="digest -a md5"
else
   digest="md5sum"
fi

ls "${root}" >/dev/null 2>&1
if [ $? -ne 0 ]
then
   echo "Couchbase root directory (${root}) doesn't exist"
   exit 1
fi

cmdfile=/tmp/gdb-commands.$$
cat > $cmdfile <<EOF
info auxv
EOF
binary=`gdb --core "${corefile}" --batch -x $cmdfile | grep AT_EXECFN | cut -f2 -d\"`
rm $cmdfile
if [ "x${binary}" = "x" ]
then
   binary_name=`file ${corefile} | cut -f 2 -d \'`
   binary=`(cd ${root} && find . -type f -name "${binary_name}" | head -n1)`
   if [ "x${binary}" = "x" ]
   then
       echo "Failed to determine the binary name from the corefile"
       exit 1
   fi
fi

if [ `echo "${binary}" | cut -c1` = "/" ]
  executable=`ls "${root}/${binary}" 2>/dev/null`
fi

if [ "x${executable}" = "x" ]
then
   echo "Failed to locate the binary for the corefile."
   echo "Your tools might be broken. Consider sending core dump to support@couchbase.com."
   exit 1
fi

cat > ${report}  <<EOF
Basic crash dump analysis of ${corefile}.

Please send the file to support@couchbase.com

--------------------------------------------------------------------------------
File information:
EOF

dump_file() {
   ls -l $1 >> ${report}
   ${digest} $1 >> ${report}
   strings $1 | egrep '[0-9]+\.[0-9]+\.[0-9]+_[0-9]+_........' >> ${report}
}

#######################################################################
# Function to get information about the binaries by using gdb...
#######################################################################
analyze_gdb() {
   if [ -z "${root}" ]
   then
      root=`dirname "${corefile}"`
   fi

   ls "${root}" >/dev/null 2>&1
   if [ $? -ne 0 ]
   then
      echo "Couchbase root directory (${root}) doesn't exist"
      exit 1
   fi

   cmdfile=/tmp/gdb-commands.$$
   cat > $cmdfile <<EOF
info auxv
EOF
   binary=`gdb --core "${corefile}" --batch -x $cmdfile | grep AT_EXECFN | cut -f2 -d\"`
   rm $cmdfile
   if [ "x${binary}" = "x" ]
   then
      binary_name=`file ${corefile} | cut -f 2 -d \'`
      binary=`(cd ${root} && find . -type f -name "${binary_name}" | head -n1)`
      if [ "x${binary}" = "x" ]
      then
         echo "Failed to determine the binary name from the corefile"
         exit 1
      fi
   fi

   if [ `echo "${binary}" | cut -c1` = "/" ]
   then
      executable="${binary}"
   else
      executable=`ls "${root}/${binary}" 2>/dev/null`
   fi

   if [ "x${executable}" = "x" ]
   then
      echo "Failed to locate the binary for the corefile."
      echo "Your tools might be broken. Consider sending core dump to support@couchbase.com."
      exit 1
   fi

   dump_file ${executable}
   dump_file ${corefile}

   cat >> ${report} <<EOF
--------------------------------------------------------------------------------
Core file callstacks:
EOF

   cmdfile=/tmp/gdb-commands.$$
   cat > $cmdfile <<EOF
thread apply all bt
quit
EOF
   gdb --command=$cmdfile ${executable} ${corefile} >> ${report} 2>&1
   rm $cmdfile

   cat >> ${report} <<EOF
--------------------------------------------------------------------------------
Module information:
EOF
   mlines=`grep "^Loaded symbols for " ${report}`
   for module in $mlines
   do
      if [ "$module" != "Loaded" -a "$module" != "symbols" -a "$module" != "for" ]
      then
         echo ${module}:
         (cd "${root}" && dump_file ${module})
      fi
   done >> ${report}
}

#######################################################################
# Function to get information about the binaries used from a Solaris
# system
#######################################################################
analyze_sunos() {
executable=`pldd ${corefile} | head -1 | tr -d '[\t]' | cut -f 2 -d: | cut -f 1 -d \ `
   dump_file ${executable}
   dump_file ${corefile}
   cat >> ${report} <<EOF
--------------------------------------------------------------------------------
Core file callstacks:
EOF
   pstack ${corefile} >> ${report} 2>&1
   cat >> ${report} <<EOF
--------------------------------------------------------------------------------
Module information:
EOF
   mlines=`pldd ${corefile} | grep -v "^core "`
   for module in $mlines
   do
      dump_file ${module}
   done >> ${report}

   cat >> ${report} <<EOF
--------------------------------------------------------------------------------
Potential memory leaks:
EOF
   echo ::findleaks -v -d | mdb ${corefile} >> ${report} 2>&1
}

case `uname -s` in
  SunOS)
    analyze_sunos
    ;;
  *)
    analyze_gdb
    ;;
esac

cat <<EOF

${report} contains initial debug information to try to identify
the problem. The log may not contain all information we may need,
so it is recommended that you preserve the corefile and the binary
for a more deep analysis. If the program use any shared libraries
(such as memcached engines) you should also preserve them.

Please send ${report} to support@couchbase.com

EOF
