#!/bin/bash
#first ensure /tmp/agent/ exists
#nohup /work/git-repo/profiler/cloud-profiler-host-agent/agent/bin/hostagent_x86 \
#--cafConfigFilePath=/work/git-repo/profiler/cloud-profiler-java-agent/cnp-native-agent/src/main/scripts/cafConfig.toml \
#--flowsConfigFilePath=/work/git-repo/profiler/cloud-profiler-java-agent/cnp-native-agent/src/main/scripts/agentFlows.json >/dev/null 2>&1 &

TIME=$( date "+%H%M%S" )
DATE=$( date "+%Y%m%d" )
RAND=$( date +%s )

CONSOLE_LOG_FILE=/tmp/app_${DATE}_${TIME}.log

printerr() { echo  "== > ERROR [${DATE}_${TIME}]: $*" 1>&2; }
printinfo() { echo "== > INFO [${DATE}_${TIME}]: $*"; }

help_and_exit()
{
echo "#-------------------------------------------------------------------------"
echo "Usage :"
echo "# --command/-c  command to run e.g \"-jar cnp_agent_test_sandbox.jar --idle\""
echo "# --attach/-a <PID> attach to PID"
echo "# --restart/-r <PID> cause the process to make restart , while keeping same PID"
echo "# --inject/-i <PID> inject the profiler to already runnig process , by causing it to restart with agent enabled"
echo "# --extract/-e <PID> revert the process back to original state after agent injection , --inject had to be performed , otherwise noop"
echo "# --jopt/-j  <JAVA_OPT> java propertiy e.g \"-Xmx2048m -Xms512m\""
echo "#[--work_folder/-w <PATH>]  temporary work folder"
echo "#[--build_folder/-b <PATH>]  libcnp-native-agent.so location folder"
echo "#[--mars_folder/-m <PATH>] pinpoint-bootstrap-1.8.1-SNAPSHOT.jar location folder"
echo "#[--with_apm/-x assume APM integration , no hostagent available , no pipes"
echo "#[-v] enable valgrid"
echo "#-------------------------------------------------------------------------"

exit 1;
}

function init()
{
##################################
# Convert long parameter to short
##################################
for arg in "$@"; do
  shift
  case "$arg" in
    "--work_folder")  set -- "$@" "-w" ;; 
    "--build_folder") set -- "$@" "-b" ;;
    "--mars_folder")  set -- "$@" "-m" ;;
    "--command")      set -- "$@" "-c" ;;
    "--attach")       set -- "$@" "-a" ;;
    "--restart")      set -- "$@" "-r" ;;
    "--inject")       set -- "$@" "-i" ;;
    "--extract")      set -- "$@" "-e" ;;
    "--jopt")         set -- "$@" "-j" ;;
    "--with_apm")     set -- "$@" "-x" ;;
    "--help")         set -- "$@" "-h" ;;
    *)                set -- "$@" "$arg" ;;
  esac
done


#################################
#  Parse parameters
#################################
BUILD_DIR=/work/git-repo/profiler/cloud-profiler-java-agent/cnp-native-agent/target/deploy/x86
MARS_BUILD_DIR=/work/git-repo/profiler/cloud-profiler-java-agent/cnp-java-agent-dist/target/profiler-agents/java/mars
WORK_DIR=/tmp/agent/
export CNP_PROFILING_MODES=47
while getopts "vw:hb:m:c:j:a:r:i:e:x" opt
do
  case $opt in
      a)
        ATTACH=true
        PID_TO_ATTACH=${OPTARG}
      ;;
      r)
        RESTART=true
        PID_TO_ATTACH=${OPTARG}
      ;;
      i)
        INJECT=true
        PID_TO_ATTACH=${OPTARG}
      ;;
      e)
        EXTRACT=true
        PID_TO_ATTACH=${OPTARG}
      ;;
      j)
        JAVA_OPT="${OPTARG} ${JAVA_OPT}"
      ;;

      b)  
        BUILD_DIR="${OPTARG}"
      ;;
    
      m)  
        MARS_BUILD_DIR="${OPTARG}"
        INJECT_MARS=1
        export CNP_PROFILING_MODES=63
      ;;
      
      c) 
        COMMAND="${OPTARG}"
        printinfo "Command is [${COMMAND}]"
      ;;

      w)  
        WORK_DIR="${OPTARG}"
      ;;
      
      v) export VALGRIND_CMD="valgrind --leak-check=full --error-limit=no --track-origins=yes --show-mismatched-frees=no --log-file=/tmp/valgrind-out-${DATE}_${TIME}.log"
         export MALLOC_CHECK_=1
      ;;

      x)
         WITH_APM=1
         printinfo "Command is [${COMMAND}] running in APM integration mode"
        ;;
      
      h)  help_and_exit
      ;;
      
      \?)  
          printerr "unkown input prameter provided , check usage"
          help_and_exit
      ;;
  esac
