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