Home | History | Annotate | Download | only in storagemonitoring
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 package com.android.car.storagemonitoring;
     17 
     18 import android.car.storagemonitoring.IoStatsEntry;
     19 import android.car.storagemonitoring.UidIoRecord;
     20 import android.util.SparseArray;
     21 import com.android.car.SparseArrayStream;
     22 import com.android.car.procfsinspector.ProcessInfo;
     23 import com.android.car.systeminterface.SystemStateInterface;
     24 import java.util.List;
     25 import java.util.Optional;
     26 
     27 public class IoStatsTracker {
     28     private abstract class Lazy<T> {
     29         protected Optional<T> mLazy = Optional.empty();
     30 
     31         protected abstract T supply();
     32 
     33         public synchronized T get() {
     34             if (!mLazy.isPresent()) {
     35                 mLazy = Optional.of(supply());
     36             }
     37             return mLazy.get();
     38         }
     39     }
     40 
     41     private final long mSampleWindowMs;
     42     private final SystemStateInterface mSystemStateInterface;
     43     private SparseArray<IoStatsEntry> mTotal;
     44     private SparseArray<IoStatsEntry> mCurrentSample;
     45 
     46     public IoStatsTracker(List<IoStatsEntry> initialValue,
     47             long sampleWindowMs, SystemStateInterface systemStateInterface) {
     48         mTotal = new SparseArray<>(initialValue.size());
     49         initialValue.forEach(uidIoStats -> mTotal.append(uidIoStats.uid, uidIoStats));
     50         mCurrentSample = mTotal.clone();
     51         mSampleWindowMs = sampleWindowMs;
     52         mSystemStateInterface = systemStateInterface;
     53     }
     54 
     55     public synchronized void update(SparseArray<UidIoRecord> newMetrics) {
     56         final Lazy<List<ProcessInfo>> processTable = new Lazy<List<ProcessInfo>>() {
     57             @Override
     58             protected List<ProcessInfo> supply() {
     59                 return mSystemStateInterface.getRunningProcesses();
     60             }
     61         };
     62 
     63         SparseArray<IoStatsEntry> newSample = new SparseArray<>();
     64         SparseArray<IoStatsEntry> newTotal = new SparseArray<>();
     65 
     66         // prepare the new values
     67         SparseArrayStream.valueStream(newMetrics).forEach( newRecord -> {
     68             final int uid = newRecord.uid;
     69             final IoStatsEntry oldRecord = mTotal.get(uid);
     70 
     71             IoStatsEntry newStats = null;
     72 
     73             if (oldRecord == null) {
     74                 // this user id has just showed up, so just add it to the current sample
     75                 // and its runtime is the size of our sample window
     76                 newStats = new IoStatsEntry(newRecord, mSampleWindowMs);
     77             } else {
     78                 // this user id has already been detected
     79 
     80                 if (oldRecord.representsSameMetrics(newRecord)) {
     81                     // if no new I/O happened, try to figure out if any process on behalf
     82                     // of this user has happened, and use that to update the runtime metrics
     83                     if (processTable.get().stream().anyMatch(pi -> pi.uid == uid)) {
     84                         newStats = new IoStatsEntry(newRecord.delta(oldRecord),
     85                                 oldRecord.runtimeMillis + mSampleWindowMs);
     86                     }
     87                     // if no new I/O happened and no process is running for this user
     88                     // then do not prepare a new sample, as nothing has changed
     89                 } else {
     90                     // but if new I/O happened, assume something was running for the entire
     91                     // sample window and compute the delta
     92                     newStats = new IoStatsEntry(newRecord.delta(oldRecord),
     93                             oldRecord.runtimeMillis + mSampleWindowMs);
     94                 }
     95             }
     96 
     97             if (newStats != null) {
     98                 newSample.put(uid, newStats);
     99                 newTotal.append(uid, new IoStatsEntry(newRecord, newStats.runtimeMillis));
    100             } else {
    101                 // if oldRecord were null, newStats would be != null and we wouldn't be here
    102                 newTotal.append(uid, oldRecord);
    103             }
    104         });
    105 
    106         // now update the stored values
    107         mCurrentSample = newSample;
    108         mTotal = newTotal;
    109     }
    110 
    111     public synchronized SparseArray<IoStatsEntry> getTotal() {
    112         return mTotal;
    113     }
    114 
    115     public synchronized SparseArray<IoStatsEntry> getCurrentSample() {
    116         return mCurrentSample;
    117     }
    118 }
    119