done  

##########################################################
#  all mandatory parameters provided verification section
##########################################################
if [ -n "${COMMAND}" ] && [ -n "${PID_TO_ATTACH}" ]
then 
    printerr "No command neither PID are provided"
    help_and_exit
fi

mkdir -p ${WORK_DIR}/workdir

ulimit -c unlimited

export LD_LIBRARY_PATH=$BUILD_DIR:$LD_LIBRARY_PATH
}

open_pipes()
{ 
  interface_pipes=(
  "${WORK_DIR}/in/outTestPipe"
  "${WORK_DIR}/out/testPipe"
  "${WORK_DIR}/ctrlin/testPipe"
  "${WORK_DIR}/ctrlin/testPipe"
  )
    
  for _p in "${interface_pipes[@]}";do
    #if not exists
    if [[ ! -p $_p ]]
    then
      printinfo "Create $_p"
      mkdir -p "$( dirname "$_p" )"
      mkfifo "$_p"
    fi
  done
}

function set_java_version()
{
  JAVA_VERSION=`java -version 2>&1| grep  version | cut -f3 -d" "`
  
   if [[ $JAVA_VERSION =~ "1.8" ]]
   then 
     JAVA_VERSION=8
   fi
   
   if [[ $JAVA_VERSION =~ "11." ]]
   then 
     JAVA_VERSION=11
   fi
   
   if [[ $JAVA_VERSION =~ "17." ]]
      then 
        JAVA_VERSION=17
      fi
   
   printinfo "JAVA_VERSION ${JAVA_VERSION}"
}

