content: /*
   * Copyright (C) 2013 The Android Open Source Project
   *
   * Licensed 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.android.internal.app.procstats;
  
  import android.util.DebugUtils;
  
  import static com.android.internal.app.procstats.ProcessStats.STATE_COUNT;
  import static com.android.internal.app.procstats.ProcessStats.STATE_NOTHING;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_SAMPLE_COUNT;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_CACHED_MINIMUM;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_CACHED_AVERAGE;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_CACHED_MAXIMUM;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_FREE_MINIMUM;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_FREE_AVERAGE;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_FREE_MAXIMUM;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_ZRAM_MINIMUM;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_ZRAM_AVERAGE;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_ZRAM_MAXIMUM;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_KERNEL_MINIMUM;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_KERNEL_AVERAGE;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_KERNEL_MAXIMUM;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_NATIVE_MINIMUM;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_NATIVE_AVERAGE;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_NATIVE_MAXIMUM;
  import static com.android.internal.app.procstats.ProcessStats.SYS_MEM_USAGE_COUNT;
  
  import java.io.PrintWriter;
  
  
  /**
   * Class to accumulate system mem usage data.
   */
  public class SysMemUsageTable extends SparseMappingTable.Table {
      /**
       * Construct the SysMemUsageTable with 'tableData' as backing store
       * for the longs data.
       */
      public SysMemUsageTable(SparseMappingTable tableData) {
          super(tableData);
      }
  
      /**
       * Merge the stats given into our own values.
       *
       * @param that  SysMemUsageTable to copy from.
       */
      public void mergeStats(SysMemUsageTable that) {
          final int N = that.getKeyCount();
          for (int i=0; i<N; i++) {
              final int key = that.getKeyAt(i);
  
              final int state = SparseMappingTable.getIdFromKey(key);
              final long[] addData = that.getArrayForKey(key);
              final int addOff = SparseMappingTable.getIndexFromKey(key);
  
              mergeStats(state, addData, addOff);
          }
      }
  
      /**
       * Merge the stats given into our own values.
       *
       * @param state     The state
       * @param addData   The data array to copy
       * @param addOff    The index in addOff to start copying from
       */
      public void mergeStats(int state, long[] addData, int addOff) {
          final int key = getOrAddKey((byte)state, SYS_MEM_USAGE_COUNT);
          
          final long[] dstData = getArrayForKey(key);
          final int dstOff = SparseMappingTable.getIndexFromKey(key);
  
          SysMemUsageTable.mergeSysMemUsage(dstData, dstOff, addData, addOff);
      }
  
      /**
       * Return a long[] containing the merge of all of the usage in this table.
       */
      public long[] getTotalMemUsage() {
          long[] total = new long[SYS_MEM_USAGE_COUNT];
          final int N = getKeyCount();
          for (int i=0; i<N; i++) {
              final int key = getKeyAt(i);
  
              final long[] addData = getArrayForKey(key);
              final int addOff = SparseMappingTable.getIndexFromKey(key);
  
              SysMemUsageTable.mergeSysMemUsage(total, 0, addData, addOff);
          }
          return total;
      }
  
      /**
       * Merge the stats from one raw long[] into another.
       *
       * @param dstData The destination array
       * @param dstOff  The index in the destination array to start from
       * @param addData The source array
       * @param addOff  The index in the source array to start from
       */
      public static void mergeSysMemUsage(long[] dstData, int dstOff,
              long[] addData, int addOff) {
          final long dstCount = dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT];
          final long addCount = addData[addOff+SYS_MEM_USAGE_SAMPLE_COUNT];
          if (dstCount == 0) {
              dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = addCount;
              for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i++) {
                  dstData[dstOff+i] = addData[addOff+i];
              }
          } else if (addCount > 0) {
              dstData[dstOff+SYS_MEM_USAGE_SAMPLE_COUNT] = dstCount + addCount;
              for (int i=SYS_MEM_USAGE_CACHED_MINIMUM; i<SYS_MEM_USAGE_COUNT; i+=3) {
                  if (dstData[dstOff+i] > addData[addOff+i]) {
                      dstData[dstOff+i] = addData[addOff+i];
                  }
                  dstData[dstOff+i+1] = (long)(
                          ((dstData[dstOff+i+1]*(double)dstCount)
                                  + (addData[addOff+i+1]*(double)addCount))
                                  / (dstCount+addCount) );
                  if (dstData[dstOff+i+2] < addData[addOff+i+2]) {
                      dstData[dstOff+i+2] = addData[addOff+i+2];
                  }
              }
          }
      }
  
  
      public void dump(PrintWriter pw, String prefix, int[] screenStates, int[] memStates) {
          int printedScreen = -1;
          for (int is=0; is<screenStates.length; is++) {
              int printedMem = -1;
              for (int im=0; im<memStates.length; im++) {
                  final int iscreen = screenStates[is];
                  final int imem = memStates[im];
                  final int bucket = ((iscreen + imem) * STATE_COUNT);
                  long count = getValueForId((byte)bucket, SYS_MEM_USAGE_SAMPLE_COUNT);
                  if (count > 0) {
                      pw.print(prefix);
                      if (screenStates.length > 1) {
                          DumpUtils.printScreenLabel(pw, printedScreen != iscreen
                                  ? iscreen : STATE_NOTHING);
                          printedScreen = iscreen;
                      }
                      if (memStates.length > 1) {
                          DumpUtils.printMemLabel(pw,
                                  printedMem != imem ? imem : STATE_NOTHING, '\0');
                          printedMem = imem;
                      }
                      pw.print(": ");
                      pw.print(count);
                      pw.println(" samples:");
                      dumpCategory(pw, prefix, "  Cached", bucket, SYS_MEM_USAGE_CACHED_MINIMUM);
                      dumpCategory(pw, prefix, "  Free", bucket, SYS_MEM_USAGE_FREE_MINIMUM);
                      dumpCategory(pw, prefix, "  ZRam", bucket, SYS_MEM_USAGE_ZRAM_MINIMUM);
                      dumpCategory(pw, prefix, "  Kernel", bucket, SYS_MEM_USAGE_KERNEL_MINIMUM);
                      dumpCategory(pw, prefix, "  Native", bucket, SYS_MEM_USAGE_NATIVE_MINIMUM);
                  }
              }
          }
      }
  
      private void dumpCategory(PrintWriter pw, String prefix, String label, int bucket, int index) {
          pw.print(prefix); pw.print(label);
          pw.print(": ");
          DebugUtils.printSizeValue(pw, getValueForId((byte)bucket, index) * 1024);
          pw.print(" min, ");
          DebugUtils.printSizeValue(pw, getValueForId((byte)bucket, index + 1) * 1024);
          pw.print(" avg, ");
          DebugUtils.printSizeValue(pw, getValueForId((byte)bucket, index+2) * 1024);
          pw.println(" max");
      }
      
  }
  
  
  