content: /**
   * Licensed to the Apache Software Foundation (ASF) under one
   * or more contributor license agreements.  See the NOTICE file
   * distributed with this work for additional information
   * regarding copyright ownership.  The ASF licenses this file
   * to you under the Apache License, Version 2.0 (the
   * "License"); you may not use this file except in compliance
   * with the License.  You may obtain a copy of the License at
   *
   * http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  package com.alibaba.jstorm.utils;
  
  import backtype.storm.utils.ShellUtils;
  import org.apache.commons.io.IOUtils;
  import org.slf4j.Logger;
  import org.slf4j.LoggerFactory;
  
  import java.io.*;
  import java.util.*;
  import java.util.regex.Matcher;
  import java.util.regex.Pattern;
  
  /**
   * @author JohnFang (xiaojian.fxj@alibaba-inc.com).
   */
  public class LinuxResource {
  
      private static final Logger LOG = LoggerFactory.getLogger(LinuxResource.class);
  
      private static final String PROCFS_STAT = "/proc/stat";
      private static final Pattern CPU_TIME_FORMAT_ALL =
              Pattern.compile("^cpu[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)");
      private static final Pattern CPU_TIME_FORMAT = Pattern.compile("^cpu[ \t]+([0-9]+)[ \t]+([0-9]+)[ \t]+([0-9]+)");
  
      private static final String PROCFS_MEMINFO = "/proc/meminfo";
      private static volatile String duHome = "./";
      public static final long JIFFY_LENGTH_IN_MILLIS;
  
      static {
          long jiffiesPerSecond = getJiffies();
          JIFFY_LENGTH_IN_MILLIS = jiffiesPerSecond != -1 ? Math.round(1000D / jiffiesPerSecond) : 100;
      }
  
      private static long lastCpuTime = -1;
      private static long lastIdieTime = -1;
      private static float lastcpuUsage = -1;
  
      private static final String PROCFS_NETSTAT = "/proc/net/dev";
  
      public interface ResourceCallback {
          Object execute(List<String> lines) throws Exception;
      }
  
      public static void setDuHome(String duHome) {
          LinuxResource.duHome = duHome;
      }
  
      static class ProcResourceParse {
          private ResourceCallback callback;
          private String file;
  
          public ProcResourceParse(String file, ResourceCallback callback) {
              this.callback = callback;
              this.file = file;
          }
  
          public Object getResource() {
              try {
                  List<String> lines = IOUtils.readLines(new FileInputStream(file));
                  return callback.execute(lines);
              } catch (Exception e) {
                  LOG.warn("Error reading the stream ", e);
              }
              return null;
          }
      }
  
      public static int getProcessNum() {
          int sysCpuNum = 0;
          try {
              sysCpuNum = Runtime.getRuntime().availableProcessors();
          } catch (Exception e) {
              LOG.info("Failed to get CPU cores .");
          }
          return sysCpuNum;
      }
  
      public static long getJiffies() {
          if (!OSInfo.isLinux()) {
              return -1;
          }
          long jiffiesPerSecond = -1;
          try {
              ShellUtils.ShellCommandExecutor shellExecutorClk =
                      new ShellUtils.ShellCommandExecutor(new String[]{"getconf", "CLK_TCK"});
              shellExecutorClk.execute();
              jiffiesPerSecond = Long.parseLong(shellExecutorClk.getOutput().replace("\n", ""));
          } catch (Exception e) {
              LOG.warn("get jiffies happened error!!!", e);
          }
          return jiffiesPerSecond;
      }
  
      public static synchronized float getTotalCpuUsage() {
          if (!OSInfo.isLinux()) {
              return 0.0f;
          }
          ProcResourceParse procResourceParse = new ProcResourceParse(PROCFS_STAT, new ResourceCallback() {
              @Override
              public Object execute(List<String> lines) throws Exception {
                  Pair<Long, Long> totalCpu2idle = null;
                  for (String line : lines) {
                      long totalCpuTime = 0;
                      long idleCpuTime = 0;
                      int size = 0;
                      Matcher matcher = CPU_TIME_FORMAT_ALL.matcher(line);
                      if (matcher.find()) {
                          size = 8;
                      } else {
                          matcher = CPU_TIME_FORMAT.matcher(line);
                          if (matcher.find())
                              size = 5;
                      }
                      for (int i = 1; i < size; i++) {
                          long value = JStormUtils.parseLong(matcher.group(i));
                          totalCpuTime += value;
                          if (i == 4)
                              idleCpuTime = value;
                      }
                      if (size > 0) {
                          totalCpu2idle = new Pair<>();
                          totalCpu2idle.setFirst(totalCpuTime);
                          totalCpu2idle.setSecond(idleCpuTime);
                          break;
                      }
                  }
                  return totalCpu2idle;
              }
          });
          Object result = procResourceParse.getResource();
          if (result == null) {
              LOG.warn("getTotalCpuUsage failed");
              return 0.0f;
          }
  
          Pair<Long, Long> totalCpu2idle = (Pair<Long, Long>) result;
          if (lastCpuTime == -1 ||
                  lastCpuTime > totalCpu2idle.getFirst()) {
              lastCpuTime = totalCpu2idle.getFirst();
              lastIdieTime = totalCpu2idle.getSecond();
              return lastcpuUsage;//return -1 first time
          }
  
          if (totalCpu2idle.getFirst() > lastCpuTime + 1) {
              float deltaCpu = totalCpu2idle.getFirst() - lastCpuTime;
              float deltaIdle = totalCpu2idle.getSecond() - lastIdieTime;
              lastcpuUsage = (1 - deltaIdle / deltaCpu) * 100f;
              lastCpuTime = totalCpu2idle.getFirst();
              lastIdieTime = totalCpu2idle.getSecond();
          }
          return lastcpuUsage;
      }
  
      public static double getTotalMemUsage() {
          if (!OSInfo.isLinux()) {
              return 0.0;
          }
          try {
              Map<String, String> memInfo = new HashMap<>();
              List<String> lines = IOUtils.readLines(new FileInputStream(PROCFS_MEMINFO));
              for (String line : lines) {
                  String key = line.split("\\s+")[0];
                  String value = line.split("\\s+")[1];
                  memInfo.put(key, value);
              }
              String total = memInfo.get("MemTotal:");
              String free = memInfo.get("MemFree:");
              String buffer = memInfo.get("Buffers:");
              String cache = memInfo.get("Cached:");
              return 1 - (Double.valueOf(free) + Double.valueOf(buffer) + Double.valueOf(cache)) / Double.valueOf(total);
          } catch (Exception ignored) {
              LOG.warn("failed to get total memory usage.", ignored);
          }
          return 0.0;
      }
  
      public static Long getFreePhysicalMem() {
          if (!OSInfo.isLinux()) {
              return 0L;
          }
          try {
              List<String> lines = IOUtils.readLines(new FileInputStream(PROCFS_MEMINFO));
              String free = lines.get(1).split("\\s+")[1];
              return Long.valueOf(free);
          } catch (Exception ignored) {
              LOG.warn("failed to get total free memory.");
          }
          return 0L;
      }
  
      /**
       * calculate the disk usage at current filesystem
       *
       * @return disk usage, from 0.0 ~ 1.0
       */
      public static Double getDiskUsage() {
          if (!OSInfo.isLinux() && !OSInfo.isMac()) {
              return 0.0;
          }
          try {
              String output = SystemOperation.exec("df -h " + duHome);
              if (output != null) {
                  String[] lines = output.split("[\\r\\n]+");
                  if (lines.length >= 2) {
                      String[] parts = lines[1].split("\\s+");
                      if (parts.length >= 5) {
                          String pct = parts[4];
                          if (pct.endsWith("%")) {
                              return Integer.valueOf(pct.substring(0, pct.length() - 1)) / 100.0;
                          }
                      }
                  }
              }
          } catch (Exception e) {
              LOG.warn("failed to get disk usage.");
          }
          return 0.0;
      }
  
      public static Pair<Long, Long> getNetRevSendCount() {
          Pair<Long, Long> pair = new Pair<>(0l, 0l);
          if (!OSInfo.isLinux()) {
              return pair;
          }
          ProcResourceParse procResourceParse = new ProcResourceParse(PROCFS_NETSTAT, new ResourceCallback() {
              @Override
              public Object execute(List<String> lines) throws Exception {
                  Pair<Long, Long> pair = new Pair<>(0l, 0l);
                  int receiveIndex = -1;
                  int sendIndex = -1;
                  for (String line : lines) {
                      String[] splitString = line.split("\\s+|\\||:");
                      List<String> strings = new ArrayList<>();
                      for (String str : splitString) {
                          if (str.trim().length() > 0) {
                              strings.add(str);
                          }
                      }
                      if (strings.size() > 0 && strings.get(0).equals("face")) {
                          for (int i = 0; i < strings.size(); i++) {
                              if (strings.get(i).equals("bytes")) {
                                  if (receiveIndex == -1) {
                                      receiveIndex = i;
                                  } else {
                                      sendIndex = i;
                                  }
                              }
                          }
                          break;
                      }
                  }
  
                  if (receiveIndex != -1 && sendIndex != -1) {
                      for (String line : lines) {
                          String[] splitString = line.split("\\s+|\\||:");
                          List<String> strings = new ArrayList<>();
                          for (String str : splitString) {
                              if (str.trim().length() > 0) {
                                  strings.add(str);
                              }
                          }
                          if (strings.size() > 0 && strings.get(0).startsWith("eth")) {
                              long rec = JStormUtils.parseLong(strings.get(receiveIndex));
                              long send = JStormUtils.parseLong(strings.get(sendIndex));
                              pair.setFirst(pair.getFirst() + rec);
                              pair.setSecond(pair.getSecond() + send);
                          }
                      }
                  } else {
                      LOG.warn("receiveIndex {}, sendIndex {}", receiveIndex, sendIndex);
                  }
                  return pair;
              }
          });
          Object result = procResourceParse.getResource();
          if (result != null)
              pair = (Pair<Long, Long>) result;
          return pair;
      }
  
  }
  