function set_java_opt()
{
  #JAVA_OPT="-XX:+UseG1GC $JAVA_OPT"
 # JAVA_OPT="-Xmx2048m -Xms512m $JAVA_OPT"
 # JAVA_OPT="-XX:+PrintGCDetails $JAVA_OPT"
 if [[ $JAVA_VERSION == 8 ]]
 then
  JAVA_OPT="-Xms32m -Xmx256m -Xss256k $JAVA_OPT"
  JAVA_OPT="-XX:MaxDirectMemorySize=64m $JAVA_OPT"
  JAVA_OPT="-XX:MaxInlineLevel=1 $JAVA_OPT"
  JAVA_OPT="-XX:InlineSmallCode=25 $JAVA_OPT"
  JAVA_OPT="-XX:MaxInlineSize=2 $JAVA_OPT"
  JAVA_OPT="-XX:InitialCodeCacheSize=48m $JAVA_OPT"
  JAVA_OPT="-XX:ReservedCodeCacheSize=48m $JAVA_OPT"
  JAVA_OPT="-XX:MetaspaceSize=32m $JAVA_OPT"
  JAVA_OPT="-XX:MaxMetaspaceSize=128m $JAVA_OPT"
  JAVA_OPT="-XX:NativeMemoryTracking=detail $JAVA_OPT"
  JAVA_OPT="-XX:-TieredCompilation $JAVA_OPT"
  JAVA_OPT="-XX:-UseCodeCacheFlushing $JAVA_OPT"
  JAVA_OPT="-XX:+G1Uncommit $JAVA_OPT"
  JAVA_OPT="-XX:+UseG1GC $JAVA_OPT"
  JAVA_OPT="-XX:MinHeapFreeRatio=20 $JAVA_OPT"
  JAVA_OPT="-XX:MaxHeapFreeRatio=30 $JAVA_OPT"
  JAVA_OPT="-XX:ParallelGCThreads=2 $JAVA_OPT"
  JAVA_OPT="-XX:ConcGCThreads=2 $JAVA_OPT"
  JAVA_OPT="-XX:G1PeriodicGCLoadThreshold=20 $JAVA_OPT"
  JAVA_OPT="-XX:G1PeriodicGCInterval=5000 $JAVA_OPT"
  JAVA_OPT="-XX:-G1EagerReclaimHumongousObjects $JAVA_OPT"
  JAVA_OPT="-XX:-G1UncommitThreadPriority $JAVA_OPT"
  JAVA_OPT="-XX:G1UncommitPercent=0.5 $JAVA_OPT"
  JAVA_OPT="-XX:+UseFastAccessorMethods $JAVA_OPT"
  JAVA_OPT="-XX:-UseBiasedLocking $JAVA_OPT"
  JAVA_OPT="-XX:-UseLargePages $JAVA_OPT"
  JAVA_OPT="-XX:-InlineSynchronizedMethods $JAVA_OPT"
  JAVA_OPT="-XX:CompileCommand=exclude,org.springframework.core.ResolvableType::forMethodParameter $JAVA_OPT"
  JAVA_OPT="-XX:+CrashOnOutOfMemoryError $JAVA_OPT"
  JAVA_OPT="-XX:+HeapDumpOnOutOfMemoryError $JAVA_OPT"
  JAVA_OPT="-XX:HeapDumpPath=/tmp/heapdump.hprof $JAVA_OPT"
  JAVA_OPT="-XX:+UnlockExperimentalVMOptions $JAVA_OPT"
fi
  #JAVA_OPT="-XX:OnOutOfMemoryError='rm /tmp/heapdump_[0-9]*.hprof 2>/dev/null; mv /tmp/heapdump.hprof /tmp/heapdump_%!p(MISSING).hprof' $JAVA_OPT"
  #JAVA_OPT="-XX:+AlwaysPreTouch $JAVA_OPT"
  #JAVA_OPT="-XX:+PrintClassHistogram $JAVA_OPT"
  #JAVA_OPT="-XX:TraceJVMTI=ec+ $JAVA_OPT"  
  #JAVA_OPT="-XX:+UnlockDiagnosticVMOptions -XX:+DebugNonSafepoints $JAVA_OPT"
}

function send_start_session()
{
  export start_command_pipe="${WORK_DIR}/out/testPipe"
  
  perl <<- '__RAW__'
  $context = "test";
  $dummy_protobuff = "t am protobuff";
  $header_size = 4 + length($context) + 4 + 8 ;
  $foo = pack('L>L>L>L>L>a4Q>L>a14',-1,$header_size,1,1,length($context),$context,123456,length($dummy_protobuff),$dummy_protobuff);
  local $handle = undef;
  print ">>>>> GOING TO SEND START COMMAND <<<<<<\n";
  open ( $handle , "> $ENV{start_command_pipe}") or die "Failed to open pipe $ENV{start_command_pipe} $!";
  
  print $handle $foo;
  close $handle;
  print ">>>>> START COMMAND SENT <<<<<<\n";
__RAW__
}

function run_profiler()
{ 
  set_java_version
  set_java_opt
   
  export JAVA_TOOL_OPTIONS=-agentpath:${BUILD_DIR}/libcnp-native-agent.so=\
input=${WORK_DIR}/out/testPipe,\
out=${WORK_DIR}/in/outTestPipe,\
debug=true,\
work_folder=${WORK_DIR}/workdir/,\
Pid=${RAND}
  
  if [[ -v INJECT_MARS ]]
  then
     JAVA_TOOL_OPTIONS="-javaagent:${MARS_BUILD_DIR}/pinpoint-bootstrap-1.8.1-SNAPSHOT.jar -Dmars_output=${WORK_DIR}/workdir ${JAVA_TOOL_OPTIONS}"
  fi

  if [[ -v WITH_APM ]]
  then
    JAVA_TOOL_OPTIONS=-agentpath:${BUILD_DIR}/libcnp-native-agent.so
  fi
  
  COMMAND_TO_RUN="java ${JAVA_OPT} $1"
  printinfo "Execute [${COMMAND_TO_RUN}]"
  printinfo "Log file [${CONSOLE_LOG_FILE}]"
  
  exec 3<>${WORK_DIR}/out/testPipe
  exec 4<>${WORK_DIR}/in/outTestPipe
    
  ${VALGRIND_CMD} ${COMMAND_TO_RUN} > "${CONSOLE_LOG_FILE}" 2>&1 & pid=$! ; tail -f "${CONSOLE_LOG_FILE}" &
  
  return 0
}

