1 /* 2 * Copyright (C) 2018 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.internal.util; 17 18 import android.os.SystemClock; 19 import android.util.SparseBooleanArray; 20 import android.util.SparseLongArray; 21 22 import java.io.PrintWriter; 23 24 public class ProviderAccessStats { 25 private final Object mLock = new Object(); 26 27 private final long mStartUptime = SystemClock.uptimeMillis(); 28 29 private final SparseBooleanArray mAllCallingUids = new SparseBooleanArray(); 30 private final SparseLongArray mQueryStats = new SparseLongArray(16); 31 private final SparseLongArray mBatchStats = new SparseLongArray(0); 32 private final SparseLongArray mInsertStats = new SparseLongArray(0); 33 private final SparseLongArray mUpdateStats = new SparseLongArray(0); 34 private final SparseLongArray mDeleteStats = new SparseLongArray(0); 35 private final SparseLongArray mInsertInBatchStats = new SparseLongArray(0); 36 private final SparseLongArray mUpdateInBatchStats = new SparseLongArray(0); 37 private final SparseLongArray mDeleteInBatchStats = new SparseLongArray(0); 38 39 private final SparseLongArray mOperationDurationMillis = new SparseLongArray(16); 40 41 private static class PerThreadData { 42 public int nestCount; 43 public long startUptimeMillis; 44 } 45 46 private final ThreadLocal<PerThreadData> mThreadLocal = 47 ThreadLocal.withInitial(() -> new PerThreadData()); 48 incrementStats(int callingUid, SparseLongArray stats)49 private void incrementStats(int callingUid, SparseLongArray stats) { 50 synchronized (mLock) { 51 stats.put(callingUid, stats.get(callingUid) + 1); 52 mAllCallingUids.put(callingUid, true); 53 } 54 55 final PerThreadData data = mThreadLocal.get(); 56 data.nestCount++; 57 if (data.nestCount == 1) { 58 data.startUptimeMillis = SystemClock.uptimeMillis(); 59 } 60 } 61 incrementStats(int callingUid, boolean inBatch, SparseLongArray statsNonBatch, SparseLongArray statsInBatch)62 private void incrementStats(int callingUid, boolean inBatch, 63 SparseLongArray statsNonBatch, SparseLongArray statsInBatch) { 64 incrementStats(callingUid, inBatch ? statsInBatch : statsNonBatch); 65 } 66 incrementInsertStats(int callingUid, boolean inBatch)67 public final void incrementInsertStats(int callingUid, boolean inBatch) { 68 incrementStats(callingUid, inBatch, mInsertStats, mInsertInBatchStats); 69 } 70 incrementUpdateStats(int callingUid, boolean inBatch)71 public final void incrementUpdateStats(int callingUid, boolean inBatch) { 72 incrementStats(callingUid, inBatch, mUpdateStats, mUpdateInBatchStats); 73 } 74 incrementDeleteStats(int callingUid, boolean inBatch)75 public final void incrementDeleteStats(int callingUid, boolean inBatch) { 76 incrementStats(callingUid, inBatch, mDeleteStats, mDeleteInBatchStats); 77 } 78 incrementQueryStats(int callingUid)79 public final void incrementQueryStats(int callingUid) { 80 incrementStats(callingUid, mQueryStats); 81 } 82 incrementBatchStats(int callingUid)83 public final void incrementBatchStats(int callingUid) { 84 incrementStats(callingUid, mBatchStats); 85 } 86 finishOperation(int callingUid)87 public void finishOperation(int callingUid) { 88 final PerThreadData data = mThreadLocal.get(); 89 data.nestCount--; 90 if (data.nestCount == 0) { 91 // Because we only have millisecond granularity, let's always attribute at least 1ms 92 // for each operation. 93 final long duration = Math.max(1, SystemClock.uptimeMillis() - data.startUptimeMillis); 94 95 synchronized (mLock) { 96 mOperationDurationMillis.put(callingUid, 97 mOperationDurationMillis.get(callingUid) + duration); 98 } 99 } 100 } 101 dump(PrintWriter pw, String prefix)102 public void dump(PrintWriter pw, String prefix) { 103 synchronized (mLock) { 104 pw.print(" Process uptime: "); 105 pw.print((SystemClock.uptimeMillis() - mStartUptime) / (60 * 1000)); 106 pw.println(" minutes"); 107 pw.println(); 108 109 pw.print(prefix); 110 pw.println("Client activities:"); 111 pw.print(prefix); 112 pw.println(" UID Query Insert Update Delete Batch Insert Update Delete" 113 + " Sec"); 114 for (int i = 0; i < mAllCallingUids.size(); i++) { 115 final int uid = mAllCallingUids.keyAt(i); 116 pw.print(prefix); 117 pw.println(String.format( 118 " %-9d %6d %6d %6d %6d %6d %6d %6d %6d %12.3f", 119 uid, 120 mQueryStats.get(uid), 121 mInsertStats.get(uid), 122 mUpdateStats.get(uid), 123 mDeleteStats.get(uid), 124 mBatchStats.get(uid), 125 mInsertInBatchStats.get(uid), 126 mUpdateInBatchStats.get(uid), 127 mDeleteInBatchStats.get(uid), 128 (mOperationDurationMillis.get(uid) / 1000.0) 129 )); 130 } 131 pw.println(); 132 } 133 } 134 } 135