#!/usr/bin/env bash

store_interval=600
ps_grep="memory-viewer|iotjs|speech-service|flora-dispatcher\
|bluetooth_service|pulseaudio|dbus-daemon|homebase\
|logread|adbd|bsa_server|net_manager|logd|wifi_monitor|wpa_supplicant\
|turenproc"
device_sn='all'
adb_mode="false"
flush_count=100

help="
Usage:
  -m capture memory usage
  -c capture cpu usage
  -a specific collected through adb shell
  -i [interval] memory snapshot interval
              default value is ${store_interval} seconds
  -f [format] a regex expression used for \`ps aux | grep -E \$fromat\`
              default value is $ps_grep
  -d [sn] specific a sn to connect in adb mode, this param will be ignored if not in adbmode
              data for all devices will be collected by default
  -s [flush_count] flush the data to a file after collected n times,
              default value is 100
  -r [render_data_path] specific a data path to render it to a html
Example:
  $ memory-viewer -i ${store_interval} -f \"$ps_grep\"

This tool is used for collect memory data over a time and present the data in a line chart
Enter ^C to stop collect data and generatte line chart
"

while [ $# -gt 0 ]; do
  case "$1" in
    -m)
      action="memory"
      ;;
    -c)
      action="cpu"
      ;;
    -i)
      store_interval="$2"
      shift
      ;;
    -f)
      ps_grep="$2"
      shift
      ;;
    -a)
      adb_mode="true"
      ;;
    -d)
      device_sn=$2
      shift
      ;;
    -r)
      set -e
      data_path=$2
      store_path="$data_path.html"
      mem_data=`cat $data_path`
      echo_templatte='<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,height=device-height"><title>memory view</title><style>::-webkit-scrollbar{display:none;}html,body{overflow:hidden;height:100%;margin:0;}</style></head><body><div id="mountNode"></div><script>document.body.clientHeight;</script><script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g2-3.2.8/dist/g2.min.js"></script><script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.data-set-0.9.6/dist/data-set.min.js"></script><script>  var data = ${data};  var chart = new G2.Chart({    container: "mountNode",    forceFit: true,    height: window.innerHeight  });  chart.source(data, {    month: {      range: [0, 1]    }  });  chart.tooltip({    crosshairs: {      type: "line"    }  });  chart.axis("data", {    label: {      formatter: function formatter(val) {        return val;      }    }  });  chart.line().position("time*data").color("args");  chart.point().position("time*data").color("args").size(4).shape("circle").style({    stroke: "#fff",    lineWidth: 1  });  chart.render();</script></body></html>'
      echo ${echo_templatte/\$\{data\}/\[$mem_data\]} > $store_path
      rm $data_path
      echo "store path: $store_path"
      exit
      ;;
    -s)
      flush_count=$2
      shift
      ;;
    --help)
      printf "$help"
      exit
      ;;
    -h)
      printf "$help"
      exit
      ;;
    --*)
      echo "Illegal option $1"
      exit
      ;;
  esac
  shift $(( $# > 0 ? 1 : 0 ))
done

if [ ! action ]; then
  echo "you should specific what to view, -m or -c, -h to get more"
  exit
fi

hostname=`uname -n`
if [ hostname = "OpenWrt" ]; then
  echo 'mount / with rw'
  mount -o remount,rw /
fi

ps_cmd="ps -eo pid,rss,args | grep -E '$ps_grep' | grep -v grep"
avaliable_cmd="cat /proc/meminfo | grep MemAvailable | awk '{print \$2}'"
free_cmd="cat /proc/meminfo | grep MemFree | awk '{print \$2}'"
buffers_cmd="cat /proc/meminfo | grep Buffers | awk '{print \$2}'"
cached_cmd="cat /proc/meminfo | grep -E '^Cached' | awk '{print \$2}'"
top_cmd="busybox top -b -n 1 | grep -E '$ps_grep|CPU:' | grep -v grep"

run_memory_cmd() {
  host=$1
  now=$2
  datat_file_prefix=$3
  if [ $adb_mode = "true" ]; then
    ps_result=($(adb -s $host shell "$ps_cmd"))
    free_result=($(adb -s $host shell "$free_cmd"))
    buffers_result=($(adb -s $host shell "$buffers_cmd"))
    cached_result=($(adb -s $host shell "$cached_cmd"))
    avaliable_result=($(adb -s $host shell "$avaliable_cmd"))
  else
    ps_result=($(eval $ps_cmd))
    free_result=($(eval $free_cmd))
    buffers_result=($(eval $buffers_cmd))
    cached_result=($(eval $cached_cmd))
    avaliable_result=($(eval $avaliable_cmd))
  fi
  ps_len=${#ps_result[@]}
  data_path=`pwd`/mem-$datat_file_prefix-$host.json
  for ((i=0;i<ps_len;++i))
  {
    ps=${ps_result[i]}
    ps_mem=`echo $ps | awk '{print $2}'`
    ps_args=`echo $ps | sed "s/$ps_mem//"`
    ps_args=${ps_args%% -*}
    echo "{\"data\":$ps_mem,\"args\":\"$ps_args\",\"time\":\"$now\"}," >> $data_path
  }
  if [ ! $free_result ]; then
    free_result=0
  fi
  echo "{\"data\":$free_result,\"args\":\"freeMem\",\"time\":\"$now\"}," >> $data_path
  echo "{\"data\":$buffers_result,\"args\":\"buffersMem\",\"time\":\"$now\"}," >> $data_path
  echo "{\"data\":$cached_result,\"args\":\"cachedMem\",\"time\":\"$now\"}," >> $data_path
  echo "{\"data\":$avaliable_result,\"args\":\"avaliableMem\",\"time\":\"$now\"}," >> $data_path
}

run_cpu_cmd() {
  host=$1
  now=$2
  data_file_prefix=$3
  if [ $adb_mode = "true" ]; then
    top_result=($(adb -s $host shell "$top_cmd"))
  else
    top_result=($(eval $top_cmd))
  fi
  top_len=${#top_result[@]}
  data_path=`pwd`/cpu-$data_file_prefix-$host.json
  for ((i=0;i<top_len;++i))
  {
    top=${top_result[i]}
    if [ $i -eq 0 ]; then
      user_cpu=`echo $top | awk '{print $2}' | sed "s/%//"`
      echo "{\"data\":$user_cpu,\"args\":\"user\",\"time\":\"$now\"}," >> $data_path
      sys_cpu=`echo $top | awk '{print $4}' | sed "s/%//"`
      echo "{\"data\":$sys_cpu,\"args\":\"sys\",\"time\":\"$now\"}," >> $data_path
      idle_cpu=`echo $top | awk '{print $8}' | sed "s/%//"`
      echo "{\"data\":$idle_cpu,\"args\":\"idle\",\"time\":\"$now\"}," >> $data_path
    else
      top=`echo $top | grep -Eo "%.*"`
      top_cpu=`echo $top | awk '{print $2}' | sed "s/%//"`
      if [ $top_cpu -gt 0 ]; then
        top_args=`echo $top | awk '{$1=$2="";print $0}'`
        top_args=${top_args%% -*}
        echo "{\"data\":$top_cpu,\"args\":\"$top_args\",\"time\":\"$now\"}," >> $data_path
      fi
    fi
  }
}

cmd_name="run_${action}_cmd"
main() {
  trap 'exit 0;' INT TERM
  trap 'echo "ignore SIGHUP"' HUP

  times=0
  flush_times=0
  dots="....."
  spaces='     '
  data_file_prefix=`date "+%Y-%m-%d-%H:%M:%S"`
  while :
  do
    IFS=$'\n'
    now=`date "+%Y-%m-%d-%H:%M:%S"`
    ((++times))
    dot_count=$(($times % 6))
    echo -ne "Collecting $action data ${dots:0:$dot_count}${spaces:$dot_count}\r"
    if [ $times -lt $store_interval ]; then
      sleep 1
      continue
    fi
    times=0
    if [ $adb_mode = "true" ]; then
      if [ $device_sn = "all" ]; then
        adb_devices=($(adb devices | sed 1d | awk '{print $1}'))
      else
        adb_devices=($(adb devices | grep $device_sn | awk '{print $1}'))
      fi
    else
      sn=`getprop ro.boot.serialno`
      adb_devices=($sn)
    fi

    if [ $flush_count -gt 0 ] && [ $flush_times -ge $flush_count ]; then
      data_file_prefix=`date "+%Y-%m-%d-%H:%M:%S"`
      flush_times=0
    fi
    ((++flush_times))
    devices_len=${#adb_devices[@]}
    for ((i=0;i<devices_len;++i))
    {
      eval "$cmd_name ${adb_devices[i]} $now $data_file_prefix"
    }
    sleep 1
  done
}

main