function attach_profiler()
{
  printinfo "Attaching to " $1
  pid=$1
  exec 3<>${WORK_DIR}/out/testPipe
  exec 4<>${WORK_DIR}/in/outTestPipe
  ${BUILD_DIR}/cnp --attach $pid --opt "input=${WORK_DIR}/out/testPipe,out=${WORK_DIR}/in/outTestPipe" --work_folder "${WORK_DIR}/workdir/" 
  
  return $?
}

function restart()
{
  printinfo "Going to restart " $1
  pid=$1
  
  ${BUILD_DIR}/cnp --restart $pid --work_folder "${WORK_DIR}/workdir/"
  
  return $?
}

function inject_profiler()
{
  printinfo "Going to inject profiler in to " $1
  pid=$1
  
  if [[ -v INJECT_MARS ]]
  then
    ${BUILD_DIR}/cnp --inject $pid --opt "input=${WORK_DIR}/out/testPipe,out=${WORK_DIR}/in/outTestPipe" --work_folder "${WORK_DIR}/workdir/"  --with-mars
  else
    ${BUILD_DIR}/cnp --inject $pid --opt "input=${WORK_DIR}/out/testPipe,out=${WORK_DIR}/in/outTestPipe" --work_folder "${WORK_DIR}/workdir/"
  fi
     
  return $?
}

function extract_profiler()
{
  printinfo "Going to extract profiler from " $1
    pid=$1
    
    ${BUILD_DIR}/cnp --extract $pid --work_folder "${WORK_DIR}/workdir/"
    
    return $?
}

#================================#
#                                #
#            M A I N             #
#                                #
#================================#
printinfo "Running with $JAVA_HOME"

init "$@"

open_pipes

if [[ -n ${COMMAND} ]]
then
  run_profiler "${COMMAND}"
  if [[ $? == 0 ]]
  then
    echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    echo Application has started PI $pid
    echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  else
     printerr "Failed to inject to " ${COMMAND} 
     exit 1
  fi
elif [[ -n ${ATTACH} ]]
then
  attach_profiler ${PID_TO_ATTACH}
  if [[ $? == 0 ]]
  then
    echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    echo Attached to PID $pid
    echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
  else
    printerr "Failed to attach to " ${PID_TO_ATTACH}
    exit 1
  fi
elif [[ -n ${RESTART} ]]
then
    restart ${PID_TO_ATTACH}
    if [[ $? == 0 ]]
    then
       echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
       echo Restarted process PID $pid
       echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    fi 
    exit 0;
elif [[ -n ${INJECT} ]]
then
    inject_profiler ${PID_TO_ATTACH}
    if [[ $? == 0 ]]
     then
       echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
       echo Injected profiler in to $pid
       echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
      else
           printerr "Failed to inject in to " ${PID_TO_ATTACH}
           exit 1
     fi
elif [[ -n ${EXTRACT} ]]
then
     extract_profiler ${PID_TO_ATTACH}
     if [[ $? == 0 ]]
     then
        echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
        echo Extract profiler from $pid
        echo !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
     fi 
     exit 0;
fi


kill -0 $pid > /dev/null 2>&1
pid_exist=$?

if [[ $pid_exist == 0 ]]
then
  sleep 1
  send_start_session
else
  exit 1
fi

function _cntrc(){ kill -9 $pid && kill -- -$$;exit 0;}
trap _cntrc SIGINT

#read the pipe so agent can write to it continiusly
while true;do
  kill -0 $pid > /dev/null 2>&1
  pid_exist=$?
  if [[ $pid_exist == 0 ]]
  then
      read -t 5
  else
    exit 0
  fi
done <"${WORK_DIR}/in/outTestPipe"
